学习 C 语言 - 笔记整理 (草稿)
Table of Contents
1 开始前
1.1 我们要做什么?
- 将 脑子里想的东西 和 解决问题的方法 翻译成 程序
- 程序
?:
计算机可以运行的 机器指令 (二进制代码 类似100000111100000100000001)- 我们直接写机器指令
?:
bang… 脑袋炸了 - 使用对应的 汇编指令 (addl $1, %ecx)
?:
好一点 但还是不要 - 自然语言
?:
喜欢 爱死了!:
目前还不可能达到 - 类似自然语言(高级语言) 限定的少量的词汇和语法
?:
OK
- 我们直接写机器指令
- 三个问题
- ? 用什么写程序
- IDE (集成开发环境)
- Editor (编辑器)
- ? 怎么翻译 (高级语言 -> 机器指令)
- 翻译(编译 或 解释)
- 编译器
- 编译流程自动化
- 翻译(编译 或 解释)
- ? 如何运行
- 运行
- 输入 输出
- ? 用什么写程序
- 脑子里的东西 和 解决问题的方法 怎么用 编程语言 写出来(表示) ?
- 把 所想的东西 转换成 计算(算法, 步骤, 菜谱)
- How?
- 计算机技术 基础知识 等等 (不在此笔记中啰嗦)
- 这里不关心 底层(计算机具体实现) 把它抽象掉(仅仅使用一些词语来代表)
1.2 准备
- 开发环境
- 编辑器或IDE : Emacs or Xcode
- 编译器 : gcc or clang
- 编译流程自动化 : make, Makefile
- 流程示范: [OS:MacOS]
- 打开 终端(Terminal)
准备工作
mkdir test # 建立 项目目录 cd test # 进入 项目目录 touch code.c # 新建 源文件
用编辑器 打开源文件 写代码
/* code.c */ #include <stdio.h> int main(){ printf("hello world"); }
编译 和 运行
cc code.c -o code ./code
注释
// 单行 /* 多行 */
2 开始
2.1 第一步
- ? 我想让程序 输出 一些文字
文字 怎么表达
?:
用 字符串"字符串"
输出
?:
输出函数 这里使用puts函数puts("字符串");
!:
puts不是我们定义的 需要导入头文件 (放在开头)#include <stdio.h>
写在哪
?:
main函数里 main()是程序执行的起点 这是约定#include <stdio.h> int main(){ puts("字符串"); return 0; }
2.2 稍息
基础 就像是一块块不同的积木 我们要做的就是 把它们组合成 我们所希望的样子
一块积木是积木 两块积木组和起来也可被当作一个积木 给每块不同积木 或 组合后的积木 一个名字 就可以被方便的重复使用 我们编程就是 从符合规则的组合中 找到 能够解决待定问题的 一种最优的路径
已经准备好的积木 它们长啥样 怎么使用:
- 数字 : 整数 浮点数
- 简单的数学计算过程 : 四则运算符
- 名字 : 变量 指针
- 组合
- 过程(按执行顺序)
- 顺序 : 一块接一块
- 分支 : 每一块积木 可以和其它积木进行组合 产生的每一种符合规则的可能 就只一条分支
- 循环 : 重复积木
- 跳转 (很少用) : 往回等同于循环 往后就像是选择了不同的分支
- 聚合 : 数组 结构 共用 枚举
- 过程(按执行顺序)
- 封装过程(把组合起来的积木 给个名字 打包起来) : 函数 参数
2.3 第二步
- ? 输入温度(华氏 或 摄氏) -转换- 输出结果
- 把问题分解为更小的部分 可以理解 甚至是可以直接求解 一个个解决 最后组合在一起
- 分成三部分: 输入 转换 输出
- 输入
?:
从终端 形如: 30C - 转换
?:
公式: C = (5/9)(F-32) 和 F = (9C)/5+32 - 输出
?:
到终端 形如: 30C 是 86F
- 输入
- 继续 (程序名为: C52F)
- 输入
?:
从终端 形如: 30C从终端
?:
程序执行时C52F 30C
程序内部如何处理
?:
运行程序提供的参数 将作为参数交给 main函数// int argc, char *argv[] : 参数个数 和 存储参数的数组 int main(int argc, char *argv[]){ return 0; }
处理参数
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]){ // 检查参数 if(argc == 1){ printf("请重新运行 并提供需要转换的温度(形如 30C 或 89F)\n"); return 1; } // 获得参数 char* tempStr = argv[1]; // 转换参数 ?: 因为参数是字符串 为了计算 需要转换为 浮点数 int loct = strlen(tempStr) - 1; char type = tempStr[loct]; tempStr[loct] = '\0'; double temp = atof(tempStr); return 0; }
- 转换
?:
公式: C = (5/9)*(F-32) 和 F = (9C)*/5+32
// C = (5/9)*(F-32) C = (5.0/9)*(F-32); // F = (9*C)/5+32 F = (9*C)/5+32;
分发温度
switch (type) { case 'F': // C = break; case 'C': // F = break; default: printf("请重新运行 并提供需要转换的温度(形如 30C 或 89F)\n"); return 1; }
结合
char cvType = ' '; switch (type) { case 'F': cvTemp = (5.0/9)*(temp-32); cvType = 'C'; break; case 'C': cvTemp = (9*temp)/5+32; cvType = 'F'; break; default: printf("请重新运行 并提供需要转换的温度(形如 30C 或 89F)\n"); return 1; }
输出
?:
到终端 形如: 30C = 86Fprintf("%s%c = %f%c\n", inTemp, intype, cvTemp, cvType);
- 输入
完成
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]){ // 检查参数 if(argc == 1){ printf("请重新运行 并提供需要转换的温度(形如 30C 或 89F)\n"); return 1; } // 获得参数 char* tempStr = argv[1]; // 转换参数 ?: 因为参数是字符串 为了计算 需要转换为 浮点数 int loct = strlen(tempStr) - 1; char type = tempStr[loct]; tempStr[loct] = '\0'; double temp = atof(tempStr); // C = (5/9)(F-32) 和 F = (9C)/5+32 double cvTemp = 0; char cvType = ' '; switch (type) { case 'F': cvTemp = (5.0/9)*(temp-32); cvType = 'C'; break; case 'C': cvTemp = (9*temp)/5+32; cvType = 'F'; break; default: printf("请重新运行 并提供需要转换的温度(形如 30C 或 89F)\n"); return 1; } printf("%d%c = %f%c\n", (int)temp, type, cvTemp, cvType); return 0; }
!!! 干脆做视频 动态的表示 会更好 写出来太麻烦了
3 基础
3.1 类型
- ? 类型 : 代表 分配的内存空间大小 将实际大小进行抽象
- 类型
- 基本
- 整 : int (short, long) ([un]signed 默认有符号)
- 字符 : char ([un]signed 依实现)
- 浮点 : float, double (long double)
- 空 : void
- 枚举 : enum
- 指针
- 函数
- 派生
- 数组
- 结构
- 共用
- 基本
- Modifiers 修饰符 : unsigned, signed, short, long
3.1.1 表示范围
- <limit.h>
- char (编译器为 无|有符号)
- CHARMIN == 0|SCHARMIN
- CHARMAX == UCHARMAX|SCHARMAX
- …
- char (编译器为 无|有符号)
3.1.2 定长整型
- <stdint.h>
- [u]int(BITS)t : int8t, uint8t, …
- [U]INT(BITS)MAX, …
- intleast(BITS)t, …
3.1.3 类型别名
Typedef
typedef unsigned char byte; int* a[10]; // typedef int* t; // t 代表 int *类型 t a[10]; int (*a)[10]; typedef int t[10]; // t 代表 10个int组成的数组类型 t *a; typedef struct { int x; int y; }point; point pa, pb; // 函数 typedef void (*func)(int); void some(func f); // 不同之处 #define iptr int* iptr a, b; // int* a, b; a和b不同类型 typedef int* iptr; iptr a, b; // a和b 同型
3.2 变量
- 变量 : 内存地址 编译时将变量名替换为地址
- Identifier 标识符 : 名字
- 数字:0~9 大小写:A~Za~z 下划线:_ (非数字开头)
- 作用域 生命周期
- 作用域 : 全局 文件内静态 局部({}块内)
- 文件内静态 : 全局变量 前加上static 则作用域只在当前文件内
- 存储期 : 静态 自动 动态
- 静态 : 全局变量 指定static的变量 从程序开始到结束
- 自动 : 其它变量 生存在{}块内 (栈实现)
- 动态 : 从 malloc()分配 到 free()为止
- 存储类型修饰符
- auto (过时)
- static
- register
- extern : 使用 其它文件中定义的 变量或函数
- 作用域 : 全局 文件内静态 局部({}块内)
- 全局 和 静态 变量 自动初始化 为 0或NULL
- Qualifiers 限定符
- const : 常量
- 指针
- 指向常量的 : const int *a; == int const *a; : 指向const int型的指针 *a不可 a可 : 指向的值不可变
- 指向非常量的常量 : int * const a; : 指向int型的const指针 *a可变 a不可 : 指向的值可变
- 指向常量的常量 : int const * const a; : 都不允许改写
- 指针
- volatile : 不稳定 变量值可能被其它例程改变
- register : 强制变量存放到寄存器
restrict : 声明时 告诉编译器 指针没有别名
void add(int size, double* restrict arr1, const double* restrict arr2) {} double v1[] = {1.0, 2.0}; double v2[] = {1.0, 2.0}; add(2, v1, v2); double* v3 = v1; add(2, v1, v3); // Error
- const : 常量
3.3 数
整数
123 -789 // 进制表示法 31 // 十进制 025 // 八进制 0开头 0x19 // 十六进制 0x开头
浮点数(小数)
123.3 .3, 3. // 省略部分 1.2e2, 1.2e-2 // 科学计数法 1.32 // 默认 double类型 1.32f // 指明是 float类型
3.4 真值
- 非0即真
3.5 字符
- 字符 : 'a'
- 内部表示 : 整数
- ASCII编码 ?
- 编码 ?
- ASCII编码 ?
特殊字符(转义)
\a
alert \b
backspace \t
horizontal tab \n
new line \v
vertical tab \f
form feed \r
carriage return \[n]
八进制 \x[n]
十六进制 例
printf("%d\n", 'n'); //-> 110 printf("%d\n", '\n'); //-> 10 printf("%c\n", '\156'); //-> n printf("%d %d %d", '0', '\0', 0 ); //-> 48 0 0
3.6 数组
声明和初始化
/// 一维 int c[3] = {1, 2, 3}; int c[] = {1, 2, 3}; // 可省略 元素个数 int c[3] = {1}; // 整数自动补0 字符补\0 /// 二维 int a[2][2] = {1,2,3,4}; int a[][2] = {1,2,3,4}; // 可省略第一维元素个数 int a[][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }; int a[2][2] = {{1} {2}}; // 部分 /// 更高维度 类似 : 数组的 数组的 数组 ...
注意
// 声明后 大小 不可变 没有边界检查 // 声明和初始化 不能分开, 不能通过 赋值 进行初始化 int c[3]; c = {1,2,3}; // Error // Memberwise Initialization (C99) int a[3][2] = { [0][1] = 9, [2][1] = 8 };
3.7 字符串
- 字符串 : '\0' 结尾的字符序列
如何表示
?:
字符 数组|指针 或 字面量(读入自动转换)char str[] = {'a', 'b', 'c', '\0'}; char str[] = "abc"; char s[] = "a\0bc"; //-> a char* str = "abc"; // 空字符串 char str[] = "" char str[] = {'\0'} //== {0}
3.8 指针
- 声明 定义 使用
- 计算 : ptr+N == 当前指针的数据类型的长度 乘 N
3.9 数组 与 指针
- 数组名(数组变量) : 数组的起始地址 (使用&运算符 结果是其本身 &s == s)
- 不能指向其他地方 即不接受赋值操作
- 不分配任何空间 编译器在出现它的地方把它替换成数组的起始地址
- 不同维
- int a[10]
- a : int类型的指针
- &a : 10个int数组的指针
- int arr[rows][cols]
- arr : cols个 int数组的 指针
- &arr : rows*cols个 int数组的 指针
- int a[10]
- 不能指向其他地方 即不接受赋值操作
- 退化(信息的丢失) : 数组赋给指针变量 指针变量只会包含数组的起始地址
- 数组 还是 指针 ?: 函数参数的形式声明 则为指针
- [] 语法糖 : p[i] -> *(p+i)
- p[i] == i[p] (可以 但不要这么做)
Example
#include <stdio.h> int main(){ int a[3] = {0, 1, 2}; printf("%p, %p\n", a, &a); printf("%p, %p\n", a+1, (&a)+1); // | 0x7fff4fc1090c | 0x7fff4fc1090c | // | 0x7fff4fc10910 | 4 | 0x7fff4fc10918 | 12 | int (*p)[3] = a; printf("%d, %d\n", p[0][0], p[0][1]); // == *((*p)+1)) == (*p)[1] p = &a; printf("%d, %d\n", p[0][0], p[0][1]); // 0 1 int a2[2][3] = {{1, 3, 2}, {5, 6, 7}}; printf("%p, %p\n", a2, &a2); printf("%p, %p\n", a2+1, (&a2)+1); // | 0x7fff5a5c68f0 | 0x7fff5a5c68f0 | // | 0x7fff5a5c68fc | 3x4(int)=12 | 0x7fff5a5c6908 | 2x3x4(int)=24 | printf("%d, %d\n", 0xfc-0xf0, 0x908-0x8f0); p = a2; printf("%d, %d\n", p[0][0], p[0][2]); // 1 2 printf("%d, %d\n", p[1][0], p[1][2]); // 5 7 p = &a2; printf("%d, %d\n", p[0][0], p[0][2]); // 1 2 printf("%d, %d\n", p[1][0], p[1][2]); // 5 7 return 0; }
3.10 运算符
- , : 逗链的值 为 最后表达式的值
- a=(1+2, 3+3, 3+8); // a=11
- Assignment 赋值
- = (Assign equal)
- 复合赋值 : +=(Assign equal) 等等
- 二元操作符 注意: a<b<c 与 a<b && b<c 不同
3.10.1 Arithmetic 算术
- + Add
- - Subtract
- * Multiply
- / Divide
- % Modulus
- 正 % 正 -> 正 (其它取决于 编译器)
- ++ Increment
- – Decrement
3.10.2 Bitwise 位运算
- 逻辑
- & (and 与)
- | (or 或)
- ^ (xor 异或)
- ~ (one's complement 取反)
- 位移
- << (shift left 左移)
- >> (shift right 右移)
- 算术右移: 补1
- 逻辑右移: 补0
3.10.3 Logical 逻辑
- && (And)
- || (Or)
- ! (Not)
- ?: (Ternary)
3.10.4 Relational 关系 (比较)
- == (Equal)
- != (Not equal)
- > (Greater than)
- < (Less than)
- >= (Greater than equal)
- <= (Less than equal)
3.10.5 Data 数据
- sizeof()
- [] : array subscript
- & : address
- * : value of 解引用 (for pointer)
- -> : Structure dereference (for pointer)
- . : Structure reference
- sizeof
- sizeof : 在存储器中占 多少字节 (编译器 根据 类型的大小 或 变量声明的类型的大小 来返回相应的值)
- 只是 向编译器询问 所以只在编译器知道对象大小的情况下使用
- 参数: 类型名|变量名 Ex.(sizeof(char))
- 返回值: sizet (unsigned int)
Ex.
sizeof(int) // 4 sizeof(3) // 4 (3 -> int) sizeof('a') // 4 int /// String sizeof("Turtles!") // 9 == 8个字符 + '\0' /// Array // sizeof(数组名) -> 数组的大小 包括'\0' char i[] = {1,2,3}; sizeof(i); // 3 char s[5] = "How?"; sizeof(s); // 5 // sizeof(指针名) -> 4|8 (bit:32|64) char *t = s; sizeof(t); char ssa[][4] = {"abc","def","ghi"}; sizeof(ssa); // 12 sizeof(ssa[0]); // 4 sizeof(ssa)/sizeof(ssa[0]); // length == 3 char **ss = {"abc","def","ghi"}; sizeof(ss); // 8 sizeof(*ss); // 8
- sizeof : 在存储器中占 多少字节 (编译器 根据 类型的大小 或 变量声明的类型的大小 来返回相应的值)
3.11 控制流程
- 顺序 分支 循环(递归)
- 条件分支 : if switch
- switch
- 条件: 整型表达式(可包含: 运算符 函数调用)
- case : 整型常量(常量表达式 常量运算)
- break
- switch
- 循环 : while do-while for
- 跳转 : goto (label)
3.12 函数
- 把 部分程序 封装起来 并 给一个名字 方便重复使用
- 定义
- 调用
- 在调用之前 先定义 或 显示声明
- 参数的 计算顺序 不确定 (依赖:编译器)
- 返回值
3.12.1 函数指针
Example
int (*name)(int, int) = someFunc; name(1, 2); // name == &name typedef int (*f_name) (int, int); f_name name = someFunc; // 函数指针数组 int (*names[12])(int, int) = {NULL}; //or: typedef int (*name) (int, int); name names[12] = {NULL};
3.13 结构 共用 枚举
3.13.1 Enum 枚举
定义 声明 使用
enum animal { Dog, Cat, Monkey, Invalid }; // Dog == 0 enum animal { Dog=3, Cat, Monkey, Invalid }; // Dog == 3, Cat == 4 emu animal selected; switch (selected = select()){ case Dog : dog(); break; case Cat : cat(); break; case Monkey : monkey(); break; default error(); }
3.13.2 Struct 结构
- 值类型 : 赋值时拷贝
声明 定义 初始化 使用
struct point { int x; int y; }; struct point pa, pb; // struct point { int x; int y; }pa,pb; // typedef struct { int x; int y; }point; point pa, pb; // 初始化 struct point pa = {3, 5}; struct point pa = {.x=3, .y=5}; // [C99] // 使用 pa.x = 3; // 结构指针 struct point *pc = &pa; (*pc).x pc->x
- 结构数组
struct complex_struct { double x, y; } a[4];
- bitfield 位段(位域)
struct Name { unsigned vname1:bit1; unsigned vname2:bit2; // ... }; // struct record{ char* name; int refcount : 4; unsigned dirty : 1; };
- 包含自指向的指针
// Errro typedef struct { char* item; Node next; }* Node; // -> typedef struct node { char* item; struct node* next; }* Node; // or struct node; typedef struct node* Node; struct node { char* item; Nodenext; }; // or struct node { char* item; struct node* next; }; typedef struct node* Node;
- 域 在 结构中的字节偏移量
- <stddef.h> : offsetof() : offsetof(structs, f)
Imp
// (&0)->f - 0 #define offsetof(type, f) ((size_t) \ ((char*) &((type*)0)->f - (char*)(type*)0))
3.13.3 Union 共用
声明 定义 初始化 使用
union Data { int i; char c; }; union Data some = {88}; // 初始化 需要与第一个类型一致 printf("Int:%d", some.i); printf("Char:%c", some.c);
3.14 动态内存分配
- 在堆上分配空间
- 函数
- 分配: malloc, calloc, realloc,
- 一段空间 : void *malloc(sizet size);
- 一段空间 并 清零 : void *calloc(unsigned n, unsigned size); (空间长度: n*size)
- 重新分配 (变大或变小) : void *realloc(void *ptr, sizet size); (将ptr指向的空间大小 改为size)
- 释放: free
- void free(void *ptr);
- 填充: memset
- memset(ptr, n, size);
- 分配: malloc, calloc, realloc,
示例
int *p = (int*)malloc(sizeof(int)); free(p); int *array = (int*)calloc(3, sizeof(int)); free(array); double *d = (double*)malloc(sizeof(double)); int *i = (int*)realloc(d, sizeof(int)); free(d); free(i);
4 高级
4.1 IO
4.1.1 标准输入输出
- 字符 : getchar, putchar
- 字符串 : gets, puts
- 格式化 : scanf, printf, sscanf, sprintf
4.1.2 文件
- <stdio.h>
- 表示 : FILE型
- stdin, stdout, stderr 都是指向 FILE型 的指针
- 开关: fopen, fclose
- 方式 : r, w, a, rb, wb, ab, r+, w+, a+, rb+, wb+, ab+
- r|rb (read) : 读 文件需存在
- w|wb (write) : 写 文件需不存在
- a|ab (append) : 追加 文件需不存在
- r+|rb+ : 读 写从头
- w+|wb+ : 读 写覆盖整个文件
- a+|ab+ : 读 追加写
Ex.
FILE *fp; // 打开 : fopen(fname, mode) fp = fopen("abc", "r"); if(fp == null) error(); else // 关闭 fclose(fp);
- 方式 : r, w, a, rb, wb, ab, r+, w+, a+, rb+, wb+, ab+
- 读写 : fscanf, fprintf; fgetc, fputc, fgets, fread, fwrite
Ex.
/// 字符读写 // 单个字符 fgetc(fp); fputc('a', fp); // 格式化 读写 fscanf(fp, "%d", &x); fprintf(fp, "%d", x); /// 二进制读写 int x = 3; fwrite(&x, sizeof(int), 1, fp); fread(&x, sizeof(int), 1, fp); int a[10]; fwrite(a, sizeof(int), 10, fp); fread(a, sizeof(int), 10, fp);
- 定位 : fseek, ftell, rewind
- fseek(文件指针, 移动的字节数(字面量时需加后缀"L"), 起始点(首:SEEK-SET:0, 当前:SEEK-CUR:1, 尾:SEEK-END:2))
- ftell : 当前位置(位移量)
- rewind : 文件的位置指针 重回文件开头
4.2 Process
- fork, waitpid, exec
- signal
4.3 String
- <string.h>
- 长度 : strlen
- 比较 : strcmp, strncmp, memcmp (返回值: +:s1>s2 -:s1<s2, 0:s1==s2, )
- 复制 : strcpy(D,S), strncpy, memcpy, memmove, strdup
- 连接 : strcat(D,S), strncat
- 搜索 : strchr, strrchr, strstr, index, rindex
- 填充 : memset
- 分割 : strtok, strtokr
- 转换
- 大小写 : strupr strlwr
- ->(int|long, double) : atoi, atof
4.4 预处理
- 预定义名
- _LINE__ : 当前编译行的行号
- _FILE__ : 文件名
- _DATA__ : 程序创建日期
- _TIME__ : 程序创建时间
- _STDC__ : 当前编译器 是否符合标准C 是:1
- 头文件 : #include : 会读取头文件中内容 插入主文件
- <> : 在存放 C库函数头文件 的目录 中找
- "" : 优先在当前目录找 (或指定路径)
- 宏
- 创建 : #define
- 撤销#define的定义 : #undef
- 编译前被替换掉 运行时无法改变
对象式宏(变量式)
#define PI 3.14
函数式宏
// 最外层括号 用于 保证优先级 #define ADD_ONE(x) ((x) + 1) // ((3) + 1) <- ADD_ONE(3) #define putsa(str) ( putchar('\a'), puts(str); ) if (na) putsa("a"); // -> ( putchar('\a'), puts("a") ); // #define device_init_wakeup(dev,val) \ do { \ device_can_wakeup(dev) = !!(val); \ device_set_wakeup_enable(dev,val); \ } while(0) // 带可变参数 : ... 和 #__VA_ARGS__ (? ##__VA_ARGS__) #define showlist(...) printf(#__VA_ARGS__)
- 规则
- 1 外部使用括号 保证优先级
- 2 内部 所有参数出现的地方 使用括号
- 3 出现多次 要考虑 可能被多次求值 是否有副作用
- 规则
运算符 : #, ## , defined()
// #运算符 : 用于创建字符串 (只用于 函数式宏) // #运算符后面应该跟一个形参 中间可有空格或Tab #define STR(s) #s STR(hello world) // "hello world" STR(123\\12) // "123\12" // ##运算符 : 把前后两个预处理Token 连接成一个 #define CONCAT(a, b) a##b CONCAT(con, cat) // 展开成两个#号 #define HASH_HASH # ## #
条件编译 : #if, #else, #elif, #ifdef, #ifndef, #endif
#ifdef SPANISH char *greeting = "Hola"; #else char *greeting = "Hello"; #endif // SPANISH宏定义与否会改变这段代码的编译方式 // defined() : 预处理操作符 #if !defined(AIR) #endif #define RED 1 #define BLUE 2 #if COLOR == RED //... #elif COLOR == BLUE //... #endif
改变行号 : #line
// #line 行号["文件名"] #line 30"text.c" printf("%d", __LINE__); // 30
- 设定编译器状态 : #progma 参数
- message参数 : 编译信息输出窗口中输出的相信
- codeseg : 设置程序中 函数代码 存放的代码段
- once : 保证头文件被编译一次
- 编译器报错退出 : #error : #error UNKNOWN TARGET MACHINE
- 编译器遇到这个预处理指示就报错退出 错误信息就是UNKNOWN TARGET MACHINE
- #warning
5 概念
- 头文件
- 导入
- 字面量
- 作用域 生命周期
- 声明 定义
- 关键字 保留字
- 变量
5.1 声明
- 理解
- 从 名字 开始读 然后 根据优先级顺序读
- 优先级 从 高到低
- 括号内
- 后缀操作符
- () : 函数
- [] : 数组
- 前缀操作符 : * : 表示 指向 某某 的指针
- const, volatile 后跟
- 类型说明符 则作用于 类型说明符
- 其它情况 作用于 左边紧邻的指针星号
- Ex.
- char * const *(*next)()
- 名字 -> 变量名 next
- 括号内 -> next 是 一个指针
- 后缀 -> next 是 一个 函数 指针
- 前缀 -> next 是 一个函数指针 返回 一个指针
- 剩下 -> next 是 一个函数指针 返回一个指针(指向 一个 类型为char 的常量指针)
- int (*hoge)[3] : hoge是指针 指向int数组(3个元素)
- char * const *(*next)()