C代码不能直接被真正的编译器所编译,需要一段程序对它进行翻译,负责翻译的程序叫做预处理器,翻译的过程叫做预处理,被翻译的代码叫做预处理指令,以#开头的都是预处理指令
gcc -E code.c
gcc -E code.c -o code.i
#include 把一个头文件导入到当前文件中
#include <>
#include “”
系统指定的路径可以通过设置环境变量来修改;
可以指定编译参数-I /路径 指定头文件的路径
#define 宏名 字面值数据
如果使用了宏,在预处理时会把所有定义宏替换成宏名后面的字面值数据
优点:方便批量处理、提高代码可扩展性、提高代码可读性、提高了安全性、可以与case配合使用
注意:宏名一般全部大写,末尾不要加分号,不能直接换行(可以使用续行符)
预定义宏:
__func__
__FILE__
__LINE__
__DATE__
__TIME__
#define FUNC(arg) {arg*10}
不是真正的函数,就是带参数的宏,使用宏函数的位置就会替换成宏函数后面的代码,调用者提供的参数会替换到相应的位置
注意:可以使用续行符、大括号来保护代码
由于处理的位置和参数不同,导致同一个宏函数有多种功能,这就叫宏函数的二义性,可以在最外层加括号、每个参数也加括号来降低产生二义性的可能。
# 把宏函数的参数变成字符串
## 合并两个参数变成标识符
根据条件决定是否生成代码或决定哪一部分参与最终的编译
#if/#elif/#else/#endif/#ifdef/#ifndef
头文件卫士:防止头文件被重复包含
#ifndef FILENAME_H
#define FILENAME_H
...
#endif//FILENAME_H
版本控制:
#if VERSION >=3
//版本3.0
#elif VERSION >=2
//版本2.0
#elif VERSION >=1
//版本1.0
#else
//
判断编译器:
#if __cplusplus
printf("C++编译器\n");
#else
printf("C编译器\n");
#endif
判断操作系统位数:
#if __x86_64__
printf("64\n");
#elif __i386__
printf("32\n");
#endif
定义一个表示一年有多少秒的宏常量,忽略闰平年
#define YEAR (365243600u)
对于类型重定义 typedef和#define的区别
宏函数与普通函数的区别
在定义常量时,const与#define的区别
问题:头文件可能被任意源文件包含,意味着头文件中的内容会在多个文件中存在,合并时不能有冲突。
重点:头文件中只能编写申明语句,不能有定义语句。
全局变量声明
函数声明
宏常量
宏函数
typedef类型重定义
结构、联合、枚举的类型声明
1、为每一个.c文件编写一个.h文件 .h文件是对.c文件的说明
2、如果需要用到某个.c文件中的函数、变量、宏、只需要把它的头文件导入。
3、.c文件也需要导入 自己的.h文件,目的是为了让声明和定义一致。
.h 写大纲 .c里面写细节 .c中需要添加自己的头文件
加入a.h包含了b.h,而b.h又包含了a.h,这种情况就叫做头文件相互包含,这种情况就会编译出错。
错误:未知类型名错误 “xxx”一般都是因为头文件相互包含导致的。
错误:还可能是粗心在复制头文件时没有改变参数。
解决方案:把a.h,b.h的交集的内容 重新拿出来命名一个c.h。所以.h文件的数量一般都比.c多。
注意:头文件相互包含(套娃,自己找),和重复包含(同一个头文件被多次导入,,头文件卫士解决)的区别
Makefile是一系列编译指令组成的可执行文本,也叫做编译脚本。
在终端执行make命令会自动执行Makefile脚本中的编译命令,而且它还可以根据文件的最后修改时间来判断该哪些
文件需要重新编译、哪些文件不需要重新编译,从而大大提高编译效率
编译规则:
1、如果这个工程没有编译过,那么我们的所有.c文件都要编译并链接。
2、如果某个.c文件被修改了,那么此次只编译被修改过的.c文件 并链接。
3、如果某个.h文件被修改了、那么依赖它的所有.c文件都需要重新编译并链接。
一个最简单的makefile脚本:
执行目标:依赖
编译指令
被依赖的目标1:依赖的文件
编译指令
被依赖的目标2:依赖的文件
编译指令
...
负责清理的执行目标
clean
…
target ... : prerequisites
command
实例:对之前的通讯录做出改变
mail : main.o phone_book.o tool.o
gcc -o mail main.o phone_book.o tool.o
main.o : main.c phone_book.h tool.h
gcc -std=gnu99 -c main.c
phone_book.o : phone_book.c phone_book.h tool.h
gcc -std=gnu99 -c phone_book.c
tool.o : tool.c tool.h
gcc -std=gnu99 -c tool.c
clean:
rm -rf mail main.o phone_book.o tool.o
//根据报错的信息添加相应文件需要的内容
升级改版
OBJ = main.o phone_book.o tool.o
CC = gcc
BIN = mail
STD = -std=gnu99
FLAG = -Wall -Werror
INCLUDE = -I .
LIB = -lm
all:$(OBJ)
$(CC) -o $(BIN) $(OBJ)
%.o:%.c
$(CC) $(STD) $(FLAG) -c$< -o$@ //$<匹配.c文件 $@改名换成.o
clean:
rm -rf $(OBJ)
文件规则:
main.c 程序入口
game2048.c game2048.h 游戏的业务逻辑
direction.c direction.h 方向键处理
tools.c tool.h 工具函数
Makefile 编译脚本
game2048.conf 编写脚本文件
CC = gcc
STD = -std=gnu99
BIN = game2048.bin
FLAG = -Wall -Werror -DDEBUG
OBJ = main.o game2048.o direction.o tool.o
all:$(OBJ)
$(CC) $(STD) -o $(BIN) $(OBJ)
main.o:main.c game2048.h
$(CC) $(STD) $(FLAG) -c main.c
game2048.o:game2048.c game2048.h direction.h tool.h
$(CC) $(STD) $(FLAG) -c game2048.c
direction.o:direction.c direction.h game2048.h tool.h
$(CC) $(STD) $(FLAG) -c direction.c
tool.o:tool.c tool.h game2048.h
$(CC) $(STD) $(FLAG) -c tool.c
clean:
rm -rf $(BIN) $(OBJ)
按键函数up( )
void up(void)
{
debug("%s\n",__func__);
for(int y=0;y<4;y++)
{
//合并的边界值
int end = 0;
for(int x=1;x<4;x++)//三层for循环利用边界值来判定是否需要下一步的移动或者合并
{
for(int i=x;i>end;i--)
{
if(view[i][y] && !view[i-1][y])
{
//移动
view[i-1][y] = view[i][y];
view[i][y] = 0;
is_move_merge = true;
}
else if(view[i][y] && view[i-1][y] == view[i][y])
{
//合并
view[i-1][y] *= 2;
view[i][y] = 0;
end = i;
is_move_merge = true;
}
}
}
}
}//其它方向同理
运用指针数组来使用堆内存
int (*view)[4] = NULL;
// 初始化相关资源,加载数据
void init_game(void)
{
debug("%s\n",__func__);
// 申请堆内存
view = calloc(sizeof(view[0][0]),16);
// 从文件中加载数据
}
malloc() 与 free 对应
// 释放相关资源,保存数据
void exit_game(void)
{
debug("%s\n",__func__);
// 保存数据
// 释放堆内存
free(view);
view = NULL;
}
判断是否有空位置来产生随机2,不然当没有空位时也会一直卡在随机给2的函数处
static bool is_full(void)
{
int* arr = (int*)view;
for(int i=0; i<16; i++)
{
if(0 == arr[i])
return false;
}
return true;
}
// 随机位置产生一个2
void rand_number(void)
{
debug("%s\n",__func__);
srand(time(NULL));
if(is_full())
{
debug("没有空位置\n");
return;
}
while(is_move_merge)
{
int x = rand()%4;
int y = rand()%4;
if(0 == view[x][y])
{
view[x][y] = 2;
return;
}
}
}