字符串&结构体&共用体&枚举

1.字符串:
C语言中字符串有3个核心要点:第一是用一个指针指向字符串头;第二是固定尾部(字符串总是以’\0’来结尾);第三是组成字符串的各字符彼此地址相连。
存储多个字符的2种方式:字符串和字符数组
(1)字符数组初始化与sizeof、strlen:
strlen是一个C语言库函数,这个库函数的原型是:size_t strlen(const char *s);这个函数接收一个字符串的指针,返回这个字符串的长度(以字节为单位)。注意一点是:strlen返回的字符串长度是不包含字符串结尾的’\0’的。我们为什么需要strlen库函数?因为从字符串的定义(指针指向头、固定结尾、中间依次相连)可以看出无法直接得到字符串的长度,需要用strlen函数来计算得到字符串的长度。
sizeof(数组名)得到的永远是数组的元素个数(也就是数组的大小),和数组中有无初始化,初始化多、少等是没有关系的;strlen是用来计算字符串的长度的,只能传递合法的字符串进去才有意义,如果随便传递一个字符指针,但是这个字符指针并不是字符串是没有意义的。
(2)字符串初始化与sizeof、strlen:
sizeof测的是字符指针p本身的长度,和字符串的长度是无关的。
strlen刚好用来计算字符串的长度。
(3)字符数组与字符串的本质差异:
字符数组和字符串有本质差别。字符数组本身是数组,数组自身自带内存空间,可以用来存东西(所以数组类似于容器);而字符串本身是指针,本身永远只占4字节,而且这4个字节还不能用来存有效数据,所以只能把有效数据存到别的地方,然后把地址存在p中。

2.结构体
数组有2个明显的缺陷:第一个是定义时必须明确给出大小,且这个大小在以后不能再更改;第二个是数组要求所有的元素的类型必须一致。更复杂的数据结构中就致力于解决数组的这两个缺陷。而结构体是用来解决数组的第二个缺陷的,可以将结构体理解为一个其中元素类型可以不相同的数组。结构体完全可以取代数组,只是在数组可用的范围内数组比结构体更简单。
(1)结构体变量中的元素访问方式:
只有一种,用.或者->的方式来访问。.和->访问结构体元素其实质是一样的,只是C语言规定用结构体变量来访问元素用. 用结构体变量的指针来访问元素用->。
其实质是指针的访问。
(2)结构体的对齐访问:
结构体中元素对齐访问主要原因是为了配合硬件,也就是说硬件本身有物理上的限制,如果对齐排布和访问会提高效率,否则会大大降低效率。
例如:32位编译器,一般编译器默认对齐方式是4字节对齐。其结构体的对齐访问关键:
①结构体对齐要考虑:结构体整体本身必须安置在4字节对齐处,结构体对齐后的大小必须4的倍数(编译器设置为4字节对齐时,如果编译器设置为8字节对齐,则这里的4是8)
②结构体中每个元素本身都必须对其存放,而每个元素本身都有自己的对齐规则。
③编译器考虑结构体存放时,以满足以上2点要求的最少内存需要的排布来算
(3)C语言中常用的由结构体变来的宏:
①offsetof宏:
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)
offsetof宏的作用是:用宏来计算结构体中某个元素和结构体首地址的偏移量
offsetof宏的原理:我们虚拟一个type类型结构体变量,然后用type.member的方式来访问那个member元素,继而得到member相对于整个变量首地址的偏移量。
②container_of宏:
#define container_of(ptr, type, member) ({ \
const typeof(((type )0)->member) __mptr = (ptr); \
(type )((char )__mptr - offsetof(type, member)); })
作用:知道一个结构体中某个元素的指针,反推这个结构体变量的指针。有了container_of宏,我们可以从一个元素的指针得到整个结构体变量的指针,继而得到结构体中其他元素的指针。
这个宏的工作原理:先用typeof得到member元素的类型定义成一个指针,然后用这个指针减去该元素相对于整个结构体变量的偏移量(偏移量用offsetof宏得到的),减去之后得到的就是整个结构体变量的首地址了,再把这个地址强制类型转换为type *即可。

3.共用体
(1)共用体和结构体的区别:
共用体和结构体的不同:结构体类似于一个包裹,结构体中的成员彼此是独立存在的,分布在内存的不同单元中,他们只是被打包成一个整体叫做结构体而已;共用体中的各个成员其实是一体的,彼此不独立,他们使用同一个内存单元。可以理解为:有时候是这个元素,有时候是那个元素。更准确的说法是同一个内存空间有多种解释方式。
union的sizeof测到的大小实际是union中各个元素里面占用内存最大的那个元素的大小。因为可以存的下这个就一定能够存的下其他的元素。
(2)共用体的用途:
测量大小段:
union myunion
{
int a;
char b;
};
// 如果是小端模式则返回1,大端模式则返回0
int is_little_endian(void)
{
union myunion u1;
u1.a = 1; // 地址0的那个字节内是1(小端)或者0(大端)
return u1.b;
}
当然也可以用指针的方式来测量来大小段
int is_little_endian2(void)
{
int a = 1;
char b = ((char )(&a)); // 指针方式其实就是共用体的本质

                            return b;
          }

4.枚举
枚举在C语言中其实是一些符号常量集。直白点说:枚举定义了一些符号,这些符号的本质就是int类型的常量,每个符号和一个常量绑定。这个符号就表示一个自定义的一个识别码,编译器对枚举的认知就是符号常量所绑定的那个int类型的数字。
(1)宏定义和枚举的区别:
枚举是将多个有关联的符号封装在一个枚举中,而宏定义是完全散的。也就是说枚举其实是多选一。
什么情况下用枚举?当我们要定义的常量是一个有限集合时(譬如一星期有7天,譬如一个月有31天,譬如一年有12个月····),最适合用枚举。(其实宏定义也行,但是枚举更好)
不能用枚举的情况下(定义的常量符号之间无关联,或者无限的)用宏定义。
(2)枚举的使用:
enum return_value
{
ERROR, // 枚举值常量是全局的,直接自己就可以用。
RIGHT,
};
enum return_value func1(void)
{
enum return_value r1;
r1 = ERROR;
return r1;
}
int main(void)
{
enum return_value r = func1();
if (r == RIGHT)
{
printf(“函数执行正确\n”);
}
else
{
printf(“函数执行错误\n”);
}
return 0;
}

参考:http://www.zhulaoshi.org/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值