微信群里有同学问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])0x601050
