C语言小知识--结构体中的灵活数组问题


前言

众所周知,C语言有各种各样神奇的小知识,今天我又和学长学到了c语言的小知识c99的标准“将数组作为结构体的成员进行声明时元素个数可以不定义(只需要写[])”


一、这篇文章说的是什么(有基础的可以不看)

首先相信看这一段的人和作者一样,都是有点懵,属于接触c语言不算很长时间的人或者基础不算很扎实的人,因为在我们正常c语言定义数组是必须标明元素的个数的,就例如int a[5];类似于这样,当我们的结构体要用到数组要不就是确定数组大小,要不就是使用指针动态申请内存空间,最后释放。

但是在C99标准中新引入了“将数组作为结构体的成员进行声明时元素个数可以不定义(只需要写[])”,注意一定要是C99标准(C11标准是否兼容我就没有查了,有知道的可以评论区告诉博主一下),C89标准一定是不行的。
这到底是是个什么玩意呢?
他其实就是数组先不确定大小,然后再将来的程序中如果那里有问题可以直接添加而不用整个程序重写。下面先看看三种不同的定义方法吧,相信前两者大家都很熟悉了。

代码如下:

#define MAX 50;
struct apow {
	int a ;
	char b;
	struct pow *next;
 	int w[MAX];//开辟50个元素的数组 
};//直接确定数组大小

struct pows {
	int  a;
	char b;
	struct pows *next;
	int *w; 
};//动态开辟内存空间

struct pow {
	int a ;
	char b;
	struct pow *next;
 	int w[]; 
};//将数组作为结构体的成员进行声明时元素个数可以不定义(只需要写[])

它有什么用?

众所周知,我们在改良一种技术的时候,所出现的新技术必然有它的用武之地,如果没有那就当我没说吧。hhh
好了言归正传,它出现的意义是什么?为什么要有它?作者目前知道的作用暂时就一个(如果大家还知道什么作用欢迎评论区给博主普及一下)为了程序的维护和书写方便。为什么这么说呢?当我们如果使用一个确定大小的数组的时候,就有一个问题,数组开多大?如果这是我们提前知道的,那就很开心了。但是如果不知道呢?我们开的很大那会浪费我们宝贵的内存空间(如果大家是在计算机或许没有感觉,但是如果是单片机,特别是那种便宜的几KB的ram),如果开的小了,就会出现程序存不下不够跑了,完了,要重新写了,这样的情况。当然这里有人要说了,我可以使用动态分配内存空间啊,同样的道理首先你如果开辟的不够那么你就需要重新开辟,如果太大那也不行;而且动态分配内存空间比起数组要慢一些。所以,它的出现也算是帮助了我们,当我们不知道自己到底要使用多大数组时,我们就可以使用它了。或者当我们后期程序要维护的时候,出现数组不够的时候我们不要重新开辟,而是直接使用。它的使用方法和普通的数组使用方法基本一致,就是存储方式可能不太一样,下文我们会提到。
当然了,虽然它很好,但是也有它的缺点和陷阱。

二、使用结构体中的灵活数组的陷阱

这种数组的使用方法和普通结构体中的数组使用方法是一致的,这里就不多啰嗦了。重点是它的陷阱。
先给大家看一段代码。

代码如下:

#include <stdio.h>
struct pow {
	int a ;
	char b;
	struct pow *next;
 	int w[]; 
};
int main(){
	struct pow mypow;	printf("pow.a=%d,pow.b=%d,pow.next=%d,pow.w=%d,mypow=%d\n",sizeof(mypow.a),sizeof(mypow.b),sizeof(mypow.next),sizeof(mypow.w[0]),sizeof(mypow));
}

请添加图片描述
这是作者运行的结果(我用的是32位端,你如果用64位结果可能不同),在这里就有个问题,我的结构体大小为什么是12位,如果说结构体中的变量a,b,next按照字节对齐的计算方法计算结构体那么他应该就是12位,那么问题来了,我的w[]数组存在了那里?因为我的结构体只有12个字节大小。
起初我的推测是因为结构体是动态的,此时因为数组的大小是0所以我测的过程中结构体是12个字节的大小,因此我做了如下改进。

