
1.C语言中字符串的本质指针指向头、固定尾部的地址相连的一段内存。C语言中字符串有3个核心要点第一是用一个指针指向字符串头第二是固定尾部字符串总是以\0来结尾第三是组成字符串的各字符彼此地址相连。2.char *p linux;在这段代码中p本质上是一个字符指针占4字节linux分配在只读常量区占6个字节实际上总共耗费了10个字节。sizeof测多少字节。strlen测字符串长度不算\0注意只能传递合法的字符串才有意义3.字符数组与字符串的本质差异内存分配角度(1)字符数组char a[] linux;来说定义了一个数组a数组a占6字节右值linux本身只存在于编译器中编译器将它用来初始化字符数组a后丢弃掉也就是说内存中是没有linux这个字符串的这句就相当于是char a[] {l, i, n, u, x, \0};(2)字符串char *p linux;定义了一个字符指针pp占4字节同时还定义了一个字符串linux分配只读常量区然后把常量区中的字符串一共占6字节的首地址也就是l的地址赋值给p。总结对比字符数组和字符串有本质差别。字符数组本身是数组数组自身自带内存空间可以用来存东西所以数组类似于容器而字符串本身是指针本身永远只占4字节而且这4个字节还不能用来存有效数据所以只能把有效数据存到别的地方然后把地址存在p中。也就是说字符数组自己存那些字符字符串一定需要额外的内存来存那些字符字符串本身只存真正的那些字符所在的内存空间的首地址。4.结构体为何要对齐访问结构体中元素对齐访问主要原因是为了配合硬件也就是说硬件本身有物理上的限制如果对齐排布和访问会提高效率对结构体取地址是一字节类型的地址。错误理解 地址1加的是一个地址类型5.内存对齐关于结构体内存假设是4字节对齐1.想象一个以4字节为宽度的的矩形2.一个 填充 的概念3.注意每一个元素都有自己的对齐规则判断填充所以要看后面的类型后面是前面的整数倍如short(2字节只能放在类似024这样的偶地址处4.整个结构体是最宽元素的整数倍例子struct mystruct //一共8字节{int a; //4char b; //11short c; //2}内存分配如图gcc支持但不推荐的对齐指令#pragma pack() #pragma pack(n) (n1/2/4/8)#pragma是用来指挥编译器常用对齐命令1. #pragma pack()这种就是设置编译器1字节对齐 2. #pragma pack(4)这个括号中的数字就表示我们希望多少字节对齐。gcc推荐的对齐指令__attribute__((packed)) __attribute__((aligned(n)))__attribute__((packed))使用时直接放在要进行内存对齐的类型定义的后面然后它起作用的范围只有加了这个东西的这一个类型。packed的作用就是取消对齐访问。__attribute__((aligned(n)))使用时直接放在要进行内存对齐的类型定义的后面然后它起作用的范围只有加了这个东西的这一个类型。它的作用是让整个结构体变量整体进行n字节对齐注意是结构体变量整体n字节对齐而不是结构体内各元素也要n字节对齐例子struct mystruct{int a;char b;short c;} __attribute__((packed)); //取消对齐访问// 那就是 412 7字节typedef struct mystruct111{int a;char b;short c;short d;}__attribute__((aligned(1024))) My111;// 4 1 1(填充) 2 2 10字节但是要填充1014字节offsetof宏的原理#define offsetof(TYPE,MEMBER) ( (int) ((TPYE *)0)-MEMBER ) //返回的是member元素相对整个结构体变量的首地址的偏移量int类型例int offsetof_a offsetof(struct stu, a)(TYPE *)0 这是一个强制类型转换把0地址强制类型转换成一个指针这个指针指向一个TYPE类型的结构体变量。 实际上这个结构体变量可能不存在但是只要我不去解引用这个指针就不会出错。((TYPE *)0)-MEMBER (TYPE *)0是一个TYPE类型结构体变量的指针通过指针指针来访问这个结构体变量的member元素((TYPE *)0)-MEMBER 等效于( ((TYPE *)0)-MEMBER )意义就是得到member元素的地址。container_of宏#define container_of(ptr, type, member ) ( { \const tpyeof(((type *) 0) -member) * __mptr (prt); \( type * ) ((char * ) __mptr - offsetof(type , member )) ; } ) //ptr 是指向结构体元素member的指针作用知道一个结构体中某个元素的指针反推这个结构体变量的指针 宏返回的是整个结构体变量的指针由此可以得到其他结构体的指针例子char *ptr str.ch;struct struct *p container_of(ptr, struct srt, ch);typeof(a) 由变量a得到a的类型const typeof( ((type *) 0) -member ) * __mptr (prt); //得到元素类型的指针例const char* __mptr prt ;( type * ) ((char * ) __mptr - offsetof(type , member )) ; //先用typeof得到member元素的类型定义成一个指针然后用这个指针减去该元素相对于整个结构体变量的偏移量偏移量用offsetof宏得到的注意转成char *)减去之后得到的就是整个结构体变量的首地址了再把这个地址强制类型转换为type *即可! 没问题共用体union共用体union就是对同一块内存中存储的二进制的不同的理解方式。union的sizeof测到的大小是union中各个元素里面占用内存最大的那个元素所占字节的倍数大端模式big endian和小端模式little endian小端模式是指数据的高字节保存在内存的高地址中而数据的低字节保存在内存的低地址中。ARM是小端模式。2种测试方法//通过指针int test_endian1(void){int i 1;char c *((char *)(i));return (1 c);}//通过unionunion myUnion{int i;char c;};int test_endian2(void){union myUnion u1;u1.i 1;return (u1.c 1);}思考几种看似可行其实不行的方式 原因都是这个运算是高于内存层次的// 位与int a 1;int b a 0xff;printf(b %d.\n, b);理论分析位与运算是编译器提供的运算这个运算是高于内存层次的或者说运算在二进制层次具有可移植性也就是说的时候一定是高字节高字节低字节低字节和二进制存储无关。// 移位int a, b;a 1;b a 1;printf(b %d.\n, b);理论分析原因和运算符不能测试一样因为C语言对运算符的级别是高于二进制层次的。右移运算永远是将低字节移除而和二进制存储时这个低字节在高位还是低位无关的。// 强制类型转换int a;char b;a 1;b (char)a;printf(b %d.\n, b);分析同上枚举默认从0开始初始直接赋值。宏定义与枚举区别(1)枚举是将多个有关联的符号封装在一个枚举中而宏定义是完全散的。也就是说枚举其实是多选一。(2)什么情况下用枚举当我们要定义的常量是一个有限集合时譬如一星期有7天譬如一个月有31天譬如一年有12个月····最适合用枚举。其实宏定义也行但是枚举更好(3)不能用枚举的情况下定义的常量符号之间无关联或者无限的用宏定义。总结宏定义先出现用来解决符号常量的问题后来人们发现有时候定义的符号常量彼此之间有关联多选一的关系用宏定义来做虽然可以但是不贴切于是乎发明了枚举来解决这种情况。enumeration 类型定义// 定义方法1,定义类型和定义变量分离开 enum week { SUN, // SUN 0 MON, // MON 1; TUE, WEN, THU, FRI, SAT, }; enum week today; */// 定义方法2,定义类型的同时定义变量 enum week { SUN, // SUN 0 MON, // MON 1; TUE, WEN, THU, FRI, SAT, }today,yesterday; */// 定义方法3,用typedef定义枚举类型别名并在后面使用别名进行变量定义 typedef enum week { SUN, // SUN 0 MON, // MON 1; TUE, WEN, THU, FRI, SAT, }week; */错误类型举例// 错误1枚举成员重名编译时报错redeclaration of enumerator ‘MON’ typedef enum workday { MON, TUE, WEN, THU, FRI, }workday; typedef enum weekend { MON, SAT, SUN, }weekend;// 结构体中元素可以重名 typedef struct { int a; char b; }st1; typedef struct { int a; char b; }st2;解释 不能有重名的枚举成员。 分析经过测试两个struct类型内的成员名称可以重名而两个enum类型中的成员不可以重名。实际上从两者的成员 在访问方式上的不同就可以看出了。struct类型成员的访问方式是变量名.成员而enum成员的访问方式为成员名。 因此若两个enum类型中有重名的成员那代码中访问这个成员时到底指的是哪个enum中的成员呢 两个#define宏定义是可以重名的该宏名真正的值取决于最后一次定义的值。编译器会给出警告但不会error