复习树的时候发现结构体实现并不流畅熟练,先搞定一下结构体。之后搞定树,二叉树,图等
目录
一、 几种结构体定义:
1.先是定义不带初始化的两个
(1)struct crunode 在定义的时候就定义了其类型的crunode1变量 ,之后仍可以在其他函数中定义其他变量如二、1.
(2)struct b 则没有,调用时需要再定义struct b b1; //b1为变量
struct crunode
{
struct crunode *parent;
int data;
char name[20];
struct crunode *leftchild;
struct crunode *rightchild;
}crunode1;
//in this define you can't determine the parameter.if you want to determine those you can determin at the position "a" like the next one .
// crunode is a type ,crunode1 is a parameter
struct b //determine 2
{
char* p;
int data;
};
2.然后是两个定义时就进行初始化的两个结构体
struct a是简单的成员分别为一个char指针和一个int数的结构体,在结构体后面进行初始化
struct c是直接初始化了一个结构体数组分别为c[0],c[1],然后其中成员包括char型数组,char型指针,int型数还有一个结构体(这里注意不能再次定义自己例如这里将struct c中的struct b b2; 换为struct c c2,;会造成死循环)。在结构体后面进行初始化要注意其中嵌套的结构体初始化内容需要用{ }进行表明,即每个机构体的内容都需要用{ }来进行标注,表明其是一个机构体。
struct a //determine 1
{
char* p;
int data;
}a1 = {"test",99}; //init 1
二、机构体初始化(在一、的基础上)
1.对机构体其中几个成员做初始化或修改
crunode1 的内存空间已经分配所以可以随意一些。
crunode2 未定义过也就没有进行过内存空间分配,也可以先定义变量然后再像crunode1一样进行初始化。
crunode1 =(struct crunode){.data = 13}; //init 4//init one of the parameter of the structure.
//equal to crunode1.data = 13;
struct crunode crunode2 = {.data = 14,.name = "crunode2"};//init 4.1 //init some members of the structure
2.定义结构体,分配了内存空间,之后对每个成员进行赋值(注意数组成员赋值方法)
struct d d1; //determine 2.1
//d1.name = {'D'}; //array can't do this. int just can be modified in turn with circulation,char can be modfied inturn with circulation and use string.h()strcmp(name,string);
strcmp(d1.name,"dudezheng"); //modify 3
d1.grade = 100;
d1.str = "aliyun";
三、结构体定以后的内容修改(包括输入)
1.进行整体修改
(struct c)可以理解强制类型转换,即向编译器表明{ }内内容为struct c类型的
c[1]=(struct c){"Dudezheng","c3",3,{"b3",33}}; //modify 2
2.进行某些或某个成员进行修改
crunode1 =(struct crunode){.data = 13}; //init 4//init one of the parameter of the structure.
//equal to crunode1.data = 13;
struct crunode crunode2 = {.data = 14,.name = "crunode2"};//init 4.1 //init some members of the structure
3.进行输入
第二个问题比较好懂,基本上是研究结构体的存储结构体就行了$$$$$
//scanf("%s%s%d",&c[1].name,&c[1].q,&c[1].num); //false
//scanf("%s,%s,%d,%s,%d",&c[1]); //false the address of the structure main for the parameter of the function and transmit the address of the member of the structure like: &c[1].name
对于第一个语句,不知道哪里有问题,先分别对三个成员分别进行赋值,发现第二个成员赋值出现问题(过程中发现c[1].name前面的&可以省略,因为name本来就是一个指针)
测试代码:
#include<stdio.h>
#include<string.h>
struct b //determine 2
{
char* p;
int data;
};
struct c //determine 3
{
char name[20];
char* q;
int num;
struct b b2;
}c[2] ={{"DU","c1",1,{"b1",11}},{"CH","c2",2,{"b2",22}}};//init 2,3
int main()
{
printf("before changing:%s\n",c[1].q);
scanf("%s",c[1].q) ;
printf("after changing:%s\n",c[1].q);
return 0;
}
出现的问题:
解决办法:c[1].q是一个指针不可以直接用scanf()进行输入,而可以用一个字符数组来接受输入,然后再将c[1].q指向这个字符数组。而printf()的时候,因为c[1].q是一个字符串的初始地址,所以对它进行输出的时候就将字符串输出出来了。实现代码如下:
char array[20];
scanf("%s",array);
c[1].q = array;
三、结构体的调用及输出
1. 整体,部分成员输出
void print(struct c test)//output1
{
printf("%s,%s,%d,%s,%d\n",test.name,test.q,test.num,test.b2.p,test.b2.data);
//printf("%s,%s,%d,%s,%d\n",test); //false-------c[1].q不能这么输入,其他的可以
//printf("%s,%s,%d,%s,%d\n",c[1]); //false
}
2.结构体嵌套输出
printf("%d\n",crunode1.data); //output 2
printf("%d\n",c[1].b2.data); //output 3
四、结构体在内存中的存储方式
取自:结构体在内存中的存储方式
结构体对齐原因有很大部分是因为计算机扫描的内存单元个数,也就是数据总线的大小。
原则1:
数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2:
结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3:
收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
例1:struct{
short a1;
short a2;
short a3;
}A;
struct{
long a1;
short a2;
}B;
sizeof(A) = 6; 这个很好理解,三个short都为2。
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
例2:struct A{
int a;
char b;
short c;
};
struct B{
char b;
int a;
short c;
};
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
深究一下,为什么是这样,我们可以看看内存里的布局情况。
a b c
A的内存布局:1111, 1*, 11
b a c
B的内存布局:1***, 1111, 11**
其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
再看一个结构中含有结构成员的例子:
例3:struct A{
int a;
double b;
float c;
};
struct B{
char e[2];
int f;
double g;
short h;
struct A i;
};
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
sizeof(B) = 48; 看看B的内存布局。
e f g h i
B的内存布局:11* *, 1111, 11111111, 11 * * * * * *,1111* * * *, 11111111, 1111 * * * *
省略宏与结构体中含位域字段两种情况说明,祥见原文。
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体 系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据 的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
1.理论存储练习:
struct b //determine 2
{
char* p;
int data;
}stu;
struct c //determine 3
{
char name[20];
char* q;
int num;
struct b b2;
}c[2] ={{"DU","c1",1,{"b1",11}},{"CH","c2",2,{"b2",22}}};//init 2,3
sizeof(stu)= 8 +4**** = 16;
sizeof(c[1])= 20**** + 8+4**** +8 +4 + ******** = 60 //char name [20]大小为20,则先填进去20字节;char*在64位系统中跟CPU字长一样所以是8字节,由原则一,需要从8的倍数开始,则补充4字节,然后填入8;int 在64位系统中为4字节,符合原则一则直接填入4字节;来到第二个结构体 其组成为char* 和int,由原则二,起开始需要为其最大成员的倍数,则为8字节的倍数,补充4字节(****),写入char* 8字节;int data符合原则一,直接填入4字节;然后整个结构体中最大成员为20字节,由原则三,其所占用内存空间为20字节的倍数,所以补足********,最后结果为60字节
在C语言中指针是一个unsigned 类型的无符号数,其所占内存字节一般是和计算机的CPU字长是一致的,拿32位计算机来说,指针所占的内存空间一般为4个字节,不管char *还是int *抑或是float *,都是4个字节。
程序员可以使用sizeof运算符获取数据类型或者某个变量的内存所占字节数。比如
printf("%d\t%d\t%d\n", sizeof(char *), sizeof(int *), sizeof(float *));
2.测试代码:
#include <stdio.h>
#ifdef _DEBUG
#pragma pack(4)
struct test
{
char x[13]; // 13
int d; // 4
double f; // 8
}ss;
#else
#pragma pack(8)
struct test
{
char x[13]; // 13
int d; // 4
double f; // 8
}ss;
#endif
int main(void){
printf("%d\n", sizeof(ss));
return 0;
}
代码
//some methods to determine the structure and some methods to init the structures and some to modify the structure
#include<stdio.h>
#include<string.h>
struct crunode
{
struct crunode *parent;
int data;
char name[20];
struct crunode *leftchild;
struct crunode *rightchild;
}crunode1;
//in this define you can't determine the parameter.if you want to determine those you can determin at the position "a" like the next one .
// crunode is a type ,crunode1 is a parameter
struct a //determine 1
{
char* p;
int data;
}a1 = {"test",99}; //init 1
struct b //determine 2
{
char* p;
int data;
};
struct c //determine 3
{
char name[20];
char* q;
int num;
struct b b2;
}c[2] ={{"DU","c1",1,{"b1",11}},{"CH","c2",2,{"b2",22}}};//init 2,3
struct d //determine 2.1------deal with array problem
{
char name[20];
int grade;
char *str;
};
void print(struct c test)//output1
{
printf("%s,%s,%d,%s,%d\n",test.name,test.q,test.num,test.b2.p,test.b2.data);
//printf("%s,%s,%d,%s,%d\n",test); //false
}
int main()
{
//struct crunode crunode;
crunode1.data = 99; //modify 1
printf("%d\n",crunode1.data); //output 2
crunode1 =(struct crunode){.data = 13}; //init 4//init one of the parameter of the structure.
struct crunode crunode2 = {.data = 14,.name = "crunode2"};//init 4.1 //init some members of the structure
printf("%d\n",c[1].b2.data); //output 3
c[1]=(struct c){"Dudezheng","c3",3,{"b3",33}}; //modify 2
printf("After change:");
printf("%d\n",c[1].b2.data); //output 3
//scanf("%s%s%d",&c[1].name,&c[1].q,&c[1].num);
//scanf("%s,%s,%d,%s,%d",&c[1]); //false the address of the structure main for the parameter of the function and transmit the address of the member of the structure like: &c[1].name
struct d d1; //determine 2.1
//d1.name = {'D'}; //array can't do this. int just can be modified in turn with circulation,char can be modfied inturn with circulation and use string.h()strcmp(name,string);
strcmp(d1.name,"dudezheng"); //modify 3
d1.grade = 100;
d1.str = "aliyun";
print(c[1]); //output 1
//printf("%s,%s,%d,%s,%d\n",c[1]); //false
return 0;
}