C语言中有不少的基础数据类型,除了这些咱们能够经过一个结构体来把一部分基础数据类型整合为一个新的自定义类型。node
struct 结构体的标签 { 成员1 ; 成员2 ; ..... }; // 最后用一个分号来表示结束
结构体的标签 : 用来区分各个不一样类型的结构体 (也能够省略)。
成 员: 指的是该结构体内部的数据,能够是任何数据类型。也但是结构体数组
struct node { int a ; char c ; double d ; }; int main(int argc, char const *argv[]) { //定义结构体 --> 分配内存空间 struct node a ; }
注意:
1.只有结构体被定义时才会被分配内存空间,结构体声明并不会分配内存空间;
2.定义结构体的步骤:
a.分配一片内存名字叫 a
b.该内存中须要存放一个结构体
c.分被存放的是 int a , char c ,double d
字体
结构体与普通的变量都是同样的,都设计涉及,初始化,赋值,取值,传值等等操做。因为结构体中的成员类型各有不一样所以有两种初始化获得方法:
1.普通初始化
写起来方便,可是改起来一点都不方便。
2.指定成员初始化
写起来虽然累一点点, 可是若是结构体在后期有更新迭代,不会有初始化的效果。ui
//1.普通初始换 struct node c ={ 100 , 'M' , 3.1456676 } ; //2.指定成员初始化 struct node d = { .a = 998, // . 称为成员引用符 .c = 'K', .d =2345.21345 };
注意:
1.指定成员初始化:
a.成员的次序或数量的变化不会致使初始化有逻辑问题;
b.能够初始化一部分红员;
c.若是结构体新增成员,该初始化语句依然可用。操作系统
因为结构体内部包含的成员不止一个,因此说须要使用 . 结构体成员的引用符来访问其中指定的成员。设计
//结构体变量.结构体成员 printf("a:%d \t c:%c \t d:%lf\n", d.a, d.c , d.d ); d.a = 123 ; d.c = 'M'; d.d = 3.14; printf("a:%d \t c:%c \t d:%lf\n", d.a, d.c , d.d );
结构体数组与其余的普通变量的数组并无差异,只不过他的数组内容为一个一个的结构体。指针
struct node arr [10]; arr[0].a = 200; arr[0].c = 'N'; arr[0].d = 889.43;
结构体指针
结构体指针与其余指针没有差异,只不过其指向的是一个结构体。code
struct node * p ; p = &d ; printf("p->a:%d \t p->c:%c \t p->d:%lf\n", p->a, p->c , p->d );
注意:
1.普通的结构体成员引用使用 .;
2.指针的结构体成员应用使用 ->。blog
struct node { int a ; char c ; double d ; }; //变形 1 struct node { int a ; char c ; double d; }Even, GEC; // 在声明结构体的时候顺便定义两个结构体变量 Even.a = 100 ; Even.c = 'C'; Even.d = 12312.234; printf("%d\t%c\t%lf\n" , Even.a , Even.c , Even.d);
//变形 2 struct {// 结构体的标签能够省略 ,可是省略的话之后无法定义更多的此类型结构体 int a ; char c ; double d; }Jacy; // 在声明结构体的时候顺便定义一个结构体变量 Jacy.a = 200 ; Jacy.c = 'A'; Jacy.d = 123.2; printf("%d\t%c\t%lf\n" , Jacy.a , Jacy.c , Jacy.d); //示例 struct CuiHua { int a ; char c ; double d ; struct { char * p ; char arr[32]; }Jacy; // 在声明结构体的时候顺便定义个结构体变量 }Even,GEC;// 在声明结构体的时候顺便定义两个结构体变量 struct CuiHua hua = { .a = 1024 , .c = 'B' , .d = 123.123, .Jacy.p = "Hello",//p指向的是数据段中的 Hello 的地址 .Jacy.arr = "GZ2069" // 数组arr 属于栈空间, 它内部存放了"GZ2069" }; printf("a:%d\t c:%c\t d:%lf\t p:%s\t arr:%s\n", hua.a,hua.c,hua.d,hua.Jacy.p, hua.Jacy.arr); hua.Jacy.p=malloc(32);//申请一篇堆空间大小为32字节让P指向hello strncpy(hua.Jacy.p , "hello" , 32 ); hua.Jacy.arr[0] = 'g'; hua.Jacy.arr[1] = 'z'; printf("a:%d\t c:%c\t d:%lf\t p:%s\t arr:%s\n", hua.a,hua.c,hua.d,hua.Jacy.p,hua.Jacy.arr);
// 变形 3 [ 推荐使用 ] typedef struct CuiHua { int num ; char *name ; char class[32]; }stu_info,*stu_info_p; //使用typedef 给结构体取别名 // stu_info 等同于 struct CuiHua //stu_info_p 等同于 struct CuiHua * int main(int argc, char const *argv[]) { stu_info a ; // 定义一个普通的结构体变量 a.num = 100 ; a.name = calloc(1 , 32 ); strncpy(a.name, "张全蛋",32); //注意拷贝以前必须分配空间给a.name。 strncpy(a.class, "GZ206666",32); printf("Num:%d\tName:%s\tClass:%s\n" ,a.num,a.name,a.class); stu_info_p c=calloc(1,sizeof(stu_info));//定义一个结构体指针变量 (*c).num = 200 ; c->name = calloc(1,32); strncpy( c->name, "张半蛋" , 32); strncpy(c->class, "GZ206667" , 32); printf("Num:%d\tName:%s\tClass:%s\n",c->num,c->name,c->class); return 0; }
指CPU一次性从内存中获取的数据大小。32位cpu一次性处理4字节2进制数,64位计算机一次性处理8字节2进制数。排序
CPU字长肯定以后就能够肯定咱们的CPU每一次在读、写内存的时候都是肯定大小 ,假设是32位系统,那么每一次读、写内存时 ,都是按照4字节进行操做。
若是是没有对齐的状况下,须要读取8字节则最多须要读取3次才能够把全部数据读取完整。若是是对齐的状况下只须要读取两次。
因此,若是地址没有对齐CPU在操做时须要浪费更多的时间,能够牺牲内存来提升效率。但也能够牺牲效率来提升内存。
变量的地址值,能被M值整除。
char c ; // 大小 1 字节 , M值 1 short s ; // 大小 2 字节 , M值 2 int i ; // 大小 4字节 , M值 4 double d ;// 大小 4字节 , M值 4 printf("a:%p\n" , &a ); printf("s:%p\n" , &s ); printf("i:%p\n" , &i ); printf("d:%p\n" , &d ); ```
注意:
1.若是变量的尺寸小于 4 字节,那么该变量的 m 值等于变量的长度;
2.若是变量的尺寸大于等于 4 字节,则一概按 4 字节对齐;
3.若是变量的 m 值被人为调整过,则以调整后的 m 值为准。
char c __attribute__((aligned(4))) ; // 手动干预通常只能往大的设置不能够设置更小
注意:
1.__attribute__是GNU特定的语法,属于C语言的拓展
2.__attribute__写法注意 左右两边都是两个下划线右边有两组括号 (( ))
3.((aligned(4)))一个变量M值只能被提升不能够下降
结构体的M值取决于结构体中最大成员的M值。结构体的对长字节取决于,结构体中最长字节和系统字长中最短的那个。
typedef struct CuiHua { int num ; // 4 char c1 ; // 1 char *name ; // 8 char c2 ; // 1 char c3 ; // 1 double d ; // 4 char c4 ; // 1 }stu,* p_stu; int main(int argc, char const *argv[]){ stu a ; printf("&a:%p\n" , &a); printf("&a.num:%p\n" , &a.num); printf("&a.name:%p\n" , &a.name); printf("&a.c1:%p\n" , &a.c1); printf("&a.c2:%p\n" , &a.c2); printf("&a.c3:%p\n" , &a.c3); printf("&a.c4:%p\n" , &a.c4); printf("&a.d:%p\n" , &a.d); printf("sizeof(a):%ld\n",sizeof(a)); //输出40 return 0; }
如上图所示,在64位系统下结构体a占40个大小,结构体最大M为8,字体字长也为8,因此结构体M为8。安装结构体中变量定义的方式排序,
每一个变量的M值,必须能被变量的起始地址整除,因此获得如上图所示的存储方式,每种颜色表明一个变量。
一样一份代码在不一样的操做系统下(系统位数),须要考虑可移植性的问题,如数据尺寸的变化,存储位置的变换
//方法1 统一压实结构体成员: struct CuiHua { int num ; // 4 char c1 ; // 1 char *name ; // 8 char c2 ; // 1 char c3 ; // 1 double d ; // 4 char c4 ; // 1 }__attribute__((packed)); //把结构体进行压实,不考虑按照顺序排列了,能多挤就多挤,固然变量的起始地址仍是会被M整除,该结构体大小为24 struct CuiHua { int num __attribute__((aligned(4))); // 4 char c1 __attribute__((aligned(1))) ; // 1 char *name __attribute__((aligned(8))); // 4 char c2 __attribute__((aligned(1))); // 1 char c3 __attribute__((aligned(1))); // 1 double d __attribute__((aligned(8))); // 4 }