C复健计划
本文是笔者研究生入学期间对C进行系统性复习总结而成的笔记,因此内部排版混乱,知识点散乱而不成系统,望读者谨慎参考
C复健计划
printf函数无法打印多行字符串,若要打印多行字符串则需要在每行后加换行符\n
\n代表换行,\n\代表下一行是上一行的延续,如果改为\n则会报错
变量名第一个字母不能是数字
ANSI C C99 C11 关键字数量分别是32 32+5 37+7
%11.2f代表输出11个占位符,其中浮点型输出到小数点后2位,如果%a.bf,其中b比a大,则直接输出到小数点后b位而忽略a的值
%x: 十六进制 (hex) %o: 八进制(oct) %d: 十进制(dec)
/* */代表多行注释
<>表示导入系统文件 ““表示导入自定义文件`
Tab == 8个空格,或者4个空格
编译分为四步
- 预编译:宏定义展开,头文件展开,条件编译等,并将注释删除(条件编译是指根据系统位数,编译库的选择确定编译环境)
- 编译:将预编译后的文件生成汇编文件
- 汇编:将汇编文件生成为目标文件(二进制代码)
- 链接:把库链接到可执行程序中(预编译只是说明函数格式)
强制类型转换不考虑四舍五入,后果是损失精度,系统自动类型转换时也会损失精度
(int) a*b 是先将 a,b 损失精度,再计算结果 (int) (a*b)是先保留精度计算,再损失精度
表达式 ? 语句1 : 语句2 表达式为真,执行语句1,否则执行语句2
switch 中可加 default 并且 case 后要接 break
for 循环可以有多个判断代码,需用 , 隔开,只要一个表达式不满足就跳出循环
goto FLAG; ………… FLAG: …………
数组下标必须是常量,不能写为下例:int i = 10; int arr[i];
当一个数组初始化(开辟内存空间)时,内部元素默认是乱码。只有当赋值时,默认才是0
eg: int arr[4] = {1}; //内部元素:1,0,0,0 int arr[4]; arr[0] = 1; //内部元素:1,数字,数字,数字
数字 0 等同于 \0 但不等同于字符 ‘0’
char arr[] = “hello”; 此时arr数组共有6位(最后一位是\0)
char arr[5] = {‘h’,’e’,’l’,’l’,‘o’}; 此时只有六位,打印的时候到 o 不会停止,会继续打印乱码,所以char arr[n]的数组只能存储n-1个字符
scanf接收到空格或回车就代表接收的结束
gets() 与 scanf() 的区别是,get 能接收空格,scanf 则不能
sizeof() 测量字符串数组长度时包括 /0 strlen() 则不包括
函数返回值传给寄存器后内存中的函数会被销毁
函数声明: extern int fun_name( type a, type b ); == int fun_name( type a, type b ); == int fun_name( type , type ); 可以不用写在主函数之内
声明不需要内存
exit(); 在子函数中使用主函数也会退出
导入自己的头文件用 “myfile.h”
#ifndef “myfile.h” #define 文件名 #endif
char 占1个字节分配1个地址 int 占4个字节分配4个地址
win 存储方式是小端对齐 即 低位数据存在低位地址中
& 是取址符号,是升维的 * 是取值符号,是降维
野指针: 指向某一个未知空间的指针变量
int const *p == const int* p 只能给 p 赋值,不能给 p 指向的变量赋值 因为 const 离 “*”近
int* const p 只能给 p 指向的变量赋值,不能给 p 赋值,因为 const 离 “p” 近
指针 +1 == 内存地址 +(sizeof(type 指针))
数组做为函数参数时会退化为指针,损失数组的大小信息,所以需要额外的数组长度做为函数参数
p[-2] 等于当前指向某一数组的指针 p 向前数2个的那个元素
2个指针相减后的结果是步长,不能相加
int* arr[3] 指针数组
arr[i] == *(arr+i)
一级指针加偏移量相当于跳过元素,二级指针加偏移量相当于跳过一维数组
-
一个参数既可以是const又可以是volatile么?
- const volatile常用在对于状态寄存器重新读取数值的时候,因为状态寄存器的值易变,因此加volatile,因为程序不应该修改状态寄存器的值,因此为const
-
一个指针可以是volatile的么?
- 可以但不常见,当需要一个经常被修改的指针时需要这么做,比如在中断子程序中会被修改的buffer指针。因为我们不知道中断处理程序何时被执行,这个指针何时被修改,因此这个指针在定义时需要使用volatile修饰
SRAM没有电容结构,因此充放电比DRAM快,读取数据也就比DRAM快,并且也不需要刷新数据 需要使用malloc来申请结构体的情况:
- 结构体大小需要动态分配(结构体内包含本身的指针)
- 结构体生命周期较长,需要在函数结束时不销毁结构体时需要
- 对性能要求不大,在栈上分配和释放内存通常比堆上快,对性能不做要求时可以使用malloc 即使在函数参数给出了const限定,但是依然有变量被更改的风险
#include <stdio.h>
int add(int* const a,int* const b)
{
int* temp1 = a;
int* temp2 = b;
*temp1 = 6;
*temp2 = 4;
return *a+*b;
}
int main()
{
int a = 5;
int b = 3;
int c = add(&a,&b);
printf("%d\n",c);
return 0;
}
::: alert-danger
结果是10
:::
define定义宏的时候不用括号括起来可能导致运算错误
#define N 3+2
int a = N*N; //a = 3+2*3+2
内存对齐
- 为什么要进行内存对齐
- 32位计算机是以地址为4的倍数读取数据的,如果不进行内存对齐可能会引发CPU额外进行数据裁剪与合并操作,降低CPU效率
- 怎样计算内存对齐?
- gcc中默认#pragma pack(4),即默认内存对齐有效值为4字节,我们可以在编译器内设置这个值。结构体一个成员的大小为有效值和那个成员的整数倍,并且满足结构体总大小为内存对齐有效值的整数倍
struct
{
int i;
char c1;
char c2;
}x1;
struct{
char c1;
int i;
char c2;
}x2;
struct{
char c1;
char c2;
int i;
}x3;
int main()
{
printf("%d\n",sizeof(x1)); // 输出8
printf("%d\n",sizeof(x2)); // 输出12
printf("%d\n",sizeof(x3)); // 输出8
return 0;
}
##可以表示粘连两个字符串,例如下面使用##定义了两个不同的变量NAMEa与NAMEb
#include <stdio.h>
#define NAME(n) int_name##n
int main()
{
int NAME(a);
int NAME(b);
NAME(a) = 520;
NAME(b) = 111;
printf("%d\n", NAME(a));
printf("%d\n", NAME(b));
return 0;
}