C语言多维数组,结构体解析

首先讲解一个误区

int a[5] = {0};
printf("a : %d\n", a);
printf("&a : %d\n", &a);
printf("a+1 :%d\n", a + 1);
printf("&a+1 :%d\n", &a + 1);

我们都知道a代表数组的首地址,&a也是数组的地址,所以它们输出的结果是一样的,那么为什么a+1 和 &a+1的结果不一样呢?

因为a代表的是数组的首地址,也就是a[0]的地址,一个Int *类型的指针指向a[0] 

&a代表是数组的地址,也就是a[5] 这个数组的地址,所以这个是 int (*)[5] 类型的指针,也就是数组指针

指针 +1的意思是指移动指向的类型大小的位置,如果是char 指针,每加1就移动了一个字节的位置,

int指针每加1就移动了4个字节的位置,这就解释了为什么&a+1 是移动了20个字节,而a+1移动了4个字节



可以使用指针来代替数组

int *a = (int *)malloc(sizeof(int) * 5);
int a[5];

这两个的效果是一样的



接下来说一下多维数组


我们类似于上面同样输出了二维数组的名字,并且加1,结果是差了8个字节,正好是第二维数组的大小

那么我们是不是可以认为,二维数组其实就是 多个数组指针的集合呢

这样我们就可以自己造一个二维数组

int main()
{
	int(*p)[2];
	p = (int (*)[2])malloc(sizeof(int) * 6);
	for (int i = 0; i < 3 ; i++)
	{
		for(int j = 0; j < 2; j++)
		{
			p[i][j] = i + j;
		}
	}
	
	for(int i = 0; i < 3; i++)
	{
		for(int j = 0; j < 2; j++)
		{
			printf("%d\n", p[i][j]);
		}
	}
	free(p);
    return 0;
}

现在我们可以下结论了  多维数组名的本质是一个数组指针



结构体

首先讨论一下结构体的内存分配问题,还是一个例子引出


一个结构体里面定义了int 和char  两个字段,但是输出的大小确是8

在看一个例子


还是8, 这个问题就值得探讨了

我来画个示意图

结构体会有一个内存对齐的特性,内存分配按块进行分配,块的大小的里面最大的内存

也就是说这个结构体最大的内存是int 4个字节,所以后面分配的内存都是按照四个字节来分配,但是如果前面的内存没有使用完,会优先使用前面的内存,也就造成了这个现象,当插入到第五个char的时候第二块内存不够会继续分配四个字节的内存

测试结果也是如此

知道了这个特性,就可以讨论不规则顺序存储了

struct Person
{
	int a;
	char c;
	short s;
};

当结构体是这种情况的时候,内存是这样分配的

系统会按照当前类型所占的内存来均匀分配,所以并不是放在c后面的两块空间,而是最后的两块空间


可以从各个数据的地址看出,确实是这样 a和c 差了四个字节  c和s差了两个字节

所以我们定义结构体的时候,要注意字段定义的顺序,顺序不同,可能内存的大小也会不同

同样的数据,只是顺序不同,空间大了四个字节


下面讨论一下一个结构体中引用另外一个结构体

struct Person
{
	//Teacher t;
	int a1;
	short s;
	char c;
	int a;
};

这个结构体的大小是 12个字节

struct Teacher
{
	double age;
};

这个结构体的大小是 8个字节

struct Person
{
	Teacher t;
	int a1;
	short s;
	char c;
	int a;
};

那这样如果正常来想,肯定会是两个结构体的内存大小相加 也就是20个字节

但结果却是24个字节

现实总是不如意,我们如果把Teacher t 换成 double age;  按照上面的计算方式来算一下,正好是24个字节

so..如果遇到这种结构体嵌入,可以直接替换成原结构体的字段,不过是顺序不能变罢了

我们可以根据类型来调整字段定义的顺序,以确保能最大限度是使用内存


如果在内存非常紧张的地方,想使用那些被“浪费“掉的内存也是可以的

struct Person
{
	int a1;	
	char c;
	short s;
};

这种情况,c后面会有一个字节的内存没有用到

int main()
{
	Person person;
	person.a1 = 1;
	person.c = 2;
	person.s = 3;
	char *p = (char *)&person;
	p = p + 5;
	*p = 4;


	printf("p.a1 : 地址是%d  值是%d \n",&person.a1, person.a1);
	printf("p.c : 地址是%d  值是%d \n", &person.c, person.c);
	printf("p.空余的内存 : 地址是%d 值为:%d\n", p, *p);
	printf("p.s : 地址是%d  值是%d\n", &person.s,person.s);
    return 0;
}

看,我们把浪费的地址又给用上了。

如果上面的代码看不懂,推荐看一下我之前的博客C语言 巧用指针类型不匹配


结构体的位字段,这个在嵌入式系统用的比较多,将内存计算到位的阶层,不会浪费一点内存,, 真是可怕



当结构体中有指针的时候要注意 深拷贝浅拷贝的问题

这个改天再写一个深拷贝浅拷贝的探讨


结构体指针的高级应用


主要就是利用地址的偏移量加上强转,来获取结构体里面的内容,这个我暂时还没有使用过,不过可以加深对c语言地址和指针的理解,适当了解一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值