课程介绍
参考:麦子学院-嵌入式C语言高级
本套课程的定位
前导课程:掌握简单C语言的基本语法
计算机程序语言的学习思路?
基本程序设计思想+语言工具的特性
基本程序设计思想:
数据类型、运算符、条件分支、循环设计
面向对象的设计
C语言工具的特性:
比如操作底层,尤其是内存地址的寻址及操作,指针的使用
掌掘C语言的设计思路,比普通的语法要重要的多
万变不离其宗,掌掘C语言的核心规律。
本套读程的想法
不是教程,是一种嵌入式C语言思想和设计的交流
什么时候用?
怎么用?
为什么要这样设计?
推荐教程
C语言深度解剖(第2版》 解开程序员面试笔试的秘密
C专家编程
程序员面试宝典《第4版)
C和指针
偏重嵌入式方向的使用
嵌入式课程的特点
2-1 : GCC的使用及其常用选项介绍【重点】
掌握C语言如何变成机器指令的过程
gcc工具的几个常用选项的意义
【难点】
C编译过程中在gcc工具上的体现
【实验考核】
自己编写程序,利用gcc工具集验证每一步的执行效果
举例说明gcc选项的意义:
gcc -I gcc -L
gcc -E gcc -S gcc -c
Gcc概述
c语言的编译过程
可参考:C/C++|物联网开发入门+项目实战|C语言基础|C语言的预处理及编译过程分析-学习笔记(2)
C语言常见错误举例
预处理错误;
include “name” 当前目录
include 系统库(环境变量)
not find
示例
从abc1.h中引入字符char_c
#include <stdio.h>
#include "abc1.h"
int main()
{
char c=char_c;
printf("c(char)=%c,c(int)=%d,c_change(char)=%c,c_change(int)=%d,c_change(HEX)=%#x\n",c,c,c+32,c+32,c+0x20);
return 0;
}
输出:
E:\temp>cd “e:\temp” && gcc 2.c -o 2 && "e:\temp"2
2.c:2:18: fatal error: abc1.h: No such file or directory
compilation terminated.
显然无此文件(文件名错误),改为include “abc.h”,运行正常。
而一般地,将头文件一般放在inc目录下。
建立inc,并把abc.h移入inc:
1、方法1:修改inclue为#include “./inc/abc.h”;
2、方法2:gcc -I ./inc 2.c -o 2
编译错误
语法错误,
链接错误
打包出错,原材料不够 undefined reference
寻找标签是否实现了,链接时是否加入一起链接
示例
#include <stdio.h>
#include "abc.h"
void fun(void);
int main()
{
char c=char_c;
printf("c(char)=%c,c(int)=%d,c_change(char)=%c,c_change(int)=%d,c_change(HEX)=%#x\n",c,c,c+32,c+32,c+0x20);
fun();
return 0;
}
main函数中引用了一个fun函数,但没有fun函数的实现,故链接器collect2.exe时出错,
提示:
E:\temp>gcc -I ./inc 2.c -o 2 && "e:\temp"2
C:\Users\Vera\AppData\Local\Temp\ccRNGpif.o:2.c:(.text+0x4b): undefined reference to `fun’
collect2.exe: error: ld returned 1 exit status
解决方法1:不声明,删除fun()的定义并直接实现一个空函数:
void fun(void)
{
}
方法2:2.c不变,新建立abc.c存放fun()的函数实现,如:
abc.c
void fun(void)
{
}
执行程序:>gcc -I ./inc -o 2 2.c abc.c #将所有用到的原材料放在输出文件之后
方法3:分别编译再拼接
E:\temp>gcc -c -I./inc -o a.o 2.c
E:\temp>gcc -c -o b.o abc.c
分别编译完成后的文件都输出在当前目录下:
gcc -o 2 a.o b.o
原材料多了(重复定义) multiple definition
解决思路:多次实现了标签,只保留一个标签实现
在2个文件中都实现fun()函数,并重新编译,链接:
E:\temp>gcc -c -I./inc -o a.o 2.c
E:\temp>gcc -c -o b.o abc.c
E:\temp>gcc -c -I./inc -o a.o 2.c
E:\temp>gcc -o 2 a.o b.o
提示错误:
E:\temp>gcc -o 2 a.o b.o
b.o:abc.c:(.text+0x0): multiple definition of `fun’
a.o:2.c:(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
宏的使用
预处理的使用
#include 包含头文件,并在当前位置展开
#deine 宏(替换),不进行语法检查,编译时才检查
#define 宏名 宏体 加括号
#define ABC (5+3)
#define ABC(x) (5+(x)) //宏函数
预定义宏(方便调试)
(C语言编译器或gcc定义)
前后2个下划线
FUNCTIO
LINE
FILE
示例
#include <stdio.h>
int main()
{
//本行执行的函数名,所在文件名,本行的行号
printf("the %s,%s,%d\n",__FUNCTION__,__FILE__,__LINE__);
return 0;
}
输出:
E:\temp>cd “e:\temp” && gcc 2.c -o 2 && "e:\temp"2
the main,2.c,7
改示例为
#include <stdio.h>
int fun()
{
int a;
//本行执行的函数名,所在文件名,本行的行号
printf("the %s,%s,%d\n",__FUNCTION__,__FILE__,__LINE__);
return 0;
}
int main()
{
fun();
return 0;
}
输出(实际调用的函数名称和所在行号):
E:\temp>cd “e:\temp” && gcc 2.c -o 2 && "e:\temp"2
the fun,2.c,8
条件预处理
#ifdef #else #endif
调试版本
发行版本
示例
原始代码:
#include <stdio.h>
int main()
{
printf("=========================\n",__FILE__);
printf("hello world!\n");
return 0;
}
debug期间允许__FILE__行显示,但发行版本中不显示,怎么实现:
#include <stdio.h>
int main()
{
#ifdef ABC
printf("=========================\n",__FILE__);
#endif
printf("hello world!\n");
return 0;
}
未定义ABC时,不打印:
E:\temp>cd “e:\temp” && gcc 003.c -o 003 && "e:\temp"003
hello world!
需要显示调试信息时,只要在#ifdef ABC之前定义ABC即可。
但是该方法还是要修改源文件,不是很方便,故引入gcc -D:cpp之前增加define
如:gcc -DABC -o build 003.c 相当于编译前引入: #define ABC。
宏展开下的#、##
# 字符串化
## 连接符号
#define ABC(x) #x
#define ABC(x) day##x //day和x进行连接
示例
#include <stdio.h>
#define ABC(x) #x //字符串化
#define DAY(x) myday##x //字符串在当前位置展开,可以执行调用前,后缀
int main()
{
int a;
int myday1 = 10;
int myday2 = 20;
//本行执行的函数名,所在文件名,本行的行号
// printf("the %s,%s,%d\n",__FUNCTION__,__FILE__,__LINE__);
printf(ABC(ab\n)); //相当于:"ab\n"
printf("the day is %d\n",DAY(1)); //前缀或者后缀以隐藏方式调用。
printf("the day is %d\n",DAY(2));
return 0;
}