【C语言】难点汇总复习(二)
目录
一、结构体
1.结构体内存对齐
对齐规则:
- 结构体的第一个成员对齐到和结构体起始位置偏移量为0的地址处。
- 其他成员变量对齐到对齐数的整数倍地址处;对齐数就是编译器默认对齐数和该成员变量大小中的较小值,VS中默认对齐数为8,Linux中没有默认对齐数,即对齐数就是该成员本身的大小。
- 结构体总大小是最大对齐数的整数倍,最大对齐数就是结构体中每个成员变量中最大的对齐数。
- 如果嵌套了结构体的情况下,嵌套的结构体对齐到自己成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体中成员的对齐数)的整数倍。
为什么存在内存对齐?
- 平台原因(移植原因):不是所有硬件平台都支持访问任意地址上的数据的,某些硬件平台只能在某些地址处取某些类型的数据,否则抛出硬件异常。
- 性能原因:为了访问未被对齐的内存,处理器可能需要访问两次内存,而对齐后的进需要一次内存访问。
- 总的来说,结构体的内存对齐是拿空间换时间的做法。
结构体在对齐方式不合适的情况下,我们可以自己更改默认对齐数:
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
//输出的结果是什么?
printf("%d\n", sizeof(struct S));
return 0;
}
//6
2. 位段
位段的内存分配:
- 位段的成员可以是int / unsigned int / signed int / char 等类型。
- 位段的空间上是按照需要以4个字节或者1个字节的方式来开辟的。
- 位段涉及很多不确定因素,是不跨平台的,注重可移植性的程序应避免使用位段。
- 和结构体相比,位段能达到同样的效果,并且能很好的节省空间,但存在跨平台问题。
展示一个在VS2013环境的使用案例:
3.联合体
联合体最大的特点是所有成员共用同一块内存空间,所以联合体也叫共用体。
联合体大小的计算:
- 联合体的大小至少是最大成员的大小
- 当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
//下⾯输出的结果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}
//8,16
可以使用联合体来判断当前机器的大小段:
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;//返回1是⼩端,返回0是⼤端
}
4.枚举
我们可以使用#define定义常量,为什么要用枚举?
枚举的优点:
- 增加代码的可读性和可维护性
- 和#define相比有类型检查,更严谨,也能调试
- 使用方便,能一次性定义多个常量
- 枚举是遵循作用域规则的,枚举声明在函数内就只能在函数内使用
enum Color//颜⾊
{
RED=2,
GREEN=4,
BLUE=8
};
二、编译链接过程
- 预处理阶段:删除所有#define,展开所有宏定义,处理所有条件编译、#include,删除所有注释,添加行号和文件名标识方便后续生成调试信息
- 编译:进行词法分析、语法分析、语义分析及优化,生成汇编代码
- 汇编:将汇编代码转换生成机器可执行的指令
- 链接: 地址和空间分配,符号决议和重定位等步骤
三、预处理详解
1.define定义常量
建议不要加上分号; 否则可能会很容易造成问题
#define MAX 1000;
#define MAX 1000
2.define定义宏
#define SQUARE( x ) x * x
如上示例,这个宏接收一个参数x,然后把SQUARE(5)替换为5*5
但这个宏存在一个问题,就是在如下代码执行时,会产生与预期不符的情况:
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
//替换为:a + 1 * a + 1 结果为11
//要解决只能改为:#define SQUARE(x) (x) * (x)
因此在实现带参的宏定义时,最好加上括号,不要嫌弃括号太多,这是保证正确的必备途径
3.宏和函数的区别
//宏有时候能做到函数做不到的事情,例如:
#define MALLOC(num, type)\
(type )malloc(num sizeof(type))
...
//使⽤
MALLOC(10, int);//类型作为参数
//预处理器替换之后:
(int *)malloc(10 sizeof(int));
四、部分关键字
1.extern
extern关键字的作用:
extern关键字用于声明一个变量或者函数,指明它是在其他文件中定义的,提醒编译器去其他文件中去找其定义
2.volatile
volatile关键字的作用:
volatile关键字用于告诉编译器当前修饰的变量可能会被意外修改,因此编译器不能直接去寄存器里拿这个变量,而必须去内存中去那这个变量
3.static
static的几个用处:
1.修饰局部变量,正常的局部变量在出了作用域后就会销毁,使用static修饰后能使局部变量生命周期变长,只有在整个程序走完才会销毁,但作用域不变,出了作用域无法识别
2.修饰全局变量,正常的全局变量在任何.c文件中都可见,而static修饰后,它的属性由外部链接变为内部链接,使得该全局变量只能在本.c文件中可见3.修饰函数,用static修饰函数后,会改变函数的链接属性,由外部链接变为内部链接,导致此函数只能在本.c文件中可见
原文地址:https://blog.csdn.net/2301_80555259/article/details/145105584
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!