C语言变长数组之剖析

【WRF工具】WRF 模型输出可视化工具 plotfmt 阅读详情

 

1、引言

我们知道,与C++等现代编程语言不同,传统上的C语言是不支持变长数组功能的,也就是说数组的长度是在编译期就确定下来的,不能在运行期改变。不过,在C99标准中,新增的一项功能就是允许在C语言中使用变长数组。然而,C99定义的这种变长数组的使用是有限制的,不能像在C++等语言中一样自由使用。

2、说明

参考文献[1]中对变长数组的说明如下:

C99 gives C programmers the ability to use variable length arrays, which are arrays whose sizes are not known until run time. A variable length array declaration is like a fixed array declaration except that the array size is specified by a non-constant expression. When the declaration is encountered, the size expression is evaluated and the array is created with the indicated length, which must be a positive integer. Once created, variable length array cannot change in length. Elements in the array can be accessed up to the allocated length; accessing elements beyond that length results in undefined behavior. There is no check required for such out-of-range accesses. The array is destroyed when the block containing the declaration completes. Each time the block is started, a new array is allocated.

以上就是对变长数组的说明,此外,在文献[1]中作者还说明,变长数组有以下限制:

1、变长数组必须在程序块的范围内定义,不能在文件范围内定义变长数组;

2、变长数组不能用static或者extern修饰;

3、变长数组不能作为结构体或者联合的成员,只能以独立的数组形式存在;

4、变长数组的作用域为块的范围,对应地,变长数组的生存时间为当函数执行流退出变长数组所在块的时候;

上述限制是最常见的一些限制因素,此外,当通过typedef定义变长数组类型时,如何确定变长数组的长度,以及当变长数组作为函数参数时如何处理,作者也做了一一说明。详细的细节情况请参阅文献[1]。由于变长数组的长度在程序编译时未知,因此变长数组的内存空间实际上是在栈中分配的。

gcc虽然被认为是最遵守C语言标准的编译器之一,但是它并不是严格按照ISO C标准规定的方式来实现的。gcc的实现方式采取了这样的策略:最大限度地遵守标准的规定,同时从实用的角度做自己的扩展。当然,gcc提供了编译选项给使用者以决定是否使用这些扩展功能。gcc的功能扩展分为两种,一种是gnu自己定义的语言扩展;另外一种扩展是在C89模式中引入由C99标准定义的C语言特性。在参考文献[2]中,有关gcc的C语言扩展占据了将近120页的篇幅,扩展的语言功能多达几十个,由此可看出gcc的灵活程度。

在参考文献[2]中,对变长数组的描述如下:

Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C89 mode and in C++. (However, GCC’s implementation of variable-length arrays does not yet conform in detail to the ISO C99 standard.) These arrays are declared like any other automatic arrays, but with a length that is not a constant expression. The storage is allocated at the point of declaration and deallocated when the brace-level is exited.

以上这段话并没有详细的说明gcc的变长数组实现和ISO C99的差异究竟体现在什么地方,但是从描述来看,基本上和文献[1]中的描述是一致的。文献[2]中没有说明而在文献[1]中给予了说明的几点是:变长数组是否能用static或者extern修饰;能否作为复合类型的成员;能否在文件域起作用。

 

 

另外,在文献[2]中提到,采用alloca()函数可以获得和变长数组相同的效果。在作者所用的Red Hat 9.0(Linux 2.4.20-8)上,这个函数被定义为一个库函数:

 

 

#include <alloca.h>

 

 

void *alloca(size_t size);

 

 

这个函数在调用它的函数的栈空间中分配一个size字节大小的空间,当调用alloca()的函数返回或退出的时候,alloca()在栈中分配的空间被自动释放。当alloca()函数执行成功时,它将返回一个指向所分配的栈空间的起始地址的指针;然而,非常特别的一点是,当alloca()函数执行失败时,它不会像常见的库函数那样返回一个NULL指针,之所以会出现这样的状况,是由于alloca()函数中的栈调整通常是通过一条汇编指令来完成的,而这样一条汇编指令是无法判断是否发生溢出或者是否分配失败的。alloca()函数通常被实现为内联函数,因此它是与特定机器以及特定编译器相关联的,可移植性因此而大打折扣,实际上是不推荐使用的。

 

 

