0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

jf_78858299 来源:码农的荒岛求生 作者:陆小风 2023-02-15 14:35 次阅读

C语言中数组在内存中是怎样表示的,今天就给大家聊聊这个话题

开局一张图:

图片

这个是经典的Linux进程内存布局,通常我们使用的数据存在这样几个地方:

  • 栈区,Stack
  • 全局区,Global
  • 堆区,Heap

接下来我们分别看一下C语言中的数组在这几个区域是怎样表示的,注意,小风哥的机器是x86 64位。

数组与栈区

来看一段极其简单的代码:

void arr_on_stack() {
    int arr[6];
  
    arr[0]=100;
    arr[1]=200;
    arr[2]=300;
    arr[3]=400;
    arr[4]=500;
    arr[5]=600;

    int a = arr[0];
}

我们定义了一个局部变量arr作为int类型的数组,然后分别将100-600写到了数组中,那么数组arr在内存中是怎样表示的呢?

首先我们编译一下:

# gcc -g -fno-stack-protector a.c

注意,-fno-stack-protector选项是为了禁止堆栈保护,让汇编更容易懂些,关于堆栈保护这个话题可以参考这篇文章《黑客攻防:缓冲区溢出攻击与堆栈保护》。

好啦,一切准备就绪,可以庖丁解牛啦,使用的刀就是gdb,代码面前了无秘密,gdb面前程序的运行时(run time)了无秘密。

用gdb来调试刚刚编译出来的程序,这里看一下arr_on_stack函数的汇编指令:

(gdb) disassemble arr_on_stack
Dump of assembler code for function arr_on_stack:
   0x0000000000400526 <+0>:     push   %rbp
   0x0000000000400527 <+1>:     mov    %rsp,%rbp
   0x000000000040052a <+4>:     movl   $0x64,-0x20(%rbp)
   0x0000000000400531 <+11>:    movl   $0xc8,-0x1c(%rbp)
   0x0000000000400538 <+18>:    movl   $0x12c,-0x18(%rbp)
   0x000000000040053f <+25>:    movl   $0x190,-0x14(%rbp)
   0x0000000000400546 <+32>:    movl   $0x1f4,-0x10(%rbp)
   0x000000000040054d <+39>:    movl   $0x258,-0xc(%rbp)
=> 0x0000000000400554 <+46>:    mov    -0x20(%rbp),%eax
   0x0000000000400557 <+49>:    mov    %eax,-0x4(%rbp)
   0x000000000040055a <+52>:    nop
   0x000000000040055b <+53>:    pop    %rbp
   0x000000000040055c <+54>:    retq
End of assembler dump.

我们在之前的文章《函数在内存中是怎样表示的?》多次提到过,每个函数在运行起来后都有属于自己的栈帧,栈帧组成栈区,此时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这一段内存嘛 ,数组在栈区就是这么表示的!

数组与全局区

同样看一段代码:

int global_array[6];

void arr_on_global() {
    global_array[0]=1;
    global_array[1]=2;
    global_array[2]=3;
    global_array[3]=4;
    global_array[4]=5;
    global_array[5]=6;

    int b = global_array[0];
}

同样使用# gcc -g -fno-stack-protector a.c编译,然后用gdb加断点在int b = global_array[0]这行代码,看下全局变量global_array的内存位置:

(gdb) p &global_array
$12 = (int (*)[6]) 0x601050 

gdb告诉我们数组global_array存放在内存0x601050这个地址上。

注意0x601050这个地址和刚才看到的0x7ffffffee280这个地址相去甚远,为什么呢?

再看下开局那张图:图片

全局区几乎在最底部,栈区在最顶部,所以相差很远。

接下来让我们看看0x601050这个内存区域中到底保存了些啥?

我们使用命令x/6wd 0x601050,这个命令告诉gdb从0x601050这个位置开始以32bit为单位用10进制依次打印6次,让我们来看看打印的是什么?

(gdb) x/6wd 0x601050
0x601050 <global_array>:        1      2      3      4
0x601060 :     5      6

哈哈,怎么样,是不是正是全局变量global_array中存放的内容:图片

这就是C语言中所谓的数组了, 无非就是从 0x601050到 0x601068这一段内存嘛 ,数组在全局区就是这么表示的!

数组与堆区

来段代码:

void array_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;

    int a = arr[0];
}

使用gdb加断点在int a = arr[0];这行代码,然后打印数组arr的地址:

(gdb) p arr
$20 = (int *) 0x602010

注意0x602010这个地址,这个地址和刚才的全局数组global_array的地址0x601050比较接近,因为堆区和全局区挨得比较近,可以再回过头看一下开局那张图。

然后我们同样使用x命令查看这个区域的内存内容:

