说明:东西都比较简单,个人水平也比较一般,表述难免不清或错误。自己学习的一点总结,还请各位多指教不足和要改进的地方,我会及时更新,这里先谢过。
上一篇 关于基本数据类型和局部变量链接:
http://bbs.pediy.com/showthread.php?p=963344#post963344
C语言之常量
C语言的常量主要只在编译前值就确定的数值,类型主要包括:整型常量,浮点型常量,字符型常量和枚举型常量。
环境为win32,Vc
示例代码:
代码:
#define MAX_NUM 256 const int g_cnTest = 123; int main(int argc, char* argv[]) { //bool bool isTest = true; //char char cChar = 'T'; //int int nNum = 0x123; nNum = MAX_NUM; int nMax = g_cnTest; //枚举 typedef enum EnTest{en_0, en_1, en_2}; EnTest et = en_2; //float(double) float fValue = 1.2f; //double double dfValue1 = 1.2; double dfValue2 = 1.2; //字符串 printf("Hello World!\r\n"); char* lpTest = (char*)("Hello World!\r\n"); return 0; }
类型为bool,char,int,float,double,enum
代码:
15: //bool 16: bool isTest = true; 0040E9D8 C6 45 FC 01 mov byte ptr [ebp-4],1 17: //char 18: char cChar = 'T'; 0040E9DC C6 45 F8 54 mov byte ptr [ebp-8],54h 19: //int 20: int nNum = 0x123; 0040E9E0 C7 45 F4 23 01 00 00 mov dword ptr [ebp-0Ch],123h 21: nNum = MAX_NUM; 0040E9E7 C7 45 F4 00 01 00 00 mov dword ptr [ebp-0Ch],100h 22: int nMax = cnTest; 0040E9EE C7 45 F0 7B 00 00 00 mov dword ptr [ebp-10h],7Bh 23: //枚举 24: typedef enum EnTest{en_0, en_1, en_2}; 25: EnTest et = en_2; 0040E9F5 C7 45 EC 02 00 00 00 mov dword ptr [ebp-14h],2 28: //float 29: float fValue = 1.2f; 0040EA03 C7 45 E4 9A 99 99 3F mov dword ptr [ebp-1Ch],3F99999Ah 30: //double 31: double dfValue1 = 1.2; 0040EA0A C7 45 DC 33 33 33 33 mov dword ptr [ebp-24h],33333333h 0040EA11 C7 45 E0 33 33 F3 3F mov dword ptr [ebp-20h],3FF33333h 32: double dfValue2 = 1.2; 0040EA18 C7 45 D4 33 33 33 33 mov dword ptr [ebp-2Ch],33333333h 0040EA1F C7 45 D8 33 33 F3 3F mov dword ptr [ebp-28h],3FF33333h
1)bool,char,int(包括#define )的字面常量值编成了操作数(立即数),直接使用,不需要寻址操作
如nNum = 0x123
对应的机器码为C7 45 E0 23 01 00 00 => 对应数值为0x123小尾方式存储。
2)枚举型和const类型整型变量也当常量使用,直接编译到代码中。因为其值定义并初始化时,其值就是固定的,不会被修改。来试着修改下
a) 直接修改
nMax = ++g_cnTest;
编译时提示需要左值,说明没有存储空间,编译器将其视作常量对待(编译器处理)。
b) 通过取地址方式来修改,编译通过,执行时。。。
代码:
int* lpInt = (int*)&g_cnTest; *lpInt = 0x111;

因为const定义的值在常量区,而常量区的内存属性通常为可读不可写,有写操作时会触发系统的内存保护机制,就给你弹框。
3) 机器码中数值长度跟具体的数据类型相关 (枚举这里貌似也是)
4)浮点数是IEEE编码方式存储
5) 此外还有一种情况就是在编译时,变量的值是可预测的,不变的(有的叫契约常量)。
代码:
int nNum = 0x123; int nTest = nNum; printf("%d", nTest);
代码:
int nNum = 0x123; 001F432E mov dword ptr [nNum],123h int nTest = nNum; 001F4335 mov eax,dword ptr [nNum] 001F4338 mov dword ptr [nTest],eax printf("%d", nTest); 001F433B mov esi,esp 001F433D mov eax,dword ptr [nTest] 001F4340 push eax 001F4341 push offset string "%d" (1F7818h) 001F4346 call dword ptr [__imp__printf (1FB2F0h)]
代码:
text:00401003 8B 3D A0 20 40+ mov edi, ds:__imp__printf .text:00401009 68 23 01 00 00 push 123h .text:0040100E 68 14 21 40 00 push offset Format ; "%d" .text:00401013 FF D7 call edi ; __imp__printf
字符串[code]
代码:
33: //字符串 34: printf("Hello World!\r\n"); 0040EA26 68 1C 40 42 00 push offset string "My Hello World!\r\n" (0042401c) 0040EA2B E8 60 26 FF FF call printf (00401090) 0040EA30 83 C4 04 add esp,4 35: char* lpTest = (char*)("Hello World!\r\n"); 0040EA33 C7 45 D0 1C 40 42 00 mov dword ptr [ebp-30h],offset string "My Hello World!\r\n" (0042401c)
1) 字符串常量保存的在内存常量区中(属性一般为可读不可写),没有和代码编译在一起直接做值使用,在指令中通过获取指定地址来使用
这里通过0x42401c来获取字符串内容
0042401C 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 0D 0A 00 00 7B Hello World!
2)值相等的字符串在内存中只会有一份,需要时获取即可。
这里printf使用的字符串和初始化指针的地址都为【0042401c】
3)对常量的修改同样是受到系统内存属性保护的约束, 一个C0xx05的错误没的跑。
代码:
lpTest[0] = 'I'; 0040EA43 8B 4D D0 mov ecx,dword ptr [ebp-30h] 0040EA46 C6 01 49 mov byte ptr [ecx],49h

在调试状态,直接修改内存
代码:
0042401C 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 0D 0A 00 00 7B 00 Hello World! <--原来的 0042401C 48 65 6C 6C 6F 20 57 4F 52 4C 44 21 0D 0A 00 00 7B 00 Hello WORLD! <--直接修改内存
主要是因为调试器的权限,比如下个软断点(修改代码+CC即int3),代码都能改,调试器还有啥做不到的呢。
总结:
1)VC处理数值型常量时(bool, char, int, enum, const xx,float,double),数值会编译到代码中,作为立即数来使用;
2)在代码中的数据的长度和类型相关;
3)常量区得数据会受到系统内存属性的保护;
4)const类型在编译时修改,会受到编译的检查;
5)字符串常量放置在常量区,相同的字符串只存在一份
水平很一般,错误难免,望多指教!
五边形 2011年5月