用C学习内存

网友投稿 255 2022-11-29

用C学习内存

内存分段

32位操作系统,地址总线是32位,寻址空间就是32位,内存编号只能编到32个二进制位,故其只能使用4G内存。

空间:

232byte=210×210×210×22byte=1024×1024×1024×4byte=1024×1024×4K=1024×4M=4G

1 byte = 8 bit, 32bit对应4字节。

64位操作系统对应的内存就是

264byte

内存分段

用途

系统内存

操作系统的内核使用


记录函数执行,存储函数中的变量

可分配内存

给应用程序动态分配


malloc获取的内存

数据段

存储全局变量和常量

代码段

存储编译后的二进制数据

上表排列是有规律的,代码段在内存的低位段0x0,内核位于高位段0xffff..f 变量代表着某块内存存储着特定的数据。

栈内变量的内存地址:

#include #include typedef unsigned long long UINT64;int main(void) { int a,b; printf("&a: %llu, &b: %llu\n",(UINT64)&a,(UINT64)&b); int *p = &a; int *p1 = &a; int c; printf("&c: %llu\n",(UINT64)&c); return

使用gdb()调试:

$ gcc -g -o exe memory_learn.c $ gdb exe(gdb) startTemporary breakpoint 1, main () at memory_learn.c:1414 int main(void) {(gdb) n16 printf("&a: %llu, &b: %llu\n",(UINT64)&a,(UINT64)&b);(gdb) p &a$1 = (int *) 0x7fffffffdc9c(gdb) p &b$2 = (int *) 0x7fffffffdca0(gdb) n&a: 140737488346268, &b: 14073748834627217 int *p = &a;(gdb) n18 int *p1 = &a;(gdb) p &p$3 = (int **) 0x7fffffffdca8(gdb) n20 printf("&c: %llu\n",(UINT64)&c);(gdb) p &p1$4 = (int **) 0x7fffffffdcb0(gdb) n&c: 14073748834627621 return

$ uname -aLinux ubuntu 4.2.0-42-generic #49-Ubuntu SMP Tue Jun 28 21:26:26 UTC 2016

所以&p1和&p的差是8 同时,我们发现上面的所有的地址都和0xff..f很相近,所以main()是放在栈内存中的。

再看一个栈和全局变量的例子:

/* ============================================================================ Name : memory_learn.c Author : theArcticOcean Version : Copyright : Description : C, Ansi-style ============================================================================ */#include #include int global;int sum(int a,int b){ return a+b;}int square(int a){ return a*a;}int square_sum(int a,int b){ int aa = square(a); int bb = square(b); int ans = sum(aa,bb); return ans;}int main(void) { int a,b; a=3; b=6; printf("square_sum is %d\n",square_sum(a,b)); printf("global is %d\n",global); return

(gdb) startTemporary breakpoint 1 at 0x4005a1: file memory_learn.c, line 28.Starting program: /home/edemon/workspace/memory_learn/src/exe Temporary breakpoint 1, main () at memory_learn.c:2828 a=3;(gdb) n29 b=6;(gdb) n30 printf("square_sum is %d\n",square_sum(a,b));(gdb) ssquare_sum (a=3, b=6) at memory_learn.c:2121 int aa = square(a);(gdb) ssquare (a=3) at memory_learn.c:1818 return a*a;(gdb) bt#0 square (a=3) at memory_learn.c:18#1 0x0000000000400572 in square_sum (a=3, b=6) at memory_learn.c:21#2 0x00000000004005be in main () at memory_learn.c:30(gdb) n19 }(gdb) nsquare_sum (a=3, b=6) at memory_learn.c:2222 int bb = square(b);(gdb) nsquare_sum (a=3, b=6) at memory_learn.c:2323 int ans = sum(aa,bb);(gdb) ssum (a=9, b=36) at memory_learn.c:1515 return a+b;(gdb) bt#0 sum (a=9, b=36) at memory_learn.c:15#1 0x0000000000400591 in square_sum (a=3, b=6) at memory_learn.c:23#2 0x00000000004005be in main () at memory_learn.c:30...(gdb) p &global$1 = (int *) 0x601044 (gdb) p &a$2 = (int *) 0x7fffffffdcb8(gdb) p sum$3 = {int (int, int)} 0x400536 (gdb) p &sum$4 = (int (*)(int, int)) 0x400536 (gdb) p main$5 = {int (void)} 0x400599

bt命令让我们从函数的地址大小关系能看到函数调用关系,先被调用者地址越大, 那些函数是在代码段中。 函数中的变量保存在栈中,全局变量global保存在数据段中,所以&a明显比&global大。

数组的内存分配

多维数组的元素的内存地址是否是简单的线性分布?让我们测试看看。

#include #include int main(void) { int a[3][5]; int i,j; for(i=0;i<3;i++){ for(j=0;j<5;j++){ printf("%p ",&a[i][j]); } puts(""); } return EXIT_SUCCESS;}/*0x7fff35512b40 0x7fff35512b44 0x7fff35512b48 0x7fff35512b4c 0x7fff35512b50 0x7fff35512b54 0x7fff35512b58 0x7fff35512b5c 0x7fff35512b60 0x7fff35512b64 0x7fff35512b68 0x7fff35512b6c 0x7fff35512b70 0x7fff35512b74 0x7fff35512b78 */

恩,栈中数组地址分布是线性的。指针运算(指针偏移,地址移动)同样证明了这一点

指针偏移

/* ============================================================================ Name : array_mem.c Author : theArcticOcean Version : Copyright : Description : C, Ansi-style ============================================================================ */#include #include int main(void) { int a[3][5]; int *p[3] = {NULL}; int i,j; for(i=0;i<3;i++){ p[i] = a[i]; } for(i=0;i<3;i++){ for(j=0;j<5;j++){ printf("%9d ",a[i][j]); } puts(""); } for(i=0;i<3;i++){ for(j=0;j<5;j++){ printf("%9d ",*p[i]); p[i]++; } puts(""); } return

970839232 32559 0 0 1 0 4196237 0 4195584 0 0 0 4196160 0 4195584 970839232 32559 0 0 1 0 4196237 0 4195584 0 0 0 4196160 0 4195584

更加简单的指针偏移:

/* ============================================================================ Name : pointer.c Author : theArcticOcean Version : Copyright : Description : C, Ansi-style ============================================================================ */#include #include int main(void) { int a[21]; int *p = a; int i; for(i=0;i<21;i++){ a[i] = i; } printf("3的倍数:\n"); p+=3; for(i=3;i<21;i+=3){ printf("%d ",*p); p+=3; } return EXIT_SUCCESS;}/*3的倍数:3 6 9 12 15 18 */

使用gdb调试的结果,因为又一次运行,所以a[][]的内容会不同。

# a的内容(gdb) p a $3 = {{-134253376, 32767, 0, 0, 1}, {0, 4196237, 0, 4195584, 0}, {0, 0, 4196160, 0, 4195584}}#p的地址信息(gdb) p p$4 = {0x7fffffffdc80, 0x7fffffffdc94, 0x7fffffffdca8}#打印0x7fffffffdc80后的5个值(gdb) x/5d 0x7fffffffdc800x7fffffffdc80: -134253376 32767 0 00x7fffffffdc90: 1#打印0x7fffffffdc80后的15个值,也即是所有的数组元素。(gdb) x/15d 0x7fffffffdc800x7fffffffdc80: -134253376 32767 0 00x7fffffffdc90: 1 0 4196237 00x7fffffffdca0: 4195584 0 0 00x7fffffffdcb0: 4196160 0 4195584

看出来,地址相差16字节的区域存储了4个int (sizeof(int) = 4)。

那么堆中又是怎样的呢?

#include #include int main(void) { int a[3][5]; int *p[3] = {NULL}; //p = (int *)malloc(sizeof(a)); /* 当分配一个多维数组空间时,除了第一维外 ,其余各维都必须显式地指定为正常数. 编译器只能记住一维 */ int i,j; for(i=0;i<3;i++){ p[i] = (int *)malloc(sizeof(a[i])); } for(i=0;i<3;i++){ for(j=0;j<5;j++){ printf("%p ",&p[i][j]); } puts(""); } return

0x1c57010 0x1c57014 0x1c57018 0x1c5701c 0x1c57020 0x1c57030 0x1c57034 0x1c57038 0x1c5703c 0x1c57040 0x1c57050 0x1c57054 0x1c57058 0x1c5705c 0x1c57060

因为存在多次的malloc,所以内存几乎不会线性连续。不过,如果让p[0]指向分配好的二维数组的内存,然后进行指针偏移,可以做到连续:

int main(void) { int *p[3] = {NULL}; /* 当分配一个多维数组空间时,除了第一维外 ,其余各维都必须显式地指定为正常数. 编译器只能记住一维 */ int i,j; p[0] = (int *)malloc(sizeof(int)*15); for(i=1;i<3;i++){ p[i] = p[i-1] + 5; } for(i=0;i<3;i++){ for(j=0;j<5;j++){ printf("%p ",&p[i][j]); } puts(""); } return

0x81d010 0x81d014 0x81d018 0x81d01c 0x81d020 0x81d024 0x81d028 0x81d02c 0x81d030 0x81d034 0x81d038 0x81d03c 0x81d040 0x81d044 0x81d048

字符数组与指针

int main(void) { char *p = "hello"; return

(gdb) p p$1 = 0x400594 "hello"#查看数组的内容(gdb) x/6c 0x4005940x400594: 104 'h' 101 'e' 108 'l' 108 'l' 111 'o' 0 '\000'#查看多余的内容(gdb) x/8c 0x4005940x400594: 104 'h' 101 'e' 108 'l' 108 'l' 111 'o' 0 '\000' 0 '\000' 0 '\000'

为什么不能给字符指针scanf()?

int main(void) { char *p ; scanf("%s",p); printf("%s\n",p); p = NULL; return

使用gdb一探究竟:

Temporary breakpoint 1, main () at pointer.c:1616 scanf("%s",p);(gdb) p p$1 = 0x0(gdb) p &p$2 = (char **) 0x7fffffffdcc8

可以发现p本身是放在栈中的,不过其指向的地址是在代码段中(0x0),而代码段的内存是不能被我们写入的。

字节对齐

一个有趣的例子:

int main(void) { char s1[6] = "hello"; char s2[10] = "world"; scanf("%s",s1); printf("s1: %s, s2: %s\n",s1,s2); return

17 scanf("%s",s1);(gdb) p &s1$5 = (char (*)[6]) 0x7fffffffdca0(gdb) p &s2$6 = (char (*)[10]) 0x7fffffffdcb0#字节对齐(gdb) x/17c 0x7fffffffdca00x7fffffffdca0: 104 'h' 101 'e' 108 'l' 108 'l' 111 'o' 0 '\000' 0 '\000'0 '\000'0x7fffffffdca8: 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000'0x7fffffffdcb0: 119 'w'#连续存储,越界存储(gdb) n0123456789abcdefgh18 printf("s1: %s, s2: %s\n",s1,s2);(gdb) ns1: 0123456789abcdefgh, s2: gh

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:sql 练习系列:数据查询 (mysql导入数据)
下一篇:Java读取PDF中的表格的方法示例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~