C语言与内存模型初探
#include<stdio.h> #include<string.h> int main(){ long long int a = 2<<30; char string[] = "Hello China1!"; char string2[] = "Hello China2!"; if(0==strcmp(string,string2)) { printf(string); printf("\n"); } else { printf("Not match!\n"); } printf("a = %u\n",a); printf("a = %d\n",a); char c[] = {'I',' ','a','m',' ','l','i','u','k','u','n'}; printf(c); return 0; }
C语言字符串不加结束符,打印出的结果不好预测。这应该跟操作系统的内存模型有关,理由如下:
Windows GCC编译打印结果是:
Not match!a = 2147483648a = -2147483648I am liukunHello China2!
Linux GCC编译打印结果是:
Not match!
a = 2147483648
a = -2147483648
I am liukunliukun
我也不知道为什么 Hello China2 内存上跟 I am liukun 连在一起的。
反汇编出来的代码好乱也不好读。
ASCII 码表:
ASCII 码 | 字符 | ASCII 码 | 字符 | ASCII 码 | 字符 | ASCII 码 | 字符 | |||||||
十进位 | 十六进位 | 十进位 | 十六进位 | 十进位 | 十六进位 | 十进位 | 十六进位 | |||||||
032 | 20 | 056 | 38 | 8 | 080 | 50 | P | 104 | 68 | h | ||||
033 | 21 | ! | 057 | 39 | 9 | 081 | 51 | Q | 105 | 69 | i | |||
034 | 22 | " | 058 | 3A | : | 082 | 52 | R | 106 | 6A | j | |||
035 | 23 | # | 059 | 3B | ; | 083 | 53 | S | 107 | 6B | k | |||
036 | 24 | $ | 060 | 3C | < | 084 | 54 | T | 108 | 6C | l | |||
037 | 25 | % | 061 | 3D | = | 085 | 55 | U | 109 | 6D | m | |||
038 | 26 | & | 062 | 3E | > | 086 | 56 | V | 110 | 6E | n | |||
039 | 27 | ' | 063 | 3F | ? | 087 | 57 | W | 111 | 6F | o | |||
040 | 28 | ( | 064 | 40 | @ | 088 | 58 | X | 112 | 70 | p | |||
041 | 29 | ) | 065 | 41 | A | 089 | 59 | Y | 113 | 71 | q | |||
042 | 2A | * | 066 | 42 | B | 090 | 5A | Z | 114 | 72 | r | |||
043 | 2B | + | 067 | 43 | C | 091 | 5B | [ | 115 | 73 | s | |||
044 | 2C | , | 068 | 44 | D | 092 | 5C | \ | 116 | 74 | t | |||
045 | 2D | - | 069 | 45 | E | 093 | 5D | ] | 117 | 75 | u | |||
046 | 2E | . | 070 | 46 | F | 094 | 5E | ^ | 118 | 76 | v | |||
047 | 2F | / | 071 | 47 | G | 095 | 5F | _ | 119 | 77 | w | |||
048 | 30 | 0 | 072 | 48 | H | 096 | 60 | ` | 120 | 78 | x | |||
049 | 31 | 1 | 073 | 49 | I | 097 | 61 | a | 121 | 79 | y | |||
050 | 32 | 2 | 074 | 4A | J | 098 | 62 | b | 122 | 7A | z | |||
051 | 33 | 3 | 075 | 4B | K | 099 | 63 | c | 123 | 7B | { | |||
052 | 34 | 4 | 076 | 4C | L | 100 | 64 | d | 124 | 7C | | | |||
053 | 35 | 5 | 077 | 4D | M | 101 | 65 | e | 125 | 7D | } | |||
054 | 36 | 6 | 078 | 4E | N | 102 | 66 | f | 126 | 7E | ~ | |||
055 | 37 | 7 | 079 | 4F | O | 103 | 67 | g | 127 | 7F | DEL |
【注】小端模式
所谓的小端模式(Little-endian),是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
例子:
0000430: e684 6c4e 0100 1800 53ef 0100 0100 0000
0000440: b484 6c4e 004e ed00 0000 0000 0100 0000
在小端模式下,前32位应该这样读: 4e 6c 84 e6( 假设int占4个字节)
记忆方法: 地址的增长顺序与值的增长顺序相同
> objdump -d datatype.exe > output.txt
反汇编代码节选:
;I am liukun 的地址安排
4014e2: c6 44 24 11 49 movb $0x49,0x11(%esp)
4014e7: c6 44 24 12 20 movb $0x20,0x12(%esp)
4014ec: c6 44 24 13 61 movb $0x61,0x13(%esp)
4014f1: c6 44 24 14 6d movb $0x6d,0x14(%esp)
4014f6: c6 44 24 15 20 movb $0x20,0x15(%esp)
4014fb: c6 44 24 16 6c movb $0x6c,0x16(%esp)
401500: c6 44 24 17 69 movb $0x69,0x17(%esp)
401505: c6 44 24 18 75 movb $0x75,0x18(%esp)
40150a: c6 44 24 19 6b movb $0x6b,0x19(%esp)
40150f: c6 44 24 1a 75 movb $0x75,0x1a(%esp)
401514: c6 44 24 1b 6e movb $0x6e,0x1b(%esp)
.......
40144d: c7 44 24 1c 48 65 6c movl $0x6c6c6548,0x1c(%esp)
End
总之,使用C编程时还是要多注意,操纵内存虽然爽,但是一不小心就会埋下Bug。
话说两个没有使用到的局部变量字串应该可以被编译器优化的,如果是这样的话栈里面就不会为其分配内存啦,不知道Linux下面的GCC是不是这样做的。