gcc编译过程
-E:预处理、只关联主函数cpp文件,若有其他cpp文件会报错
g++: fatal error: cannot specify -o with -c, -S or -E with multiple files
-S:编译、只关联预处理文件产生的 .ii文件,若有其他.ii文件会报错
-c:汇编成目标代码(.o)只关联编译产生的 .s文件,若有其他.s文件会报错
链接:多文件编译时,需要main cpp文件中头文件对应的cpp文件(一般也将该文件编译成.o文件),若不包含会产生头文件中函数找不到的错误
gcc -E 源文件 -o *.ii 预处理/预编译, 使用 c 源程序文件,生成 *.ii 文件,.ii文件就是生成的预处理文件
gcc -S *.ii -o *.s 编译,使用 *.ii 文件,生成 *.s 文件,.s文件就是生成的汇编文件
gcc -c *.s -o *.o 汇编,使用 *.s 文件,生成 *.o 文件,.o就是字节码文件
gcc *.o -o *.exe 链接,将 *.o 文件与标准库链接,生成可执行文件exe
注意:一般直接使用 gcc/g++ *.c源程序文件,会自动执行 预编译、编译、汇编操作。
使用GCC直接编译源程序文件(不使用Makefile),并且源程序中包含自定义头文件,使用GCC编译时可通过 -I头文件路径 指定头文件的路径,存在多个头文件路径时使用 , 分隔,每个头文件路径前都加 -I 即可。
单个头文件:
$ gcc test.c I../inc -o test
包含多个目录:
$ gcc test.c -I../inc, -I../inc2 -o test
使用GCC编译时有一下选项可用:
gcc -g 生成调试信息。GNU 调试器可利用该信息,不加 -g 不可以调试
gcc -Wall 生成所有警告信息
gcc -w 不生成任何警告信息
gcc -c 只编译并生成目标文件
-shared :指定生成动态链接库。
-fPIC :产生位置无关的代码,当产生共享库的时候(动态库),应该创建位置无关的代码,这会让共享库使用任意的地址而不是固定的地址。
-static :指定生成静态链接库。
-L:指定要连接的库所在的目录。
-l:指定链接时需要的库名(动态/静态),编译器查找连接库时有隐含的命名规则:即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。
-I:(大写i)指定头文件的文件夹。
-Wall :生成所有警告信息。
-ggdb :此选项将尽可能的生成gdb的可以使用的调试信息。
-g :编译器在编译的时候产生调试信息。
-o:指定执行文件的名字;
参考: https://blog.csdn.net/Ethan_Novice/article/details/7192076
https://blog.csdn.net/liuxiao723846/article/details/97617548
源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。 而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码 (Linker Error),在VC下,这种错误一般是: Link 2001错误
,意思说是说,链接器未能找到 函数的实现。你需要指定函数的Object File。
将结果保存到指定文件/从指定文件中读取输入
运行编译后的exe文件,可以指定输入从ain.txt中读取,输出结果保存到aout.txt文件中.
xxx.exe <infile> outfile
Makefile文件编写
参考:https://seisman.github.io/how-to-write-makefile/overview.html
注意:
-
all: clean: 等所有标签行要顶格写
-
另外所有标签下的命令前必须使用一个Tab键
make是如何工作的
在默认的方式下,也就是我们只输入 make
命令。那么,
-
make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
-
如果找到,它会找文件中的第一个目标文件(target),写在第一行 :前的标记文件,并把这个文件作为最终的目标文件。
-
如果 目标文件 不存在,或是目标文件 :后面的 依赖文件1 的修改时间要比 目标文件 这个文件新,那么,他就会执行后面所定义的命令。
-
如果 目标文件 的 依赖文件1 也不存在,那么make会在当前文件中找目标为 依赖文件1 的 目标文件2,如果找到则再根据那一个规则生成 对应文件
-
如果目标文件和其依赖文件都存在,则就按照规则生成对应的最终 目标文件。
在Makefile中,规则的顺序是很重要的,因为,Makefile中应该只有一个最终目标,其它的目标都是被这 个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标 可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么 ,第一条规则中的第一个目标会成为最终的目标。make所完成的也就是这个目标。
伪目标
Makefile中的第一个目标会被作为其默认目标,对于伪目标 all,clean等,他们都有一个显式写法就是 .PHONY:clean 。
但是一般都直接使用 clean:(这是隐式写法,不推荐)。
下面是一个伪目标的实例。
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
我们声明了一个“all”的伪目标,all依赖于其它三个目标(prog1 prog2 prog3)。由于默认目标的特性是,总是被执行的,但由于“all”又是一个伪目标,伪目标只是一个标签不会生成文件,所以不会有“all”文件产生。于是,其它三个目标的规则总是会被执行。也就达到了我们一口气生成多个目标的目的。
静态模式
<targets ...> : <target-pattern> : <prereq-patterns ...>
<commands>
...
targets定义了一系列的目标文件,可以有通配符。targets是目标的一个集合。定义一个/多个目标文件,提前定义好多个文件。
target-parrtern是指明了targets的类型,也就是的targets目标集的类型。目标文件的集合,生成的目标文件是什么后缀/类型。
prereq-parrterns是目标的依赖模式,即生成目标需要依赖的文件类型。生成目标文件的依赖项的类型。
例如:目标文件A :%.o : %.c
意思是 目标文件A 依赖于 :后面的文件,其中%.o代表 后缀为.o的目标文件A的模式集合,%.c代表后缀为.c的文件。
%.o : %.c 代表 目标文件A后缀模式是 .o 的集合,生成 .o 文件的依赖文件就是 %.c。
生成目标文件时,使用 % 自动替换为同名文件。
使用静态模式时使用前就需要指定 生成目标文件是那个文件
例如下面使用静态模式,首先定义了生成目标是 foo.o和bar.o,在下面说明了目标文件的集合模式是 .o,而生成.o文件所依赖的文件.c文件。
其中 $< 代表第一个依赖文件,生成目标文件的第一依赖文件,$@ 代表目标文件就是 objects
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
使用静态模式实例
# 先指定生成exe目标文件
SRCS = ./src/class.exe ./src/Test.exe
SRCSC = ./src
BIN = ./bin
all : $(SRCS)
# 显式目标写法,伪目标一般建议都用显式写法
.PHONY : all
# $@ 目标文件就是$(SRCS)
$(SRCS) : %.exe: %.cpp
g++ $< -o $@
# 伪目标
.PHONY : clean
clean :
-rm -rf $(SRCSC)/*.o
-rm -rf $(SRCSC)/*.exe
-rm -rf $(BIN)/*.o
echo Clean Done
自动生成依赖项文件
gcc/g++ -MM 源程序文件.c
使用GNU的C/C++编译器,你得用 -MM
参数,不然, -M
参数会把一些标准库的头文件也包含进来。
Makefile的综合实例
# 源程序目录
PSRC = ./src
# 静态模式目标文件
SRCS = ./src/class.exe ./src/Test.exe ./src/class.o ./src/Test.o
# exe文件存储目录,一般不需要
BIN = ./bin
# 编译all标签
all : $(SRCS) temp
echo Complie Begin
# 显式目标写法,伪目标一般建议都用显式写法
.PHONY : all
# $@ 目标文件,一般就是指冒号前的标签,如果标签是一个目标文件则自动替换,
# 若标签是一个伪目标文件,则直接使用标签名为目标文件名
# $< 当前目标文件的第一个依赖文件
# $^ 当前目标文件的所有依赖文件
# 这里的$@就是$(SRCS)
# 使用filter只产生exe目标文件
$(filter %.exe,$(SRCS)) : %.exe: %.cpp
g++ $< -o $@
# 使用filter只产生.o目标文件
$(filter %.o,$(SRCS)) : %.o: %.cpp
g++ -c $< -o $@
# 不使用静态模式,直接生成目标文件
# 这里的$@就是temp 伪目标,会直接生成temp.exe目标文件
temp: $(PSRC)/*.c
# 编译指定源程序文件,生成目标文件到BIN目录下
gcc $^ -o $(BIN)/$@
# 编译所有依赖文件时只能有一个main()函数,否则编译无法通过
# 伪目标
.PHONY : clean
clean :
-rm -rf $(PSRC)/*.o
-rm -rf $(PSRC)/*.exe
echo CLean PSRC Done
-rm -rf $(BIN)/*.*
echo Clean BIN Done
Make Error 总结
1.使用make出现下述错误
$ make all
make: Nothing to be done for 'all'.
解决办法:这是因为源程序文件没有修改,之前已经编译过。若要重新编译先使用 make clean命令
2.预处理、编译、汇编是,使用多个文件
g++: fatal error: cannot specify -o with -c, -S or -E with multiple files
使用Makefile编译学生管理系统实例
1.Makefile
# 源码Path
PSRC = ./ManageStuSys.cpp ./ManageLink.cpp
# 依赖头文件目录
INC = ../inc/
# 生成的目标文件,
SRC = ./ManageStuSys.ii ./ManageStuSys.s ./ManageStuSys.o ./ManageStuSys.exe
BIN = ./StuSys
all : $(SRC) temp clean
# 预处理
# 生成.i 预处理,预处理将源码中的 宏展开、头文件复制进来
# $(filter 指定生成的文件, 生成的文件集合),从文件集合中筛选出指定的文件
# %.o: %.i : 前面是生成的文件,后面是依赖的文件
$(filter %.ii, $(SRC)) : %.ii: %.cpp
pwd
echo Preprocessing Begin
g++ -E $< -o $(BIN)/$@
echo Preprocessing product .ii
# 编译
# 生成.i
$(filter %.s, $(SRC)) : %.s: $(BIN)/%.ii
echo Compile Begin
g++ -S $< -o $(BIN)/$@
echo Compile product .s
# 生成.o
$(filter %.o, $(SRC)) : %.o: $(BIN)/%.s
echo Assembly Begin
g++ -c $< -o $(BIN)/$@
g++ -c ./ManageLink.cpp -o $(BIN)/ManageLink.o
echo Assembly product .o
# 生成exe
$(filter %.exe, $(SRC)) : %.exe: $(BIN)/*.o
echo Link Begin
g++ $^ -I $(INC) -o $(BIN)/$@
echo Link product .exe
# 直接生成exe
temp : $(PSRC)
g++ $(PSRC) -I $(INC) -o $(BIN)/$@
echo Compile End
clean :
-rm -rf $(BIN)/*.*
echo allfile Done
2. ManageStuSys.h
#ifndef _HManageStuSys
#define _HManageStuSys
// define student struct
typedef struct STUNODE
{
char stuName[20];
char stuID[20];
char stuGrade[20];
char stuClass[20];
char *stuInfo[4] = {stuName,stuID,stuGrade,stuClass};
double stuScore;
STUNODE *next;
}STU;
void* NodeInit();
void printNode(STU *);
void exitPrecent();//显示退出系统百分比
void addStuInfo(STU *, char *[] , double);
void saveStudInfo(STU *);//存储学生信息
void modifyStuSocre(STU *,char [],double);//修改学生信息
void serachStuInfo(STU *);//查询学生信息
void deleteStuInfo(STU *);//删除学生信息
void getStuQueue(STU *);//将学生排序
#endif
3.ManageStuSys.cpp
#include <iostream>
#include "../inc/ManageStuSys.h"
using namespace std;
// g++ -g ManageStuSys.cpp ManageLink.cpp -I ../inc -o ms
int main(){
// static int i = 1;
char name[20],id[20],Grade[20],Class[20];
double socer = 0.0;
STU *head = (STU*)NodeInit();
STU *NewNode = (STU*)NodeInit();
NewNode->stuInfo[0] = NewNode->stuName;
NewNode->stuInfo[1] = NewNode->stuID;
NewNode->stuInfo[2] = NewNode->stuGrade;
NewNode->stuInfo[3] = NewNode->stuClass;
cout <<"*******************欢迎使用GitW学生管理系统****************"<<endl;
cout <<"******************* 系统版本号:V0.0.1 ****************"<<endl;
cout <<"******************* 操作指南 ****************"<<endl;
cout <<"*******************1:添加学生信息*************************"<<endl;
cout <<"*******************2:修改成绩 *************************"<<endl;
cout <<"*******************3:查询学生信息*************************"<<endl;
cout <<"*******************4:查看学生成绩排名**********************"<<endl;
cout <<"*******************5:删除学生信息*************************"<<endl;
char MenuNumber;
while(true){
cout <<"请输入指令:";
cin >> MenuNumber;
switch (MenuNumber)
{
case 'q':
cout <<"*****即将退出系统*****"<< endl;
exitPrecent();
free(head);
free(NewNode);
exit(0);
case '1':
cout <<"*****请添加添加学生信息*****"<< endl;
cout <<"*****格式:姓名,学号,年级,班级,总成绩*****"<< endl;
cin >> NewNode->stuName >> NewNode->stuID >> NewNode->stuGrade
>> NewNode->stuClass >> NewNode->stuScore;
// if(NewNode->stuScore){
// cout <<"成绩输入有误请重新输入"<<endl;
// break;
// }
// cin >> NewNode->stuID;
// cin >> NewNode->stuGrade;
// cin >> NewNode->stuClass;
// cin >> NewNode->stuScore;
// g++ -g -Wall ManageStuSys.cpp ManageLink.cpp -I ../inc -o ms
addStuInfo(head, NewNode->stuInfo, NewNode->stuScore);
// printNode(NewNode);
saveStudInfo(NewNode);
break;
case '2':
cout <<"*****请输入修改的学生的学号、总成绩*****"<< endl;
cout <<"*****格式:学号,总成绩 *****"<< endl;
cin >> id >> socer;
modifyStuSocre(head,id,socer);
break;
case '3':
cout <<"*****请输入查询的学生姓名和学号*****"<< endl;
cout <<"*****格式:姓名,学号 *****"<< endl;
// cin >>>>>>;
serachStuInfo(head);
break;
case '4':
cout <<"*****你现在正在查看学生成绩排名*****"<< endl;
getStuQueue(head);
break;
case '5':
cout <<"*****请输入删除的学生姓名和学号*****"<< endl;
cout <<"*****格式:姓名,学号 *****"<< endl;
deleteStuInfo(head);
break;
case '6':
cout <<"*****功能开发中*****"<< endl;
break;
case '7':
cout <<"*****功能开发中*****"<< endl;
break;
case '8':
cout <<"*****功能开发中*****"<< endl;
break;
default:
cout <<"您的输入有误,请重新输入"<< endl;
break;
}
}
return 0;
}
4. ManageLink.cpp
#include <iostream>
#include <string>
#include <Windows.h>
#include <fstream>
#include "../inc/ManageStuSys.h"
// #include "ManageStuSys.h"
void* NodeInit(){
STU *head;
head = (STU*)malloc(sizeof(STU));
head->next = NULL;
return head;
}
//显示所有成绩
void printNode(STU *head){
while(head->next){
std::cout <<"姓名:"<< head->next->stuName
<< "\t学号:"<< head->next->stuID
<< "\t年级:"<<head->next->stuGrade
<< "\t班级:"<<head->next->stuClass
<<"\t总成绩:"<< head->next->stuScore << std::endl;
head = head->next;
}
}
void exitPrecent(){
int i = 0;
printf("正在退出系统...\n");
while(1){
// for(int i =0 ;i<101;i+=10){
for(int j = 0 ;j <i;j+=10){
printf("*");
}
for(int n = 0; n<10-i/10;n++){
printf("=");
}
if(i<101){
Sleep(50);
printf("\r%d%%",i);
}
else
{
printf("\n已退出系统");
break;
}
i+=10;
}
}
void addStuInfo(STU *head, char *stuInfo[], double stuScore){
STU *Temp = (STU*)NodeInit();
strcpy(Temp->stuName, stuInfo[0]);
strcpy(Temp->stuID, stuInfo[1]);
strcpy(Temp->stuGrade, stuInfo[2]);
strcpy(Temp->stuClass, stuInfo[3]);
Temp->stuScore = stuScore;
Temp->next = head->next;
head->next = Temp;
// free(Temp);
}
void saveStudInfo(STU *New){
std::ofstream out;
char str[100];
out.open("./stuinfo.txt",std::ofstream::app);
if(out.is_open()){
for(int i = 0;i < 4;i++){
strcpy(str, New->stuInfo[i]);
// std::cout << New->stuInfo[0] <<std::endl;
out << str;
if(i < 4)out << ",";
}
out << New->stuScore;
out << "\n";
}else{
std::cout <<"File Error"<<std::endl;
}
out.close();
}
void modifyStuSocre(STU *New, char id[], double socer){
while(New->next){
if(New->stuID == id){
New->stuScore = socer;
}
New = New->next;
}
std::cout <<"成绩已修改"<< std::endl;
std::cout <<"姓名:"<< New->stuName
<< "\t学号:"<< New->stuID
<< "\t年级:"<< New->stuGrade
<< "\t班级:"<< New->stuClass
<<"\t总成绩:"<< New->stuScore << std::endl;
}
void serachStuInfo(STU *New){
// printNode(New);
}
void deleteStuInfo(STU *New){
;
}
void getStuQueue(STU *New){
;
}
5. make结果
$ make
pwd
/h/CWorkSpace/GCCBuild/src
echo Preprocessing Begin
Preprocessing Begin
g++ -E ManageStuSys.cpp -o ./StuSys/ManageStuSys.ii
echo Preprocessing product .ii
Preprocessing product .ii
echo Compile Begin
Compile Begin
g++ -S StuSys/ManageStuSys.ii -o ./StuSys/ManageStuSys.s
echo Compile product .s
Compile product .s
echo Assembly Begin
Assembly Begin
g++ -c StuSys/ManageStuSys.s -o ./StuSys/ManageStuSys.o
g++ -c ./ManageLink.cpp -o ./StuSys/ManageLink.o
echo Assembly product .o
Assembly product .o
echo Link Begin
Link Begin
g++ StuSys/ManageStuSys.o StuSys/ManageLink.o -I ../inc/ -o ./StuSys/ManageStuSys.exe
echo Link product .exe
Link product .exe
g++ ./ManageStuSys.cpp ./ManageLink.cpp -I ../inc/ -o ./StuSys/temp
echo Compile End
Compile End
rm -rf ./StuSys/*.*
echo allfile Done
allfile Done