C-class Week3.Day14

复习

预处理指令:

C代码不能直接被真正的编译器所编译,需要一段程序对它进行翻译,负责翻译的程序叫做预处理器,翻译的过程叫做预处理,被翻译的代码叫做预处理指令,以#开头的都是预处理指令

gcc -E code.c  
gcc -E code.c -o code.i
1、文件包含

#include 把一个头文件导入到当前文件中
#include <>
#include “”
系统指定的路径可以通过设置环境变量来修改;
可以指定编译参数-I /路径 指定头文件的路径

2、宏常量

​ #define 宏名 字面值数据
​ 如果使用了宏,在预处理时会把所有定义宏替换成宏名后面的字面值数据
​ 优点:方便批量处理、提高代码可扩展性、提高代码可读性、提高了安全性、可以与case配合使用
​ 注意:宏名一般全部大写,末尾不要加分号,不能直接换行(可以使用续行符)
​ 预定义宏:

    __func__
    __FILE__
    __LINE__
    __DATE__
    __TIME__
3、宏函数

​ #define FUNC(arg) {arg*10}
​ 不是真正的函数,就是带参数的宏,使用宏函数的位置就会替换成宏函数后面的代码,调用者提供的参数会替换到相应的位置
​ 注意:可以使用续行符、大括号来保护代码

二义性:

由于处理的位置和参数不同,导致同一个宏函数有多种功能,这就叫宏函数的二义性,可以在最外层加括号、每个参数也加括号来降低产生二义性的可能。

	#	把宏函数的参数变成字符串
	##      合并两个参数变成标识符
4、条件编译

​ 根据条件决定是否生成代码或决定哪一部分参与最终的编译
​ #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

5、常考的笔试面试题

​ 定义一个表示一年有多少秒的宏常量,忽略闰平年
​ #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多。

​ 注意:头文件相互包含(套娃,自己找),和重复包含(同一个头文件被多次导入,,头文件卫士解决)的区别

Mekefile:

​ 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)

2048游戏makefile

​ 文件规则:

​ 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;
		}
	}
}

你可能感兴趣的:(标准C,c语言)