#include <stdio.h>
struct pow {
	int a ;
	char b;
	struct pow *next;
 	int w[]; 
};
struct pows {
	int  a;
	char b;
	struct pows *next;
	int *w; 
};

int main(){
	struct pow mypow;
	for(int i=0;i<9;i++){
		mypow.w[i]=i;
		printf("w[%d]=%d\n",i,mypow.w[i]);
	}
	printf("pow.a=%d,pow.b=%d,pow.next=%d,pow.w=%d,mypow=%d\n",sizeof(mypow.a),sizeof(mypow.b),sizeof(mypow.next),sizeof(mypow.w[0]),sizeof(mypow));
}

请添加图片描述
我此时完成了w[]数组的赋值,按照我的想法结构体应该不是12了,我运行之后发现还是12个字节大小,我就很疑惑,于是我请出了万能的学长,然后就发现自己犯了一个很蠢的错误,w[]数组开始的确和我想的一样因为是刚开始没有使用大小是0个所以测出来大小是12,但是当我使用后为什么出现了大小还是12的问题,在于sizeof是关键字,在程序跑之前就已经算好了是12字节装入,同时,由于我的结构体之中开始不包含w[]数组,因此w[]数组所占用的实际上是一段废内存,后面在使用时,虽然可以使用,但是还是一段废内存!!!
这个问题我明白了,我后面在思考w[]的地址是多少,在结构体当中是如何储存的,不出意外的话,我可以想明白,在我程序的测试中w[]数组的地址在结构体大小12个字节之后。好了又要给大家看一段代码

#include <stdio.h>
struct pow {
	int a ;
	char b;
	struct pow *next;
 	int w[]; 
};
struct pows {
	int  a;
	char b;
	struct pows *next;
	int *w; 
};

typedef struct pow pow_t;
typedef struct pows pows_t;
int main()
{
	pow_t mypow[2];
	pows_t mypows;
   /*  Write C code in this online editor and run it. */
   printf("%d %d %d %d\n",sizeof(mypow[0].a),sizeof(mypow[0].b),sizeof(mypow[0].next),sizeof(mypow[0].w[0]));
   printf("%d %d %d %d\n",sizeof(mypows.a),sizeof(mypows.b),sizeof(mypows.next),sizeof(mypows.w));
   printf("%d\n%d\n",sizeof(mypow[0]),sizeof(mypows));
   printf("%p %p %p %p %p\n",&mypow[0],&mypow[0].a,&mypow[0].b,&mypow[0].next,&mypow[0].w[0]);
	printf("%p %p %p %p %p\n",&mypow[1],&mypow[1].a,&mypow[1].b,&mypow[1].next,mypow[1].w);
   printf("%p %p %p %p %p\n",&mypows,&mypows.a,&mypows.b,&mypows.next,&mypows.w);
   return 0;
}

请添加图片描述
这是我的运行结果,很明显根据地址w[]跟在结构体的后面,但是又出现意外了,这里就是本文的重点,它的陷阱了,想必同学们看出来了,我的w[]数组的地址和结构体数组的下一个元素首地址重合了!!!!在这里就出现了问题,在我使用的过程中可能会跑到一半因为内存泄露而造成程序崩溃。很不明显一个错误。在这里分享了,希望各位以后使用的过程中不要出现。当然了学长给我讲,最好的解决方法就是,不会用就别用。哈哈哈哈

总结

在本文中作者主要出现了两个问题
1、sizeof不是函数,是关键字,在程序运行之前就完成了测算然后已经装入了。
2、使用“将数组作为结构体的成员进行声明时元素个数可以不定义(只需要写[])”时,需要注意内存泄露问题,这就是它最大的陷阱。
3、虽然可以使用,对于c语言是合法的,但是操作对于内存来说是不合法的。

切记,这段数组的地址对内存是非法的!虽然可以使用,一定要注意内存泄露。存在可能你快快乐乐用灵活数组,但是可能和你的变量或者如上的结构体冲突。(虽然可能很小但是还是有可能的)

hhh,作者是个小菜鸡,争取把自己犯的错,都记下来以后避免,当然在学习的过程中可能会有很多错误,欢迎大家评论区批评指出。
最后,感谢蒙面学长,hhh。

本文仅限于学习

  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值