C语言中嵌入汇编,究竟有何意义?
我实在想不透,这种行为的意义,难道仅仅是为了快吗?
另外,汇编语言依赖于特定的机器,在C语言中嵌入的汇编语言,会不会出现移植性的问题?
[解决办法]
比如可以直接可以操作寄存器的值
会出现移植性的问题
[解决办法]
这样加快操作速度。。看你移植的对象了!!
[解决办法]
在C语言中嵌入的汇编语言,会不会出现移植性的问题?肯定会呀。
使用嵌入汇编,主要两个目的:一是为了控制方便(比如生成位置无关的代码、使用特权指令、使用特定架构的指令实现算法等等),其次才是提高性能。
[解决办法]
这个你有个理解误区,你的c语言其实也可以翻译成了汇编语言,
C和汇编都是面向过程的语言,
比如你上面的代码是用C写的,它已经执行完了,然后你后面的代码是汇编语言,它继续执行,
部存在篡改问题。
[解决办法]
还是有好多特殊用途,这个就一言难尽了。
[解决办法]
有些东西只有用汇编才可以,比如溢出标志,另外在对效率很在乎的地方(如内核等),也会用到很多用来提高效率
[解决办法]
通常的C表达式是无法直接取到寄存器的数值的(使用寄存器变量和伪变量除外)。对于不使用寄存器变量和伪变量的表达式而言,寄存器中保存的仅是计算的中间结果而已。由于内嵌汇编不可能出现在表达式计算过程中,所以在内嵌汇编中修改寄存的值不会影响到表达式的计算。
当然也存在特殊的寄存器,例如80X86中的EIP,它保存的是下一条指令的相对地址(也就是PC),因此除了特殊应用外不要改变EIP的值。
[解决办法]
如果函数工具一类的钢筋混凝土不够你来建造一座坚实的大楼 你可以用汇编纳米原子来填充
[解决办法]
CSDN之前(http://topic.csdn.net/u/20100413/20/87aa7378-cd9f-44ad-90ae-4201a3680b71.html)已经有讨论过嵌入汇编的问题,可以好好参考一下。
基本上吵嚷者汇编效率高出C许多的都是没用过汇编的(典型语录——要效率你用汇编啊)……原因很简单,一般水平的家伙写汇编就别说逻辑上跑赢C/C++的代码优化了,就在指令集这块——教材上的386级别的指令也完全比不上最新的intel指令集,现在有些牛逼的SSE指令抵得上一个小型的C函数了。
VS2008可以直接认得SSE4指令集.
类似的,VS2005支持到SSE3.
我觉得,要内嵌汇编,不用SSE3~SSE4就没有价值,而能跑赢C/C++编译器优化的专业汇编选手也太少了,因此我见到喜欢内嵌汇编的,总感觉在装B……
又见到一篇欠扁的文章,该“大牛”竟然在32位C语言程序里内嵌8086汇编以“加强效率”——mov ax,bx
[解决办法]
两点:
1.方便。有些东西用汇编写会更加方便,代码反而更少
2.功能。有些功能靠C不能实现或者实现比较困难。例如你要写感染病毒等
[解决办法]
有些地方需要控制寄存器。
[解决办法]
看看汇编的作用吧:
//=====================================================================================
/* CPUID指令是intel IA32架构下获得CPU信息的汇编指令,
可以得到CPU类型,型号,制造商信息,商标信息,序列号,
缓存等一系列CPU相关的东西。
*/
#include <windows.h>
#include <iostream>
#include <string>
using namespace std;
//用来存储eax,ebx,ecx,edx四个寄存器的信息
DWORD deax;
DWORD debx;
DWORD decx;
DWORD dedx;
void ExeCPUID(DWORD veax) //初始化CPU
{
__asm
{
mov eax,veax
cpuid
mov deax,eax
mov debx,ebx
mov decx,ecx
mov dedx,edx
}
}
/*在Intel Pentium以上级别的CPU中,有一个称为“时间戳(Time Stamp)”的部件,
它以64位无符号整型数的格式,记录了自CPU上电以来所经过的时钟周期数。
由于目前的CPU主频都非常高,因此这个部件可以达到纳秒级的计时精度。
这个精确性是上述两种方法所无法比拟的。
在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)
来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中
*/
long GetCPUFreq() //获取CPU频率,单位: MHZ
{
int start,over;
_asm
{
RDTSC
mov start,eax
}
Sleep(50);
_asm
{
RDTSC
mov over,eax
}
return (over-start)/50000;
}
/* 把eax = 0作为输入参数,可以得到CPU的制造商信息。
cpuid指令执行以后,会返回一个12字符的制造商信息,
前四个字符的ASC码按低位到高位放在ebx,中间四个放在edx,最后四个字符放在ecx。
*/
string GetManID() //获取制造商信息
{
char ID[25];
memset(ID,0,sizeof(ID));
ExeCPUID(0); //初始化
memcpy(ID+0,&debx,4); //制造商信息复制到数组
memcpy(ID+4,&dedx,4);
memcpy(ID+8,&decx,4);
return string(ID);
}
/* 在我的电脑上点击右键,选择属性,可以在窗口的下面看到一条CPU的信息,
这就是CPU的商标字符串。CPU的商标字符串也是通过cpuid得到的。
由于商标的字符串很长(48个字符),所以不能在一次cpuid指令执行时全部得到,
所以intel把它分成了3个操作,eax的输入参数分别是0x80000002,0x80000003,0x80000004,
每次返回的16个字符,按照从低位到高位的顺序依次放在eax, ebx, ecx, edx。
因此,可以用循环的方式,每次执行完以后保存结果,然后执行下一次cpuid。
*/
string GetCPUType()
{
const DWORD id = 0x80000002; //从0x80000002开始,到0x80000004结束
char CPUType[49];//用来存储CPU型号信息
memset(CPUType,0,sizeof(CPUType));//初始化数组
for(DWORD t = 0 ; t < 3 ; t++ )
{
ExeCPUID(id+t);
//每次循环结束,保存信息到数组
memcpy(CPUType+16*t+ 0,&deax,4);
memcpy(CPUType+16*t+ 4,&debx,4);
memcpy(CPUType+16*t+ 8,&decx,4);
memcpy(CPUType+16*t+12,&dedx,4);
}
return string(CPUType);
}
void main()
{
cout<<"本机CPU信息如下:"<<endl;
cout<<"CPU 主 频: "<<GetCPUFreq()<<" MHZ"<<endl;
cout<<"CPU 制造商: "<<GetManID()<<endl;
cout<<"CPU 型 号: "<<GetCPUType()<<endl;
cin.get();
}
[解决办法]
比如
//有符号整形a和b,如何判断a+b是否溢出
#include <stdio.h>
int ifo_add(int a,int b) {
__asm {
mov eax,a
add eax,b
jo overflowed
xor eax,eax
jmp no_overflowed
overflowed:
mov eax,1
no_overflowed:
}
}
int main() {
int a,b;
a= 1;b= 2;printf("%11d+(%2d) %d\n",a,b,ifo_add(a,b));
a= -1;b=-2;printf("%11d+(%2d) %d\n",a,b,ifo_add(a,b));
a= 2147483647;b= 1;printf("%11d+(%2d) %d\n",a,b,ifo_add(a,b));
a=-2147483647;b=-1;printf("%11d+(%2d) %d\n",a,b,ifo_add(a,b));
a=-2147483647;b=-2;printf("%11d+(%2d) %d\n",a,b,ifo_add(a,b));
}
// 1+( 2) 0
// -1+(-2) 0
// 2147483647+( 1) 1
//-2147483647+(-1) 0
//-2147483647+(-2) 1
再比如
#include <stdio.h>
int ROR(int v,unsigned char b) {
__asm {
push ecx
mov eax,v
mov cl,b
ror eax,cl
pop ecx
}
}
void main() {
printf("0x%08x\n",ROR(0x12345678,4));
}
//0x81234567
[解决办法]
_asm 和 __asm 有啥区别
[解决办法]
1. 不同“架构”的cpu,汇编不同
2. 为了“快”,而且有些功能 “高级”语言不提供
3. compiler 手册会提供一定的嵌入格式,规避了“污染”其他程序中的重要数据的风险--比如先 push stack...
[解决办法]
阁下,是高手!
[解决办法]
你需要遵循当前平台下的底层习俗,比如用哪些寄存器。你的C/C++编译器编译出的代码同样会遵循那些习俗。
[解决办法]
一合理的程序,是程序的主用C完成,而和效率敏感的部分用ASM完成。因此,人建某些重要的子程序用ASM封成C代的形式,如此即可兼可性和效率
[解决办法]
有些需要处理寄存器需要使用汇编,
[解决办法]
我们平常说的各种寄存器不是一个,而是一组。
[解决办法]
但是 操作系统如果管理的不好,也会出现你说的问题。
[解决办法]
谁说手写的汇编代码跑不赢编译器。看看我们的测试结果。
千分求汇编语言优化 UInt96x96To192(...)
x86上128位二进制乘法最快速算法征解
二进制32位整数快速平方根
[解决办法]
不会,简单来说,每个进程都有自己的上下文环境Context,不同的进程切换时这些寄存器值会保存下来,x86上有个TSS,用来保存任务状态,而不同OS的进程切换实现也不尽相同,Intel建议每个进程单独一个TSS,但Linux就是每CPU只用一个TSS。至于为什么要汇编,首先效率问题,很多地方编译器做的比都人工强,但也有些地方它肯定不如有经验者做得好(比如利用特殊指令集方面,除非是特别优化过的编译器,比如intel编译器对用intel的指令集优化的应该也不错,但一般编译器肯定是有人工可以改进之处),以前看过文章比较究竟哪些方面编译器比较强。其次在灵活和简洁方面,汇编有可以使用特权指令(内核态),特殊指令,直接存取寄存器的好处,对于一些挂钩,裸函数,系统漏洞利用等特殊场合自有其妙用,很多时候会很好用,而对于图形影像声音等需要大量计算的数据处理上,合理利用多媒体扩展指令集,可以使程序性能有惊人的提升,多数优秀的音视频编解码器都有用汇编写的部分。总之对于一般场合的用户态程序确实没什么必要,写出来的汇编往往不如编译器,以前有个同事试过改写一段性能要求较高的循环函数,结果代码看上去比编译器的更少,指令利用貌似更合理,但结果比编译器的更慢。而且MS的64位编译器也不支持内联汇编了,实际上大多数指令MS也提供API了,出于兼容32位64位编译考虑,原本需要内联汇编的地方尽量用MS的API比较好。
[解决办法]
这个不用管了,OS为了处理的很好,你想到的这些,微软在设计OS的时候,早想到了
[解决办法]
汇编可以让你精确控制,如排除操作系统的保护问题,你还可以指定变量的存放地址,这就是精确控制的特点之一。
虽然汇编对硬件有移植性问题,但是只要你用得好,其核心能力强,稳定性好,可控性好,而且同样条件下,代码效率高。
一般么,汇编用来写一些关键的核心程序,以确保精确控制和稳定性。
[解决办法]
汇编一个用途是针对不同的CPU做优化,可想而知是不能移植的,不过一般用宏定义来区分,比如:
#if HAVE_AMD3DNOW
__asm__ volatile("femms");
#elif HAVE_MMX
__asm__ volatile("emms");
#endif
可以看ffmpeg代码,里面用到了大量的汇编。
[解决办法]
是的,你管理好你的进程的内存就行了。
[解决办法]
更接近底层了,更好控制
[解决办法]
我觉得是有可能篡改其他的寄存器的数据的,从而引起小到错误,达到崩溃的可能,所以,如果没有把握,可以在自己汇编要使用寄存器的时候,先push入栈,然后pop恢复,可能我多虑了吧。VS有个功能把C++编译成ASM输出,可以先看看输出的ASM文件和自己嵌入的汇编有没有寄存器使用上的冲突,确定一下总是好的.
[解决办法]
比如程序需要的功能C语言没有该功能,自己想想吧
[解决办法]
小声回复下,arm里的c内嵌汇编是会改变寄存器r0~r3的值,内嵌完可加上改变了哪个寄存器的值,然后c产生相应的汇编时会回避这个寄存器,用没被用过的。不知道这么说对楼主有帮助么?
[解决办法]
假设一个CPU有四个核0 1 2 3.
那么同时运行的task最多只有4个。而每个cpu都有自己的寄存器组。所以说你只用考虑一个cpu上的寄存器。
task被切换出去的时候会保存其切换出去之前时刻的寄存器信息到task的结构体(类似)中,等它再次运行的时候再恢复这些寄存器。
因此,像你说的修改寄存器是否影响其他进程的情况不存在。能影响的只有当前task所在的上下文的寄存器。
如果你在嵌入式汇编中要修改某个寄存器的值,那么先保存然后用完恢复即可。
当然,不要随意修改pc的值。