|
|
玩篮球的西装 · 操控瀏覽器歷史紀錄 - Web API | MDN· 2 月前 · |
|
|
爱跑步的花生 · VNG Careers· 5 月前 · |
|
|
腼腆的酱牛肉 · 中国综艺嘉年华项目落户青岛 ...· 5 月前 · |
|
|
狂野的荒野 · Client Challenge· 6 月前 · |
|
|
粗眉毛的桔子 · 关于小黑屋的一些看法。 ...· 1 年前 · |
首先编写c代码,并输入以下如图代码,生成c文件hello.c
-----------------------------------------------------------------------------
1、预编译(Preprocessing)命令:
会对各种预处理指令( #include、#define、#ifdef 等#开始的代码行 )进行处理,删除注释和多余的空白字符,生成一份新的代码。
gcc -o hello.i hello.c -E 或者 gcc -o hello.i -E hello.c 或者 gcc -E hello.c -o hello.i (注意:-o 作用是指定输出文件的名字,如果不加-o的话,则生成的文件名字总叫a.out。)
(即-o 参数:是对命令输出结果进行导入操作,这里是把 gcc -E hello.c 操作结果输出到文件hello.i(命名可以自定义)中进行保存。)
预编译hello.c,预编译之后得到的文件的名字叫hello.i。
(注意:编译后的名字可以随意起,但是呢我们知道预编译后的文件还是文本的.c文件,所以为了好区分起名字为 xxx.c,这里我们为了显示整个过程,预编译后的文件名我们起为xxx.i。)
在c语言中#开头的语句又叫预编译指令。例如:#include <stdio.h>
预编译的功能之一: 会把include包含的头文件内容做一个简单的替换,即替换到.c文件里面去。 ...... //此处省略1万行 ...... 预编译的功能之二: 会把代码中的注释去掉。
----------------------------------------------------------------------------- 2、编译(Compilation)的命令:
对代码进行语法、语义分析和错误判断,生成汇编代码文件。
gcc -o hello.s hello.i -S 或者 gcc -o hello.s -S hello.i 或者 gcc -S hello.i -o hello.s
(-S 参数:是gcc对目标文件进行编译,这里针对的是文件hello.i文件。)
通过这一步我们知道 C语言跟汇编的 关系,至于他们之前是如何进行转换的,大家可以进行更深入的学习与探讨。
此时目录下多了一个hello.s文件,内容如下图所示:
-----------------------------------------------------------------------------
3、汇编(Assembly)的命令:
把汇编代码转换成计算机可认识的二进制文件,即把文本的c语言编译为二进制指令。要知道计算机只认识0和1呢!
gcc -o hello.o hello.s -c 或者 gcc -o hello.o -c hello.s 或者 gcc -c hello.s -o hello.o
(-c 参数:是gcc对目标文件执行指令转换的操作。)
此步骤我们得到文件hello.o文件。
大家也同样打开文件(cat hello.o)查看一下,这个文件里面几乎没几个字符大家能看懂,这就对了,但大家可以通过这种方法将其转化为我们可读的形式:
root@iZ2zeeailqvwws5dcuivdbZ:~/2/01# readelf -a hello.o
----------------------------------------------------------------------------- 4、链接(Linking/Build)的命令:
通俗的讲就是把多个*.o文件合并成一个可执行文件,即二进制指令文件。
gcc -o hello hello.o 或者 gcc hello.o -o hello(注意:gcc没有单独的链接参数)
将系统库函数与hello.o进行链接(简言之合并),得到可执行的程序,该程序的名字叫hello。
root@iZ2zeeailqvwws5dcuivdbZ:~/2/01# gcc -o hello hello.o
这里我们就得到了一个可以直接在系统下执行的文件 hello。
我们也可以对这个文件进行readelf操作,也可以进行 二进制指令转汇编 的操作,如下图所示:
root@iZ2zeeailqvwws5dcuivdbZ:~/2/01# objdump -d hello
-----------------------------------------------------------------------------
5、程序运行
我们想知道在linux系统下到底链接来了什么库来呢?(即可执行程序需要用到什么库呢?) 使用命令 ldd hello 查看。
gcc -E -o a.e a.c
预编译a.c文件,生成的目标文件名为a.e
预编译就是将include包含的头文件内容替换到C文件中,同时删除代码中没用的注释
示例:
//main.c
#include <stdio.h>
#include "sum.h"
int main(){
int m = 6;
int n = 5;
int result = sum(m,n); printf("hello\n%\n",result);
//sum.h
int sum(int x,int y);
运行之后结果为:
第一步就是这样的啦!
2 . 编译
gcc -S -o a.s a.e
将a.e翻译成一个ASCII汇编语言文件a.s.
3 .链接
gcc -o a a.s
这行命令告诉gcc对源程序a.o进行链接,生成可执行程序a
gcc 没有任何参数,表示就是链接
GCC编译器的基本选项如下表:
|
类型 |
说明 |
|---|---|
|
-E |
预处理后即停止,不进行编译、汇编及连接 |
|
-S |
编译后即停止,不进行汇编及连接 |
|
-c |
编译或汇编源文件,但不进行连接 |
|
-o file |
指定输出文件file |
C语言的include头文件
头文件的注意事项:
Hello World!
#include <stdio.h>
int main()
printf("Hello World!\n");
return 0;
}
$ gcc a.c
# 生成 a.out
$ ./a.out
//声明
# include “max.c”
# 不声明,会发生警告信息
$ gcc max.c hello.c -o main.out
$ gcc hello.c
不经常变动的函数 生成
静态库
$ gcc -c max.c -o max.o
# hello.c 声明去掉
$ gcc max.o hello.c
# 可以将文件写为 头文件
$ gcc max.o min.o hello.c
# 注释
hello.out:max.o min.o hello.c
gcc max.o min.o hello.c -o hello.out
max.o:max.c
gcc -c max.c
min.o:min.c
gcc -c min.c
$ gcc -g main.c -o main.out
$ gdb ./main.out
#include <stdio.h>
#include "head.h"
annotation one
annotation two
extern int N;
int main(){
printf("build test N=%d\n",N);
printStr("abc");
getchar();
}
#ifndef HEAD_H
#define HEAD_H
int N=100;
void printStr(char *);
#endif
#include<stdio.h>
void printStr(char *str){
printf("%s\n",str);
}
处理关于 “#” 的指令
$ gcc -E a.c -o a.i
...省略部分代码
# 2 "a.c" 2
# 1 "head.h" 1
# 3 "head.h"
int N=100;
void printStr(char *);
# 3 "a.c" 2
printStr("abc");
getchar();
}
预编译结果解释
# linenum filename flags
分别对应行号、文件、标识。 flag对应的含义
extern "C
块
gcc -S a.i -o a.s
.file "a.c"
.globl _N
.data
.align 4
.long 100
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
.ascii "build test N=%d\12\0"
.ascii "abc\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB10:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
call ___main
movl _N, %eax
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _printf
movl $LC1, (%esp)
call _printStr
call _getchar
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
.cfi_endproc
LFE10:
.ident "GCC: (GNU) 5.3.0"
.def _printf; .scl 2; .type 32; .endef
.def _printStr; .scl 2; .type 32; .endef
.def _getchar; .scl 2; .type 32; .endef
gcc -c a.s -o a.o
使用到了C标准库的东西“printf”,但是编译过程只是把源文件翻译成二进制而已,这个二进制还不能直接执行,这个时候就需要做一个动作,将翻译成的二进制与需要用到库绑定在一块。 函数库一般分为静态库和动态库两种
gcc head.o a.o -o a.exe
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径 LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
说下生成静态库的方法:
ar cr libxxx.a file1.o file2.o
就是把file1.o和file2.o打包生成libxxx.a静态库
使用的时候
gcc test.c -L/path -lxxx -o test
动态库的话:
gcc -fPIC -shared file1.c -o libxxx.so
也可以分成两部来写:
gcc -fPIC file1.c -c //这一步生成file1.o
gcc -shared file1.o -o libtest.so
|
|
玩篮球的西装 · 操控瀏覽器歷史紀錄 - Web API | MDN 2 月前 |
|
|
爱跑步的花生 · VNG Careers 5 月前 |
|
|
狂野的荒野 · Client Challenge 6 月前 |