(gdb)  x/6wd 0x602010
0x602010:       100     200     300     400
0x602020:       500     600

依然不出我们所料,这个区域保存的正是数组的值。

图片

这就是C语言中所谓的数组了, 无非就是从 0x602010到 0x602028这一段内存嘛 ,数组在堆区就是这么表示的!

现在你应该明白了吧,C语言中所谓的数组是怎么表示的?很简单,其实也没啥表示, 无非就是内存中一段连续的空间 ,仅此而已。

希望这篇文章对大家理解C语言中的数组有所帮助。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内存
    +关注

    关注

    8

    文章

    3024

    浏览量

    74037
  • C语言
    +关注

    关注

    180

    文章

    7604

    浏览量

    136790
  • 数组
    +关注

    关注

    1

    文章

    417

    浏览量

    25943
收藏 人收藏

    评论

    相关推荐

    浅谈C语言中数组在内存的存在形式

    我们定义了一个局部变量arr作为int类型的数组,然后分别将100-600写到了数组。那么,数组arr在内存
    的头像 发表于 09-28 14:50 2258次阅读
    浅谈<b class='flag-5'>C</b><b class='flag-5'>语言中</b><b class='flag-5'>数组</b><b class='flag-5'>在内存</b>的存在形式

    小数在内存是如何存储的?为什么C语言中的浮点数不支持位移操作?

    小数在内存是如何存储的?为什么C语言中的浮点数不支持位移操作?
    发表于 08-16 09:24 1052次阅读
    小数<b class='flag-5'>在内存</b><b class='flag-5'>中</b>是如何存储的?为什么<b class='flag-5'>C</b><b class='flag-5'>语言中</b>的浮点数不支持位移操作?

    C语言中数组和结构体的内存表示和布局

    C语言中数组和结构体都可以代表一块内存,但为什么结构体可以直接赋值,而数组不可以?这个问题涉及到C
    发表于 08-28 10:54 1603次阅读

    C语言中数组的用法

    C语言数组是一种数据结构,它可以存储多个相同类型的数据,例如整数,字符,浮点数等。数组的每个元素都有一个索引,用来表示它在
    的头像 发表于 11-24 17:48 1298次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言中</b><b class='flag-5'>数组</b>的用法

    如何使用单片机的C语言中数组

    如何使用单片机的C语言中数组
    发表于 10-15 08:59

    单片机C语言中常用的基本数据类型分为哪几类

    不管在什么语言中,定义一个变量时必然要在内存开辟一个相应大小的空间来存储该变量。不同的数据类型在内存所占的空间大小不同,其所能表示的数据范
    发表于 02-28 06:46

    C语言中的char数组和char指针有什么区别?

    让我们通过下面的例子,来了解 C语言中字符数组和字符指针之间的区别。
    的头像 发表于 01-29 16:35 2736次阅读

    C语言中数组空间动态开辟

    C语言中数组空间动态开辟 在C语言中,必不可少的需要使用到数组,通常为了动态的开辟
    的头像 发表于 02-10 12:42 2165次阅读

    C语言中什么是指针数组

    C语言中一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组的每一个元素都存
    的头像 发表于 03-10 15:26 1769次阅读

    C语言怎么建立内存的动态分配

    C语言中,全局变量是分配在内存的静态存储区的,非静态的局部变量,包括形参是分配在内存的动态
    的头像 发表于 03-10 15:30 851次阅读

    c语言数组查找指定元素

    的操作,本文将详细介绍C语言中如何在数组中进行查找,并提供几种常用的查找算法和技巧。 在开始之前,我们先来了解一下数组的基本概念和使用方法。数组
    的头像 发表于 11-24 10:07 4217次阅读

    C语言如何创建数组

    用法和注意事项。 要创建一个数组,首先需要确定数组的数据类型和大小。C语言中的数据类型包括基本数据类型(如整数、浮点数、字符等)以及派生数据类型(如
    的头像 发表于 11-24 10:08 1915次阅读

    c语言中数组怎么定义

    C语言中数组是一种用来存储相同类型元素的数据结构。它可以存储多个元素,并通过一个共同的名称来引用这些元素。数组是一种很重要的数据结构,可以用于解决很多实际的问题。 在
    的头像 发表于 11-24 10:11 3081次阅读

    c语言中多维数组可以嵌套定义

    C语言中多维数组可以嵌套定义,这使得我们可以在一个数组存储另一个数组。通过这种方式,我们可以创
    的头像 发表于 11-24 10:18 1170次阅读

    数组和链表在内存的区别 数组和链表的优缺点

    数组和链表在内存的区别 数组和链表的优缺点  数组和链表是常见的数据结构,用于组织和存储数据。它们在内
    的头像 发表于 02-21 11:30 1034次阅读