C 语言数组在内存中是怎样表示的?

IT科技2025-11-05 12:14:47942

微信群里有同学问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 <global_array>

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

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

再看下开局那张图:

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

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

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

(gdb) x/6wd 0x601050

0x601050 : 1 2 3 4

0x601060 <global_array+16>: 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语言中的数组有所帮助。

本文地址:http://www.bzve.cn/news/11f63899350.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

全站热门

南坪万达广场魅族维修中心的服务质量如何?(南坪万达广场魅族维修中心值得信赖吗?)

重温数据结构经典:HashCode及HashMap原理

MySQL慢查询日志相关的配置和使用

TensorFlow在推荐系统中的分布式训练优化实践

假如你是一个测试入侵侦测系统或一些网络访问控制策略的网络管理员,那么你经常需要抓取数据包并在离线状态下分析这些文件。当需要保存捕获的数据包时,我们一般会存储为 libpcap 的数据包格式 pcap,这是一种被许多开源的嗅探工具以及捕包程序广泛使用的格式。假如 pcap 文件被用于入侵测试或离线分析的话,那么在将他们注入网络之前通常要先对 pcap 文件进行一些操作。在该文中,我将介绍一些操作 pcap 文件的工具,以及如何使用它们 。Editcap 与 MergecapWireshark,是最受欢迎的 GUI 嗅探工具,实际上它带了一套非常有用的命令行工具集。其中包括 editcap 与 mergecap。editcap 是一个万能的 pcap 编辑器,它可以过滤并且能以多种方式来分割 pcap 文件。mergecap 可以将多个 pcap 文件合并为一个。 该文就是基于这些 Wireshark 命令行工具的。假如你已经安装过 Wireshark 了,那么这些工具已经在你的系统中了。假如还没装的话,那么我们接下来就安装 Wireshark 命令行工具。 需要注意的是,在基于 Debian 的发行版上我们可以不用安装 Wireshark GUI 而仅安装命令行工具,但是在 Red Hat 及 基于它的发行版中则需要安装整个 Wireshark 包。Debian, Ubuntu 或 Linux Mint复制代码代码如下:$ sudo apt-get install wireshark-commonFedora, CentOS 或 RHEL复制代码代码如下:$ sudo yum install wireshark当安装好工具后, 就可以开始使用 editca 与 mergecap 了。pcap 文件过滤通过 editcap, 我们能以很多不同的规则来过滤 pcap 文件中的内容,并且将过滤结果保存到新文件中。首先,以“起止时间”来过滤 pcap 文件。 - A < start-time >和 - B < end-time >选项可以过滤出在这个时间段到达的数据包(如,从 2:30 ~ 2:35)。时间的格式为 “ YYYY-MM-DD HH:MM:SS。复制代码代码如下:$ editcap -A 2014-12-10 10:11:01 -B 2014-12-10 10:21:01 input.pcap output.pcap 也可以从某个文件中提取指定的 N 个包。下面的命令行从 input.pcap 文件中提取100个包(从 401 到 500)并将它们保存到 output.pcap 中:复制代码代码如下:$ editcap input.pcap output.pcap 401-500使用 -D < dup-window > (dup-window可以看成是对比的窗口大小,仅与此范围内的包进行对比)选项可以提取出重复包。每个包都依次与它之前的 < dup-window >-1 个包对比长度与MD5值,假如有匹配的则丢弃。复制代码代码如下:$ editcap -D 10 input.pcap output.pcap 遍历了 37568 个包, 在 10 窗口内重复的包仅有一个,并丢弃。也可以将 < dup-window >定义成时间间隔。使用-w < dup-time-window >选项,对比< dup-time-window >时间内到达的包。复制代码代码如下:$ editcap -w 0.5 input.pcap output.pcap 检索了 50000 个包, 以0.5s作为重复窗口,未找到重复包。分割 pcap 文件当需要将一个大的 pcap 文件分割成多个小文件时,editcap 也能起很大的作用。将一个 pcap 文件分割成数据包数目相同的多个文件复制代码代码如下:$ editcap -c 输出的每个文件有相同的包数量,以 < output-prefix >-NNNN的形式命名。以时间间隔分割 pcap 文件复制代码代码如下: $ editcap -i 合并 pcap 文件假如想要将多个文件合并成一个,用 mergecap 就很方便。当合并多个文件时,mergecap 默认将内部的数据包以时间先后来排序。复制代码代码如下:$ mergecap -w output.pcap input.pcap input2.pcap [input3.pcap . . .]假如要忽略时间戳,仅仅想以命令行中的顺序来合并文件,那么使用 -a 选项即可。例如,下列命令会将 input.pcap 文件的内容写入到 output.pcap, 并且将 input2.pcap 的内容追加在后面。复制代码代码如下:$ mergecap -a -w output.pcap input.pcap input2.pcap 总结在这篇指导中,我演示了多个 editcap、 mergecap 操作 pcap 文件的例子。除此之外,还有其它的相关工具,如 reordercap用于将数据包重新排序,text2pcap 用于将 pcap 文件转换为文本格式, pcap-diff用于比较 pcap 文

PostgreSQL之时间戳自动更新

MySQL:什么时候NOT IN不等于NOT EXISTS

半小时一篇文过完C语言基础知识点

友情链接

滇ICP备2023006006号-39