目录
结构体的三种初始化
定义如下结构体
struct student
{
char name[100];
int age;
float height;
} ;
1、定义变量时初始化
struct student xm = {"小明",18,175.3}
2、不按顺序的初始化
struct student xm ={.age=18, .height=175.3, .name="小明"};
3、在定义后初始化
struct student xm;
//xm.name = "小明"; // name是数组名是一个常量,常量不能赋值
strcpy(xm.name,"小明");
xm.age = 18;
xm.height=173.5;
结构体的两种引用
1、结构体变量是普通值时,使用变量名+ “ . ” 访问
2、结构体变量是指针时,使用指针+ “ -> ” 访问
结构体数组
定义:
struct student
{
char name[1024]; // 姓名
int id; // 学号
char clas[1024]; // 班级
};
初始化:
struct student arry[50] =
{
{"小明", 1, "高一三班"},
{"小东", 2, "高二三班"},
{"小美", 3, "高三三班"},
};
结构体大小
结构体大小是根据 《最大的数据字节》对齐的原则进行分配的! 每次数据分配的空间都是《根据最大的字节数》进行对齐。
参考:【C语言】结构体内存对齐_编译器默认对齐数-CSDN博客
结构体实现位段操作
位段操作允许程序员直接操作数据的特定位,而不是整个数据结构
从而在需要精确控制硬件或进行高效数据处理时非常有用。
带位段操作的结构体的大小
联合体
联合体的外在形式跟结构体非常类似,但它们有一个本质的区别:
结构体中的各个成员是各自独立的,而联合体中的各个成员却共用同一块内存,因此联合体也称为共用体。
整个联合体变量的尺寸,取决于联合体中尺寸最大的成员。
给联合体的某个成员赋值,会覆盖其他的成员,使它们失效。
联合体各成员之间形成一种“互斥”的逻辑,在某个时刻只有一个成员有效。
声明联合体:
union attr
{
int x;
char y;
double z;
};
定义联合体:
int main()
{
// 定义联合体变量
union attr n;
}
// 普通初始化:第一个成员有效(即只有100是有效的,其余成员会被覆盖)
union attr at = {100, 'k', 3.14};
// 指定成员初始化:最后一个成员有效(即只有3.14是有效的,其余成员会被覆盖)
union attr at = {
.x = 100,
.y = 'k',
.z = 3.14,
};
联合体指针
union attr *p = &at;
p->x = 100;
p->y = 'k';
p->z = 3.14; // 只有最后一个赋值的成员有效
printf("%d\n", p->x);
printf("%c\n", p->y);
printf("%lf\n", p->z);
内存对齐
1.修改一个数据的对齐原则
char c __attribute__((aligned(32))); // 将变量 c 的值、内存对齐值设置为32
attribute语法:
attribute 机制是GNU特定语法,属于C语言标准语法的扩展。
attribute 前后都是双下划线,aligned两边是双圆括号。
attribute 语句,出现在变量定义语句中的分号前面,变量标识符后面。
attribute 机制支持多种属性设置,其中 aligned 用来设置变量的 m 值属性。
一个变量的内存对齐值只能提升,不能降低,且只能为正的2的n次幂
2.不适用任何的对齐原则 ,直接以真实大小存储
__attribute__((packed));
内存对齐,是为了CPU 更高效率取读取,内存的资源进行处理。 修改空间虽然可以节省内存,但是会降低CPU的处理效率。
C的预处理
头文件:#include
定义宏:#define
取消宏:#undef
条件编译:#if、#ifdef、#ifndef、#else、#elif、#endif
显示错误:#error
修改当前文件名和行号:#line
向编译器传送特定指令:#progma
一个逻辑行只能出现一条预处理指令,多个物理行需要用反斜杠连接成一个逻辑行
如:
#define LONG_MACRO_NAME \
this is a long macro definition \
that spans multiple lines
将 LONG_MACRO_NAME 替换为 this is a long macro definition that spans multiple lines
可以通过如下编译选项来指定来限定编译器只进行预处理操作:
gcc example.c -o example.i -E
带参宏
//使用 pf 来替换 printf("%d",参数);
#define pf(参数) printf("%d",参数);
// 求两个数据的较大值
#define max(a, b) a > b ? a : b
// 求两个数据的较小值
#define min(a, b) a < b ? a : b
注意事项:
带参宏的特点:
直接文本替换,不做任何语法判断,更不做任何中间运算。
宏在编译的预处理阶段就被替换掉,运行中不存在宏。
宏将在所有出现它的地方展开,这一方面浪费了内存空间,另一方面有节约了切换时间。
使用双井号粘贴字串
#define LAYER_INITCALL(num, layer) __zinitcall_##layer##_##num
如果使用 LAYER_INITCALL(service, 1),那么预处理器会将其展开为 __zinitcall_service_1;如果使用 LAYER_INITCALL(feature, 2),则会展开为 __zinitcall_feature_2。
如果字串出现在最末尾,则最后的双井号必须去除,如果粘贴的字串并非出现在最末尾,则前后都必须加上双井号
另外,如果字串本身拼接为字符串,那么只需要使用一个井号即可
例如:
#define domainName(a, b) "www." #a "." #b ".com"
int main()
{
printf("%s\n", domainName(baidu, aaa));
}
//输出www.baidu.aaa.com
条件编译
#define A 0
#define B 1
#define C 2
#if A
printf("真");
#endif
// 二路分支
#if A
printf("A");
#elif B
printf("B");
#endif
// 多路分支
#if A
printf("A");
#elif B
printf("B");
#elif C
printf("C");
#endif
ifndef和endif语句判断头文件中函数是否被重复定义
如对C的标准库进行定义时:
#ifndef stdio
#define stdio
void printf。。。
。。。scanf。。。
。。。int。。。
。。。float。。。
#endif
有时候多个头文件中包含了相同的函数声明或定义,或者一个程序中重复引用了某些头文件,这样做是为了防止其被重复引用