typedef struct S { char c[20]; struct S* next; }S; #include <stdio.h> int main() { S s1; return 0; }
typedef是一个很实用的标识符,能够将类型重命名,好比编译器就将,unsigned int 重命名为size_t,在这里也是一样的将 struct S重命名为S,使用的时候更加方便。我本人是比较喜欢在结构体中使用 typedef 的。ide
struct S { char c[20]; struct S* next; }; #include <stdio.h> int main() { return 0; }
结构体里面是不能直接出现或者引用自身的,若是出现就会进入无限的自引用,可是能够出现指向自身的指针,直到指针溢出指针就无效了,也不会再引用。优化
#pragma pack(4) //设置默认对齐数为4 //MSVC的默认对齐数为8 typedef struct S1 { char a; int c; char b; }S1; //typedef struct S2 //{ // char a; // char b; // int c; //}S2; //typedef struct S3 //{ // int c; // char a; // char b; //}S3; #pragma pack() //取消设置的默认对齐数 #include <stdio.h> #include <stddef.h> int main() { S1 n1 = { 0 }; printf("%d\n", sizeof(n1)); //S2 n2 = { 0 }; //printf("%d\n", sizeof(n2)); //S3 n3 = { 0 }; //printf("%d\n", sizeof(n3)); printf("%d\n", offsetof(S1, a)); printf("%d\n", offsetof(S1, b)); printf("%d\n", offsetof(S1, c)); return 0; }
内存对齐的现象在各个编译器中都会存在,可是没有统一的标准,内存对齐,指的是在结构体内部,对内部的元素进行内存分配时存在的一种规律,之内存对齐数为标准,MSVC中默认的内存对齐数为8,成员类型的对齐数为自己字节数,好比int的对齐数为4,double的对齐数为8,对齐数是默认对齐数与成员对齐数的较小值。
以第一个元素存储开始的内存位置为0,下一个成员开始的位置的须要是相对于第一个成员内存开始的位置的对齐数的整数倍,好比,显存进去一个char 占一个字节,那么以这个字节为开始,后面要继续存进去一个int (四个字节)的话,就须要在下一个是4的整数倍的位置开始,也就是说不能从该char以后的第1,2,3个位置存,得从第4个开始存,因此可以明显的知道,若是先定义char 再定义int 那么这两个成员在结构体中占据了8个字节,若是先定义int,在定义char,那么这两个成员只会占5个字节
#pragma pack()是能够改默认对齐数使,将要改的数字放在括号里,用完以后要在后面再加一句#pragma pack(),使得默认对齐数恢复默认。指针
//位段 - 位指的是二进制位 #include <stdio.h> struct A { int _a : 2;//只要占2个比特位 int _b : 5;//只要占5个比特位 int _c : 10;//只要占10个比特位 int _d : 30;//只要占30个比特位 }; //注重移植的程序不该该使用位段 //int 位段被当成有符号数仍是无符号数是不肯定的 //位段中的最大位数是不能肯定的 //位段中的从左向右分配仍是从右向左分配还没有定义 //当一个结构体包含两个位段,第二个位段成员较大时,没法容纳第一个位段剩余位时,是舍弃剩余位仍是利用,这是不肯定的 //位段的数字不能大于32 int main() { struct A s; printf("%d\n", sizeof(s));//8个字节 return 0; } typedef struct S { char a : 3; char b : 4; char c : 5; char d : 4; }S; int main() { S s = { 0 }; s.a = 10; s.b = 20; s.c = 3; s.d = 4; return 0; }
位段也是一种自定义类型,不肯定性较多,最重要的是不能移植,可是优势是可以优化内存管理。调试
enum Color { RED, GREEN, BULE }; #include <stdio.h> int main() { enum Color c = RED; return 0; }
若不赋初值,则自动进行了赋初值,对应的是RED = 0, GREEN = 1, BULE = 2也能够进行手动赋初值
使用方法结构体相似,可是定义出来的变量的值只能取枚举内部的值
优势
1.增长了代码的可读性
2.和define定义的标识符比枚举有类型检查,更加严谨code
#include <stdio.h> typedef union Un { char c;//1个字节 int i;//4个字节 }Un; int main() { Un u; printf("%d\n", sizeof(u)); printf("%p\n", &u); printf("%p\n", &(u.c)); printf("%p\n", &(u.i)); //i和c不能同时使用 return 0; }
联合体也是一种自定义类型,意思是,内部的成员使用了同一块空间,联合体所占的空间大小是由内部较大的成员决定的,当最大成员不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍,可是两个成员不能同时使用,由于,同一块空间不能存得下两个元素。内存
#include <stdio.h> int check_sys() { union U { char c; int i; }u; u.i = 1; return u.c; //int a = 1; ////返回1 表示小端 //return *(char*)&a; } int main() { int ret = check_sys(); if (1 == ret) { printf("小端字节序\n\a"); } else { printf("大端字节序\n"); } //高高低低 小端字节序 //高低低高 大端字节序 return 0; }
对于大端字节序和小端字节序,我有点记不住,就发明了个简短的总结,高高低低,意思是数字的高位存储在内存的高位,数字的低位存储在内存的低位,叫作小端字节序,高低低高,就是数字的高位存储在内存的低位,数字的低位存储在内存的高位,叫作大端字节序。编译器
当初是写了一个a=1来进行读取第一个字节的数字判断大小端,如今用联合体,由于公用同一块空间,因此在空间内部存进去数字以后,不一样的类型读取的位数不一样,可是读取的顺序都是由低地址到高地址,因此经过char访问由int存进去的数字,就可以判断大小端了。it