当前位置: 首页 > 科技观察

C语言数组在内存中是如何表示的?

时间:2023-03-20 01:17:34 科技观察

微信群里有同学问C语言中数组在内存中是如何表示的。今天小编就为大家聊聊这个话题。开场图:这是一个经典的Linux进程内存布局。通常,我们使用的数据存在于几个地方:栈区、Stack全局区、全局堆区、Heap。下面我们分别来看一下C语言中的数组。几个区域是如何表示的?注意小风哥的机器是x8664位的。让我们看一下数组和堆栈区域中的一段极其简单的代码:voidarr_on_stack(){intarr[6];arr[0]=100;arr[1]=200;arr[2]=300;arr[3]=400;arr[4]=500;arr[5]=600;inta=arr[0];}我们定义了一个局部变量arr为一个int类型的数组,然后将100-600写入数组,那么这个数组arr在内存中是如何表示的呢?首先,让我们编译:#gcc-g-fno-stack-protectora.c注意,-fno-stack-protector选项是禁用堆栈保护并使汇编更容易理解。好了,万事俱备,可以解决问题了,用的刀是gdb,代码面前没有秘密,gdb面前的程序运行时也没有秘密。使用gdb调试新编译的程序,看看arr_on_stack函数的汇编指令:(gdb)disassemblearr_on_stackDumpofassemblercodeforfunctionarr_on_stack:0x0000000000400526<+0>:push%rbp0x00000000000400527<+1>:mov%r%RBP0x000000000040052A<+4>:MOVL$0x64,-0x20(%RBP)0x000000000000400400531>:MOVL$0XC8,-0X1C(-0x1c(%RBP)0x000000000000000000000000004005538<+1000000000000000000000000>0x122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222<+0x12:movl$0x190,-0x14(%rbp)0x0000000000400546<+32>:movl$0x1f4,-0x10(%rbp)0x000000000040054d<+39>:movl$0xc258(%rbp)0x000000004005:54MO<+4005:54%RBP),%Eax0x0000000000400557<+49>:MOV%Eax,-0x4(%RBP)0x0000000040055a<+52>:不是0x0000000055B<+53>:POP%RBP0X000000005555C+54>:retq.Endofassemblerdump我们在之前的文章《函数在内存中是怎样表示的?》中多次提到,每个函数运行后都有自己的栈帧,栈帧形成栈区。这时候函数arr_on_stack的栈区在哪里呢?答案在寄存器rbp中。我们来看看rbp寄存器指向哪里?(gdb)p$rbp$3=(void*)0x7ffffffee2a0啊哈,原来的堆栈帧在0x7ffffffee2a0,那么我们的数组arr在哪里?不用担心,这条指令会告诉我们答案:0x000000000040052a<+4>:movl$0x64,-0x20(%rbp)这行指令的意思是把100(0x64)放在rbp寄存器中减去0x20,显然这是数组的开头,我们计算一下rbp寄存器减去0x20:0x7ffffffee2a0(%rbp)-0x20=0x7ffffffee280因此,我们预测arr应该在0x7ffffffee280处。接下来我们用gdb验证一下:(gdb)p&arr$2=(int(*)[6])0x7ffffffee280呵呵,怎么样,是不是和我们猜测的一样,数组arr确实放在了0x7ffffffee280的位置,是这样存储的:这就是C语言中所谓的数组。无非就是0x7ffffffee280到0x7ffffffee298之间的内存。这就是数组在堆栈区域中的表示方式!数组和全局区也看一段代码:intglobal_array[6];voidarr_on_global(){global_array[0]=1;全局数组[1]=2;全局数组[2]=3;全局数组[3]=4;全局数组[4]=5;全局数组[5]=6;intb=global_array[0];}也用#gcc-g-fno-stack-protectora.c编译,然后用gdb在intb=global_array[0]这行代码加断点,看内存全局变量global_array的位置:(gdb)p&global_array$12=(int(*)[6])0x601050gdb告诉我们数组global_array存储在内存地址0x601050处。注意0x601050的地址和刚才看到的0x7ffffffee280的地址相差很远,为什么?再看开图:全局区几乎在最下面,栈区在最上面,相差甚远。接下来我们看看内存区0x601050里面存放的是什么?我们使用命令x/6wd0x601050。该命令告诉gdb从0x601050开始以32位为单位以十进制打印6次。让我们看看打印的是什么?(gdb)x/6wd0x6010500x601050:12340x601060:56哈哈,怎么样,是不是就是全局变量global_array中存储的内容:这就是C中所谓的数组语言,无非就是0x601050到0x601068之间的一段内存。这就是数组在全局区域中的表示方式!数组和堆代码:voidarray_on_heap(){int*arr=(int*)malloc(sizeof(int)*6);arr[0]=100;arr[1]=200;arr[2]=300;arr[3]=400;arr[4]=500;arr[5]=600;inta=arr[0];}使用gdb在inta=arr[0];处添加一个断点这行代码,然后打印数组arr的地址:(gdb)parr$20=(int*)0x602010注意地址0x602010,这个地址更接近刚才全局数组global_array的地址0x601050,因为heap区和global区比较接近,可以再回头看看开篇图。然后我们也用x命令查看这个区域的内存内容:(gdb)x/6wd0x6020100x602010:1002003004000x602020:500600还是和我们预想的一样,这个区域存放的是数组的值。这就是C语言中所谓的数组。无非就是0x602010到0x602028这块内存。数组在堆区是这样表示的!现在你应该明白了,C语言中所谓的数组是怎么表示的呢?很简单,其实它没有任何意义,无非就是内存中的一个连续的空间,仅此而已。希望这篇文章能帮助你理解C语言中的数组。