gcc的使用
-v / --v / --version 查看gcc版本号
-I目录 指定头文件目录,注意-I和目录之间没有空格
-c 只编译,生成.o文件,不进行链接
-g 包含调试信息
-On n=1~3 编译优化,n越大优化的越多
-Wall 提示更多告警信息(严格编译)
-D<DEF> 编译时定义宏,注意-D和<DEF>之间没有空格
-E 生成预处理文件,一般是.i文件,(展开头文件,替换宏常量)
-M 生成.c文件与头文件依赖关系以用于Makefile,包括系统库的头文件
-MM 生成.c文件与头文件依赖关系以用于Makefile,不包括系统库的头文件
gcc各项参数的实际说明
目录结构:
.
├── include
│ └── calc.h
├── src
│ └── calc.c
└── test
└── main.c
- 头文件创建格式 calc.h
#ifndef __CALA_H_
#define __CALA_H_
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b);
#endif
- 函数库 calc.c
int add(int a, int b)
{
return a+b;
}
int sub(int a, int b)
{
return a-b;
}
- 代码 main.c
#include <stdio.h>
//#include "../include/calc.h" //第一条
//#include <calc.h> //第二条
#include <calc.h> //第三条
int main(void)
{
int a = 4;
int b = 2;
printf("%d + %d = %d\n", a, b, add(a,b));
return 0;
}
-I
- 使用第一条头文件的时候,gcc的编译命令是
gcc main.c ../src/calc.c
,通过"../include/calc.h"
这段找到头文件并且编译成功- 使用第二条头文件的时候,gcc的编译命令是
gcc main.c ../src/calc.c -I../include/
,一般来说,用<>括起来的头文件是系统头文件,-I../include/
这条命令就是把../include/
这条目录临时并到系统查找的头文件目录里,来查找头文件- 使用第三条头文件的时候,gcc的编译命令还是
gcc main.c ../src/calc.c -I../include/
。
-c
- gcc命令
gcc main.c -I../include/
这样的话,会有错误提示
/tmp/ccQeQFPr.o: In function 'main':
main.c:(.text+0x29): undefined reference to 'add'
collect2: ld 返回 1
因为main函数要调用add函数,但是包含add函数的calc.c没有参与编译,造成add函数未定义
- 但是如果是执行
gcc main.c -I../include/ -c
这条命令则不会报错,会生成main.o这个文件,.o叫做可重新定位的目标二进制文件,它是不可执行的,它要跟calc.c生成的calc.o链接才会生成a.c的二进制可执行文件。
gcc main.c ../src/calc.c -I../include -c/
同时生成main.o和calc.o
-D
代码
#include <stdio.h>
int main(void)
{
int a;
scanf("%d", &a);
#ifdef MYDEBUG
printf("a = %d\n", a); //调试语句,查看scanf输入的值是多少
#endif
if(a > 10)
printf("num is more than 10\n");
else
printf("num is less than 10\n");
return 0;
}
- 在编译器进行预处理的时候,如果发现MYDEBUG(这个名字可以随便起)这个宏没有定义,就会无视从#ifdef到#endif之间的代码
只有定义了这个宏#define MYDEBUG 1
,#ifdef到#endif的代码才会执行,MYDEBUG可以不用置换任何常量,#define MYDEBUG
也是可以的。- 也可以通过gcc里面的编译参数来定义这个宏,并且这个宏数全局的
gcc -DMYDEBUG main.c
,这个时候#ifdef到#endif之间的代码也会执行
- 应用场景
在程序研发阶段可以利用-D调试代码,由于调试代码过多,也为了以后进行二次使用这段代码,在不删除调试代码的情况下,生成一个不会出现调试代码的产品,这种方法还是非常便利的。
当然,也可以把MYDEBUG放到头文件定义,但会有像上例中calc.c的文件本来就不必包含头文件的情况下,多少有些多余,但是方法还是可行的。
编译过程
hello.c->hello.i->hello.s->hello.o->a.out
编译工具链
- cpp :预处理器(展开头文件,替换宏常量)
- gcc :编译器,是把.c文件翻译成汇编,gcc -S把.c编译成汇编
- as :汇编器
- ld :链接器:1.插入启动代码,2.地址重定位
在.o文件链接之后,还有插入启动代码、地址重定位
实际上,一个程序是从启动代码开始的,启动代码在调用main函数,例如在main里打开一个文件,如果没有关闭,return 0之后,由启动代码把文件关掉,return的值也是返回给启动代码的。