由C数组array[-1]想到的

本文通过测试发现,C语言中使用负数下标访问数组不会报错且能正常运行,这引发了关于C数组边界检查和指针操作的讨论。数组越界访问可能引发潜在的危险,例如栈或缓冲区溢出,可能导致不可预知的系统行为。作者提醒开发者在使用C语言时要谨慎处理指针,避免类似strcpy和strcat等可能导致溢出的函数,并增强程序的错误处理能力。

        昨天一个同学和我谈起他在测试C程序时时发现使用-1为下标来访问数组,gcc并没有报错,而且能够运行成功,这个以前我还没试过,处于好奇,今天下午抽了点时间看了一下。

 

     下面是我使用的测试代码,运行环境是DEV-C++4.9.9.2,编译器使用的是gcc。

 

  int a[4] = {1,2,3,4};
  int b[4] = {5,6,7,18};
  //  double c = 20.3;
  printf("a[-1] is %d/n",a[-1]);
  printf("a[-4] is %d/n",a[-4]);
  printf("b[4] is %d/n",b[4]);

 

  运行结果为

 

a[-1] is 18
a[-4] is 5
b[4] is 1
请按任意键继续. . .

 

现在我和室友的解释是这样,程序中a,b数组按下列方式进入运行栈:

 

....a[3],a[2],a[1],a[0],b[3],b[2],b[1],b[0]....

 

这时a[-1]恰好指到b[3],b[-4]指到b[0],而同理b[4]指到了a[0]。

 

    以前自己写程序从来没想过去把数组程序写成这样,觉得那样程序会报错,然而现在的执行结果却让

 

我很吃惊,非但不报错,而且运行很正常。那么,对于C里面的数组,就有些“名存实亡”的嫌疑,因为

 

这里完全是C指针在控制着一切,并不存在边界检查这样的机制(书上的解释是提高运行速度),那么C

 

数组实际上就是顺序存储了一列同类型的数,然后让一个常数指针指向其开始地址即可。这时这个强大而

 

又可怕的指针貌似就可“为所欲为”了,向上或向下访问,已经不存在障碍了。那么这里一个潜在的信号

 

是:dangerous。因为你可以用它访问超过栈或缓冲区的边界,如果修改那里面的值,就可能造成不可

 

知的危险。一些对服务器或其他软件的攻击就可能是利用这一点。

 

     C里还有其他函数也存在类似的问题:如strcpy和strcat等,很有可能超出目的数组的边界,导致溢

 

出,还有输入函数,都存在这样的问题。这里给我们提了醒:小心使用C里面的指针,同时做好程序里的

 

错,尽力来避免这些错误。

<think>嗯,用户问的是C语言中如何定义大小不确定的动态数组。首先,我得回忆一下C语言的基本知识。C语言里,传统的数组是静态的,大小在编译时就要确定。但用户需要的是动态数组,也就是运行时才能确定大小的数组。这时候,应该想到动态内存分配函数,比如malloc、calloc和realloc。 首先,我得解释为什么需要动态数组。静态数组的大小固定,不够灵活,而动态数组可以根据程序运行时的需求分配内存,更高效地利用资源。然后,分步骤说明如何定义。可能的方法有两种:使用malloc函数动态分配,或者使用柔性数组成员。 根据引用[2]中的例子,柔性数组成员的结构体,比如struct DynamicArray,里面有一个不定长的数组。这样在创建时,通过malloc分配结构体大小加上数组所需的内存,就能实现动态数组。这种方法的好处是内存连续,访问效率高,但需要手动管理内存。 另一种方法是使用指针和malloc,比如先声明一个指针,然后在运行时用malloc分配所需大小的内存空间。例如,int *arr = malloc(n * sizeof(int)); 这样就可以创建一个大小为n的数组。这种方法更基础,但同样需要注意内存释放,避免泄漏。 还要注意,动态分配的内存必须用free释放,否则会导致内存泄漏。引用[3]提到动态数组的ADT实现,可能涉及更复杂的功能,比如插入、删除元素,这时候可能需要结合结构体和函数指针来管理数组。 另外,用户可能需要知道如何调整数组大小,这时候可以用realloc函数。比如,当数组填满后,重新分配更大的内存块,并复制原有数据。这需要小心处理,避免内存错误。 最后,总结几种方法的优缺点,帮助用户根据实际需求选择合适的方式。比如,柔性数组成员的适用场景,指针方法的灵活性,以及动态数组库的使用可能带来的便利。</think>### C语言动态数组定义方法 在C语言中定义大小不确定的动态数组,主要通过动态内存分配实现。以下是三种常用方法及操作步骤: #### 一、malloc基础方法 ```c #include <stdlib.h> int main() { int n = 10; // 运行时确定的大小 int *dynamicArray = malloc(n * sizeof(int)); // 分配内存 if(dynamicArray == NULL) { // 处理内存分配失败 } // 使用数组 for(int i=0; i<n; i++){ dynamicArray[i] = i*2; } free(dynamicArray); // 必须释放内存[^2] return 0; } ``` *优点*:简单直接 *缺点*:需手动管理内存 #### 二、柔性数组成员(C99标准) ```c #include <stdlib.h> struct DynArray { int length; int data[]; // 柔性数组成员 }; struct DynArray* createArray(int size) { struct DynArray* arr = malloc(sizeof(struct DynArray) + size*sizeof(int)); arr->length = size; return arr; } // 使用示例: struct DynArray* arr = createArray(20); arr->data[5] = 100; // 访问元素 free(arr); // 一次性释放[^2] ``` *特点*:内存连续,访问效率高 #### 三、动态数组ADT实现 通过结构体封装数组操作: ```c typedef struct { int *data; size_t capacity; size_t size; } Vector; void initVector(Vector *v, size_t initialSize) { v->data = malloc(initialSize * sizeof(int)); v->size = 0; v->capacity = initialSize; } void pushBack(Vector *v, int value) { if(v->size >= v->capacity) { v->capacity *= 2; v->data = realloc(v->data, v->capacity * sizeof(int)); // 扩容[^3] } v->data[v->size++] = value; } // 使用示例: Vector v; initVector(&v, 5); pushBack(&v, 10); free(v.data); // 必须释放[^3] ``` ### 关键注意事项 1. 内存对齐:动态数组元素保持连续存储 2. 扩容策略:常用倍增算法(如从N扩展到2N) 3. 错误处理:每次分配后必须检查返回值 4. 内存释放:必须成对使用malloc/free ### 方法对比表 | 方法 | 内存管理 | 扩容难度 | 适用场景 | |-------------|----------|----------|------------------| | malloc基础 | 手动 | 困难 | 简单临时数据存储 | | 柔性数组 | 半自动 | 不可 | 固定大小结构体 | | ADT封装 | 自动 | 容易 | 复杂数据结构 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值