作者之所以会关注变长数组的问题是出于一次偶然的因素,在调试的时候发现gdb给出的变长数组的类型很怪异,由此引发作者对gcc中的变长数组进行了测试。本文中给出的就是对测试结果的说明和分析。

 

 

3、实例

第一个测试所用的源代码很简单,如下所示:

 1 int

2 main(int argc, char *argv[])

3 {

4 int i, n;

5

6 n = atoi(argv[1]);

7 char arr[n+1];

8 bzero(arr, (n+1) * sizeof(char));

9 for (i = 0; i < n; i++) {

10 arr[i] = (char)('A' + i);

11 }

12 arr[n] = '';

13 printf("%sn", arr);

14

15 return (0);

16 }

上述程序名为dynarray.c,其工作是把参数argv[1]的值n加上1作为变长数组arr的长度,变长数组arr的类型为char。然后向数组中写入一些字符,并将写入的字符串输出。

像下面这样编译这个程序:

[root@cyc test]# gcc -g -o dynarray dynarray.c

 

 

然后,用gdb观察dynarray的执行情况:

[root@cyc test]# gdb dynarray

 

 

(gdb) break main

 

 

Breakpoint 1 at 0x80483a3: file dynarray.c, line 6.

(gdb) set args 6

 

 

(gdb) run

 

 

Starting program: /root/source/test/a.out 6

 

 

 

Breakpoint 1, main (argc=2, argv=0xbfffe224) at dynarray.c:6

6 n = atoi(argv[1]);

(gdb) next

 

 

7 char arr[n+1];

(gdb) next

 

 

8 bzero(arr, (n+1) * sizeof(char));

(gdb) print/x arr

 

 

$2 = {0xb0, 0xe5}

(gdb) ptype arr

 

 

type = char [2]

(gdb) print &arr

 

 

$3 = (char (*)[2]) 0xbfffe1c8

这里,当程序执行流通过了为变长数组分配空间的第7行之后,用print/x命令打印出arr的值,结果居然是两个字节;而如果尝试用ptype打印出arr的类型,得到的结果居然是arr是一个长度为2的字符数组。很明显,在本例中,因为提供给main()函数的参数argv[1]是6,因此按常理可知arr应该是一个长度为7的字符数组,但很遗憾,gdb给出的却并不是这样的结果。用print &arr打印出arr的地址为0xbfffe1c8。继续上面的调试过程:

(gdb) x/4x &arr

 

 

0xbfffe5c8: 0xbfffe5b0 0xbfffe5c0 0x00000006 0x40015360

(gdb) x/8x $esp

 

 

0xbfffe5b0: 0xbffffad8 0x42130a14 0xbfffe5c8 0x0804828d

0xbfffe5c0: 0x42130a14 0x4000c660 0xbfffe5b0 0xbfffe5c0

可以看到,在&arr(即地址0xbfffe5c8)处的第一个32位值是0xbfffe5b0,而通过x/8x $esp可以发现,栈顶指针esp恰好就指向的是0xbfffe5b0这个位置。于是,可以猜想,如果arr是一个指针的话,那么它指向的就恰好是当前栈顶的指针。继续上面的调试:

(gdb) next

 

 

