长度为0的数组——C语言的非标准用法之一

本文探讨了GCC环境下长度为0的数组的使用方式及其在变长缓冲中的应用,解释了这种特殊用法背后的原理和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在标准C和C++中,长度为0的数组是被禁止使用的。不过在GNU C中,存在一个非常奇怪的用法,那就是长度为0的数组,比如Array[0];很多人可能觉得不可思议,长度为0的数组是没有什么意义的,不过在这儿,它表示的完全是另外的一层意思,这个特性是不可移植的,所以,如果你致力于编写可移植,或者是稍稍需要跨平台的代码,这些Trick最好还是收起来的好。

在GNU的指南中,它是如此写道:

struct line {
int length;
char contents[0];
};

//...ommit code here

{
struct line *thisline
   = (struct line *) malloc (sizeof (struct line) + this_length);
thisline->length = this_length;
}

这个用法主要用于变长Buffer,struct line的大小为4,结构体中的contents[0]不占用任何空间,甚至是一个指针的空间都不占,contents在这儿只是表示一个常量指针,这个特性是用编译器来实现的,即在使用thisline->contents的时候,这个指针就是表示分配内存地址中的某块buffer,比如malloc (sizeof (struct line) + this_length)返回的是0x8f00a40,thisline->contents指向的位置就是(0x8f00a40 + sizeof(struct line)),而这儿sizeof(struct line)仅仅是一个int的四字节。

对于这个用法,我们定义的结构体指针可以指向任意长度的内存buffer,这个技巧在变长buffer中使用起来相当方便。可能有朋友说,为什么不把最后的contents直接定义为一个指针呢?这儿的差别是这样的,如果定义为一个指针,它需要占用4Bytes,并且在申请好内存后必须人为赋地址才可以。如果使用这个用法,这个常量指针不占用空间,并且无需赋值。

但是,方便并不是绝对的,在释放分配的内存的时候,由于函数free会认为*thisline 只是指向一个4字节的指针,即只会释放length的空间,而对于后面占据大头的buffer却视而不见,这个就需要人为干预;而对于后面的声明指针的方式,则可以直接用Free(thisline->contents)的方式释放掉分配的内存。

ASSERT:除非必要,不要轻易使用这个功能,GNU C下可以编译通过,所以你在使用vc++,那就不用尝试了,编译都无法通过。

### 如何在C语言中从数组中删除元素 在C语言中,由于数组大小固定不可变,因此无法像某些高级语言那样直接移除数组中的某个元素并缩小数组长度。通常的做法是创建一个新的临时数组来存储不需要被删除的元素或将后续元素向前移动覆盖待删除位置。 #### 方法一:通过位移法实现原地修改 此方法不真正减少原始数组容量而是逻辑上认为已跳过指定索引处的数据项[^1]: ```c #include <stdio.h> #define SIZE 5 void removeElement(int arr[], int *size, int index) { if (index >= 0 && index < *size) { // 检查越界情况 for (int i = index; i < (*size)-1 ; ++i){ arr[i]=arr[i+1]; } --(*size); } else{ printf("Index out of range\n"); } } int main(){ int myArray[SIZE]={1,2,3,4,5}; int size=SIZE; printf("Original Array:\n"); for(int i=0;i<size;++i){ printf("%d ",myArray[i]); } removeElement(myArray,&size,2); printf("\nModified Array after removing element at position 2:\n"); for(int i=0;i<size;++i){ printf("%d ",myArray[i]); } return 0; } ``` 上述代码展示了如何定义`removeElement()`函数接收目标数组及其当前有效尺寸作为参数,并接受要移除元素的位置。如果该位置合法,则遍历剩余部分使每个元素向左平移一位从而覆盖掉选定下标的旧值;最后更新实际使用的数组长度变量减去1表示新状态下的规模变化。 #### 方法二:利用动态内存分配构建不含特定成员的新集合 当面对更复杂场景比如频繁增删操作时可以考虑借助标准库提供的malloc()系列接口先开辟一块适当空间再逐一遍历源列表挑选符合条件者填入其中形成最终版本[^2]: ```c #include <stdio.h> #include <stdlib.h> // 假设我们有一个整型数组以及它的大小 int* filterOutValue(const int source[], const unsigned int length, const int valueToRemove, unsigned int *newLengthPtr) { unsigned int count = 0; // 计算有多少个不是我们要删除的值 for(unsigned int i = 0; i < length; ++i){ if(source[i]!=valueToRemove){ ++count; } } // 创建新的数组 int *filteredArr=(int*)calloc(count,sizeof(int)); if(filteredArr==NULL){ fprintf(stderr,"Memory allocation failed.\n"); exit(EXIT_FAILURE); } // 将非匹配项复制到新数组里 unsigned int j=0; for(unsigned int i=0;i<length;++i){ if(source[i]!=valueToRemove){ filteredArr[j++]=source[i]; } } *newLengthPtr=count; return filteredArr; } int main(void){ const int originalData[]={89,-76,34,0,-76,56}; unsigned int dataLen=sizeof(originalData)/sizeof(originalData[0]); unsigned int resultSize=dataLen; int *result=filterOutValue(originalData,dataLen,-76,&resultSize); puts("After filtering:"); for(size_t k=0;k<resultSize;++k){ printf("%d ",result[k]); } free(result); return EXIT_SUCCESS; } ``` 这段示范说明了怎样编写辅助工具`filterOutValue()`以筛选出所有不同于给定数值之外的内容存放到另一片区域供调用方继续使用。注意这里采用了堆栈外额外申请的方式确保灵活性同时也增加了管理责任——记得适时释放不再需要的对象防止泄露资源浪费。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值