首先讲解一个误区
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语言地址和指针的理解,适当了解一下