9 for (i = 0; i < n; i++) {

(gdb) next

 

 

10 arr[i] = (char)('A' + i);

(gdb) next

 

 

9 for (i = 0; i < n; i++) {

(gdb) until

 

 

12 arr[n] = '';

(gdb) next

 

 

13 printf("%sn", arr);

(gdb) x/8x $esp

 

 

0xbfffe5b0: 0x44434241 0x42004645 0xbfffe5c8 0x0804828d

0xbfffe5c0: 0x42130a14 0x4000c660 0xbfffe5b0 0xbfffe5c0

注意上面表示为蓝色的部分,由于Intel平台采用的是小端字节序,因此蓝色的部分实际上就是’ABCDEF’的十六进制表示。而红色的32位字则暗示着arr就是指向栈顶的指针。为了确认我们的这一想法,下面通过修改arr的值来观察程序的执行情况(需要注意的是:每一次运行时堆栈的地址是变化的):

(gdb) run

 

 

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /root/source/test/dynarray 6

 

 

 

Breakpoint 1, main (argc=2, argv=0xbfffde24) at dynarray.c:6

6 n = atoi(argv[1]);

(gdb) next

 

 

7 char arr[n+1];

(gdb) next

 

 

8 bzero(arr, (n+1) * sizeof(char));

(gdb) print/x &arr

 

 

$3 = 0xbfffddc8

(gdb) x/8x $esp

 

 

0xbfffddb0: 0xbffffad8 0x42130a14 0xbfffddc8 0x0804828d

0xbfffddc0: 0x42130a14 0x4000c660 0xbfffddb0 0xbfffddc0

(gdb) set *(unsigned int*)&arr=0xbfffddc0

 

 

(gdb) x/8x $esp

 

 

0xbfffddb0: 0xbffffad8 0x42130a14 0xbfffddc8 0x0804828d

0xbfffddc0: 0x42130a14 0x4000c660 0xbfffddc0 0xbfffddc0

(gdb) next

 

 

9 for (i = 0; i < n; i++) {

(gdb) next

 

 

10 arr[i] = (char)('A' + i);

(gdb) next

 

 

9 for (i = 0; i < n; i++) {

(gdb) until

 

 

12 arr[n] = '';

(gdb) next

 

 

13 printf("%sn", arr);

(gdb) x/8x $esp

 

 

0xbfffddb0: 0xbffffad8 0x42130a14 0xbfffddc8 0x0804828d

0xbfffddc0: 0x44434241 0x40004645 0xbfffddc0 0xbfffddc0

地址0xbfffddc8(也就是arr的地址)处的值本来为0xbfffddb0,我们把它改成了0xbfffddc0,于是,当程序运行到向变长数组输入数据完成之后,我们发现这次修改的地址的确是从0xbfffddc0开始的。这就表明arr的确像我们通常所理解的一样,数组名即指针。只不过这个指针指向的位置在它的下方(堆栈向下生长),而不是像大多数时候一样指向上方的某个位置。

4、分析

上面的测试结果表明:变长数组的确是在栈空间中分配的;变长数组的数组名实际上就是一个地址指针,指向数组所在的栈顶位置;而GDB无法判断出变长数组的数组名实际上是一个地址指针。

GDB为什么无法准确判断出变长数组的类型的原因尚不清楚,但是作者猜测这和变长数组的动态特性有关,由于变长数组是在程序动态执行的过程生成的,GDB无法向对待常规数组一样从目标文件包含的.stabs节中获得长度信息,于是给出了错误的类型信息。

另外,作者对变长数组的作用域进行了测试,测试代码根据上例修改得到,如下所示:

 1 int n;

2 char arr[n+1];

3

4 int

5 main(int argc, char *argv[])

6 {

7 int i;

8

9 n = atoi(argv[1]);

10 bzero(arr, (n+1) * sizeof(char));

11 for (i = 0; i < n; i++) {

12 arr[i] = (char)('A' + i);

13 }

14 arr[n] = '';

15 printf("%sn", arr);

16

17 return (0);

18 }

当如下编译的时候,gcc会提示出错:

[root@cyc test]# gcc -g dynarray.c

 

 

dynarray.c:2: variable-size type declared outside of any function

可见gcc不允许在文件域定义变长数组。

对于gcc中的变长数组能否用static修饰则使用如下代码进行测试:

 1 int

2 main(int argc, char *argv[])

3 {

4 int i, n;

5

6 n = atoi(argv[1]);

7 static char arr[n+1];

8 bzero(arr, (n+1) * sizeof(char));

9 for (i = 0; i < n; i++) {

10 arr[i] = (char)('A' + i);

11 }

12 arr[n] = '';

13 printf("%sn", arr);

14

15 return (0);

16 }

当编译此源文件的时候,gcc给出如下错误提示:

[root@cyc test]# gcc -g dynarray.c

 

 

dynarray.c: In function `main':

dynarray.c:7: storage size of `arr' isn't constant

dynarray.c:7: size of variable `arr' is too large

根据提示,可知当数组用static修饰的时候,不能将其声明为变长数组。至于这里的提示说arr太大,作者猜测可能的原因是这样的:对于整数,gcc在编译期赋予了一个非常大的值,于是导致编译报错,不过这仅仅是猜测而已。

最后需要说明的是,作者是出于对gcc如何实现变长数组的方式感兴趣才进行上面的这些测试的。对于编程者来说,不用做这样的测试,也不需要知道变长数组是位于栈中还是其它地方,只要知道变长数组有上面这样一些限制就行了。另外,本文中有很多地方充斥着作者的推断和猜测。不过这并没有太大的关系,又不是写论文,谁在乎呢?

另外,上面的测试也说明了:尽管文献[2]没有像文献[1]中那样仔细说明变长数组的限制条件,但实际上它就是那样工作的。再一次体现出gcc的确很好地遵守了C标准的规定。

参考文献

[1] Samuel P. Harbison III, Guy L. Steele Jr.; C: A Reference Manual Fifth Edition; Prentice Hall, Pearson Education, Inc.; 2002

 

 

[2] Richard M. Stallman and the GCC Developer Community; Using the GNU Compiler Collection; FSF; May 2004

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Android 12.0 系统开机设置默认横屏显示 在12.0的系统产品开发中,对于产品需求来说,由于是宽屏设备所以产品需要开机默认横屏显示,开机横屏显示这就需要从两部分来实现,一部分是开机动画横屏显示,另一部分是系统显示横屏显示,从这两方面就可以做到开机默认横屏显示了。 阅读详情

相关推荐

告别串口助手!手把手教你用Matlab直接读取STM32的传感器数据(附完整代码)

本文详细介绍了如何利用Matlab直接与STM32进行串口通信,实现传感器数据的实时采集与分析。通过完整的代码示例和系统架构设计,帮助开发者告别传统的串口助手工具链,打造高效、自动化的数据采集系统。文章涵盖了从硬件配置、通信协议设计到Matlab数据处理与可视化的全流程,特别适合嵌入式开发与数据分析结合的实践场景。

weixin_42629671的博客 429

C语言变长数组

今天在做“2. 题目:有n个人围成一圈,顺序排号,从第一个开始报数(从1到3报数),凡报到3的人退出圈子,问最后最后留下的是原来第几号的那位.提示:用数组完成”该题时,想根据输入来确定数组的长度n,也就是想用动态数组,即使用malloc来给数组分配内存空间,根据我的记忆在VC6.0下诸如int a[n];这样的语句是报错的,但是今天在GCC上竟然通过了,当时与同学讨论未果,于是找了些资料,以下为较

苏嵌——杭天龙 1393

Kindle电子书DRM破解全攻略:从下载到转换的完整流程(含最新DeDRM插件配置)

本文详细介绍了如何合法移除个人购买的Kindle电子书的DRM保护,实现跨设备阅读与备份。核心内容包括使用最新DeDRM插件(如noDRM分支)配合Calibre进行配置,并针对亚马逊最新的KFX加密格式提供了密钥提取与实战操作指南。文章强调该方法仅适用于个人合理使用,旨在帮助读者真正掌控自己的数字资产。

delta的博客 576

如何在VC++6.0中运行C语言中的变长数组?

程序是这样的: #include #include int main(void) {     int i, n;     printf("How many numbers do you want to reverse: ");     scanf("%d", &n);     int a[n];     printf("Enter %d num

陈刚编程心得与知识集 1560

AgentFlow:模块化智能体框架与Flow-GRPO强化学习实战解析

在人工智能领域,大语言模型(LLM)与智能体(Agent)技术正推动着自动化任务处理的发展。其核心原理在于通过规划、执行与反馈的循环,使AI系统能够自主调用工具解决复杂问题。这一技术的价值在于突破单一模型的局限性,实现更可靠的多步骤推理。在实际应用中,智能体框架需要处理搜索、代码执行、数学计算等多种场景。本文聚焦的AgentFlow框架,正是通过模块化设计将智能体拆分为规划者、执行者、验证者和生成者四个专家模块,并引入Flow-GRPO强化学习算法进行流程内优化。该框架在搜索增强与工具调用等任务上展现出显著

weixin_42311427的博客 369

基于Simulink的自适应控制(Adaptive Control)电励磁同步电动机

目录基于Simulink的自适应控制(Adaptive Control)电励磁同步电动机项目实例1. 项目背景2. 系统架构2.1 电励磁同步电动机模型2.2 三相逆变器模型2.3 自适应控制器(Adaptive Controller)2.4 系统框图3. 模型设计3.1 创建Simulink模型3.2 自适应控制(Adaptive Control)设计3.3 仿真环境搭建4. 参数优化4.1 自动调优5. 实际应用案例5.1 工业驱动系统5.2 风力发电系统6. 结论进一步优化建议需求分析电励磁同步电动机

MHD0815的博客 266

变长结构体

看到老师写的一个结构体很好奇,结构体的最后是一个长度为0的数组,当时感觉老师是不是写错了,这样写意义何在呢?都没有分配空间,貌似没有存在的意义。 后来网上查了一下,其实这是在很多高级的东东里面都用到的东西,linux kernel, MFC, openoffice, 估计更多的地方都用到了。 先说说我的理解: struct example{     __u16 tag_type;

深入思考 1330

电脑硬件:电脑主板上的接口介绍,看完你就懂了

它们主要用于连接老式的键盘和鼠标,虽然速度和兼容性不如USB接口,但在某些特定场合依然发挥着重要作用。PCIe接口是主板上的扩展能力象征,可以连接各种扩展卡,如显卡、声卡、网卡等。电脑主板作为电脑硬件组成关键的部分,今天给大家分享一下电脑主板上有哪些接口,具体每一个接口的特点、使用场景,有需要的朋友可以来了解一下!它们不仅能连接鼠标、键盘、U盘、打印机,还能连接各种外设,如移动硬盘、游戏手柄等。:虽然现在用得越来越少,但在一些特定场合,如工业控制、老旧设备上,仍然需要使用PS/2接口。

IT技术分享社区 2787

c语言可变长数组带来的好处

#include <stdlib.h> #include <stdio.h> #include <string.h> typedef struct msg_info_s{ char name[16]; char msg_info[32]; }msg_info_t; typedef struct msg_s{ int id; char data[0]; }msg_...

努力的志 的博客 1238

Superpowers与 OpenSpec、Spec Kit 对比

按照官方仓库定义,Superpowers 是一套构建在 composable skills 之上的完整软件开发工作流。它不是单纯的 prompt 集合,而是把一系列工程动作做成默认流程。一个需要明确的点:Superpowers 并不只偏 Claude Code,根据。

人不风流枉少年 1813

【FF-A】第四章 Concepts

SPM的责任分为两个组件:SPM调度器(SPMD)和SPM核心(SPMC)。这两个组件都可以访问整个物理地址空间,并且是受信任的计算基础的一部分。当无需区分这两个组件时,术语SPM被使用。这些组件的责任如下所示。SPMD 位于 EL3 中,以 AArch64 或 AArch32 执行状态运行。SPMC 可以与 SPMD 共存在 EL3 中,也可以位于相邻的异常级别,即 S-EL1 或 S-EL2。SPM 在启动时初始化 EL3 LSP,并在运行时促进它们与其他端点之间的通信。

baron-周贺贺-代码改变世界ctw 1195

C语言实现变长数组

struct MyData  {     int nLen;     char data[0]; };          开始没有理解红色部分的内容,上网搜索下,发现用处很大,记录下来。                    在结构中,data是一个数组名;但该数组没有元素;该数组的真实地址紧随结构体MyData之后,而这个地址就是结构体后面数据的地址(如果给这个结构体分配的内容大于

Virtual_Func的博客 4458

Android Telephony主要模块及功能

Telephony的代码主要由五个模块组成 1.Telephony Frameworks 代码区域: frameworks/opt/telephony/ 其中主要的文件在如下目录 /frameworks/opt/telephony/src/java/com/android/internal/telephony/ 其中比较常见的有以下几类文件 1.BaseCommands、RIL构成R

泊客 7723

C 语言数组全方位详解:从基础到实战(含 C99 变长数组)

本文全面介绍了C语言中数组的核心概念和使用方法。主要内容包括:一维数组的创建、初始化及遍历技巧;数组内存连续存储的特性;使用sizeof计算数组元素数量的实用方法;二维数组作为"数组的数组"的本质特性及其初始化与遍历方式;C99新增的变长数组特性。文章还通过字符汇聚和二分查找两个经典案例,展示了数组的实际应用。最后强调理解数组内存特性对后续学习指针和字符串处理的重要性。全文既适合初学者系统学习,也可作为进阶者的查漏补缺指南。

hwtwhy的博客 819

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

D4—C语言数组深度解析:从内存布局到算法实践

如果说变量是存储数据的单个盒子,那么数组就是一组整齐排列的相同盒子。理解数组,就是理解计算机如何高效组织批量数据的开始。

一名零基础编程小白的成长历程 1172

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

C语言变长数组原理与工程实践指南

变长数组是C语言中实现动态内存管理的重要技术,其核心原理是通过运行时确定数组长度来突破静态数组的限制。从技术实现看,C99标准引入的VLA特性与GNU扩展的零长度数组各具特点,前者支持栈上动态分配,后者则常用于构建变长结构体。这类技术在网络协议栈、嵌入式系统等场景具有显著价值,能通过单次内存分配实现高效的内存管理,相比传统指针方案可减少内存碎片并提升缓存命中率。特别是在USB驱动开发、视频流处理等需要动态调整缓冲区的场景,零长度数组与变长结构体的组合能有效降低内存开销。本文通过对比测试数据展示了不同实现方案

weixin_30336531的博客 254

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【C语言】 数组

本文系统介绍了C语言中数组的相关知识。首先阐述了数组的概念,即相同类型元素的集合,分为一维和二维数组。详细讲解了数组的创建、初始化方法及使用技巧,包括下标访问、输入输出操作。通过内存存储分析展示了数组元素的连续性特征,并解释了sizeof计算数组元素数量的方法。针对二维数组,说明了其初始化方式、使用方法和内存存储特点。最后介绍了C99标准中的变长数组特性,允许运行时确定数组大小。全文通过代码示例直观展示了数组的各种操作,为理解和使用数组提供了全面指导。

youren_7715的博客 557

C语言数组

有一个比较迷惑的点,变长数组的意思是数组的大小是可以使用变量来指定的,在程序运行的时候,根据变量的大小来指定数组的元素个数,而不是说数组的大小是可变的。从输出的结果来看,每一行内部的每个元素都是相邻的,地址之间相差4个字节,跨行位置处的两个元素(如:arr[0][4]和arr[1][0])之间也是差4个字节,所以二维数组中的每个元素都是连续存放的。的,如果我们把一维数组做为数组的元素,这时候就是二维数组,二维数组作为数组元素的数组被称为三维数组,二维数组以上的数组统称为多维数组。

2506_94220139的博客 1832

C语言变长数组与零长度数组深度解析

变长数组(VLA)是C语言中实现动态内存管理的重要特性,它允许在运行时确定数组长度,突破了传统固定长度数组的限制。从内存管理原理看,VLA在栈上分配内存,具有自动生命周期管理的特点,而零长度数组作为GNU扩展,则通过不占用实际存储空间的特性,实现了结构体与可变数据的连续内存模型。这些技术在网络编程、嵌入式系统等场景中展现出显著优势,特别是在处理动态缓冲区、多媒体数据等需要高效内存管理的场景。通过对比指针方案,零长度数组在内存连续性和分配效率上具有20-30%的性能提升。Linux内核中的URB实现和网络协议

weixin_36001279的博客 516

C语言数组全解析:从入门到精通

从输出的结果来看,每一行内部的每个元素都是相邻的,地址之间相差4个字节,跨行位置处的两个元素(如:arr[0][4]和arr[1][0])之间也是差4个字节,所以二维数组中的每个元素都是连续存放的。如果我们把一维数组做为数组的元素,这时候就是二维数组,二维数组作为数组元素的数组被称为三维数组,二维数组以上的数组统称为多维数组。借助 [ ] 可以访问数组的元素,比如我们访问下标为7的元素,就使用 arr[7],想要访问下标为3的元素,就使用arr[3],如果数组有n个元素,最后一个元素的下标是n-1。

2401_87570983的博客 910

C语言入门教程(第5讲):数组详解——一次性搞懂一维数组、二维数组与内存布局

掌握数组,就是掌握C语言的记忆力。本讲带你彻底搞懂一维数组、二维数组与内存布局,让程序真正“记住”数据。

ZGX_2005的博客 812

C语言数组分析

定义二维数组的时候,行可以省略,列绝对不能省略。int a[][4];// 合法写法int a[3][];// 编译报错静态数组内存连续,空间固定;malloc 动态内存不一定连续。C99 变长数组不能动态扩容。,所有下标底层都是指针解引用运算。多维数组拆解思路:二维数组 = 存放多个一维数组的数组;三维数组 = 存放多个二维数组的数组。分清两个极易混淆概念:→ 数组指针(指针,接收多维数组)int *p[n]→ 指针数组(数组,存放一堆指针)

W_32600的博客 574

C语言变长数组(VLA)详解:从栈内存分配到工程实践指南

在C语言编程中,数组作为基础数据结构,其传统固定长度特性常导致内存浪费或使用不便。为解决此问题,C99标准引入了变长数组(Variable-Length Array, VLA)特性,允许在运行时根据变量确定数组长度。VLA本质是在栈内存上分配,具有分配速度快、自动管理内存的优势,但同时也受栈空间限制,存在栈溢出风险。这一特性填补了小型临时缓冲区的需求空白,特别适用于函数内根据参数动态创建数组的场景。然而,由于可移植性问题和潜在安全风险,C11标准将其降为可选特性。在实际工程中,需根据场景权衡使用VLA与动态

weixin_33146151的博客 249

c语言进阶指南(数组)

*系统为内存中的每一字节的空间都进行了编码,这些编码被称为地址**,以一个变量的创建为例,当我们创建一个变量时,**系统会根据变量的声明来划分空间给变量**(int的数据空间为4字节,char为1字节,float为4字节)。在c语言中,任何变量(基本变量,指针变量,结构体变量,数组变量)的空间都是**整体开辟**,但任何元素的**起始地址**一定是**最小的**。我们可知arr数组中的元素为{1,2,3,0,0,0,0,0,0,0}10个元素。当数组进行初始化后,数组中的每一个元素都具有一个对应的下标。

m0_74824865的博客 902

鹏哥c语言复习第五讲数组

我今天也是开始51单片机的了,但是还是会按时更新复习资料。

shiyu_anran的博客 376

C语言速成秘籍—— 数组(一维数组)

一维数组,数组的创建和初始化和类型(数组的大小的多种写法和变长数组,数组赋初值,字符数组和字符串数组不同(\0的存在),不同数据类型的数组);一维数组的使用:通过下标使用,数组是连续存放的;通过sizeof计算数组的大小,数组某个元素的大小,数组元素的个数,sizeof计算的大小是以字节为单位的

2402_89218457的博客 693
上一篇: C和C++学习经验,说得挺有道理的
下一篇: 判断CPU是大端还是小端
flickedball
博客等级 码龄19年 12粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值