一。 gcc介绍
Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。
gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。
gcc编译器能将C、C++语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,
gcc将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执 行文件。
gcc则通过后缀来区别输入文件的类别,下面我们来介绍gcc所遵循的部分约定规则。
.c为后缀的文件,C语言源代码文件;
.a为后缀的文件,是由目标文件构成的档案库文件;
.C,.cc或.cxx 为后缀的文件,是C++源代码文件;
.h为后缀的文件,是程序所包含的头文件;
.i 为后缀的文件,是已经预处理过的C源代码文件;
.ii为后缀的文件,是已经预处理过的C++源代码文件;
.m为后缀的文件,是Objective-C源代码文件;
.o为后缀的文件,是编译后的目标文件;
.s为后缀的文件,是汇编语言源代码文件;
.S为后缀的文件,是经过预编译的汇编语言源代码文件。
1. GCC安装
gcc工具默认未安装未安装在centos系统中 需要通过yum安装
通过yum group 获取到开发工具组(有些系统可能是中文)
[root@cdh2 vc]# yum group list
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* epel: mirror01.idc.hinet.net
* extras: mirrors.aliyun.com
* updates: mirrors.aliyun.com
Available Environment Groups:
Minimal Install
Compute Node
Infrastructure Server
File and Print Server
Cinnamon Desktop
MATE Desktop
Basic Web Server
Virtualization Host
Server with GUI
GNOME Desktop
KDE Plasma Workspaces
Development and Creative Workstation
Installed Groups:
Development Tools
这里找到组的名字是 Development Tools
yum groupinstall 组名
安装完成后 执行gcc查看版本
[root@cdh2 vc]# gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
2. GCC编译c程序
任意目录新建 hello.c 编写代码(linux使用vi notepad++安装nppftp插件远程编辑)
#include
void main(){
printf("helloworld\n");
}
在linux上默认 <> 直接搜索系统/usr/include目录下的源文件 如果是当前项目编写头文件 需要使用 ""应用
比如 stdio.h
[root@cdh2 vc]# ls /usr/include | grep stdio.h
stdio.h
编写完成后执行命令 编译
gcc 源文件名称 -o 指定编译后生成的可执行文件名称 如果不指定默认输出为 a.out
[root@cdh2 vc]# gcc hello.c -o hello
[root@cdh2 vc]# ll
total 16
-rwxr-xr-x 1 root root 8511 Jun 6 19:55 hello
-rw-r--r-- 1 root root 61 Jun 6 19:40 hello.c
执行查看效果
[root@cdh2 vc]# ./hello
helloworld
3. GCC编译步骤
gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
[root@cdh2 vc]# gcc hello.o -o hello
[root@cdh2 vc]# ./hello
helloworld
二。gcc命令选项
以下内容拷贝自互联网 备查
选项 | 含义 |
---|---|
--help --target-help |
显示 gcc 帮助说明。‘target-help’是显示目标机器特定的命令行选项。 |
--version | 显示 gcc 版本号和版权信息 。 |
-ooutfile | 输出到指定的文件。 |
-xlanguage | 指明使用的编程语言。允许的语言包括:c c++ assembler none 。 ‘none’意味着恢复默认行为,即根据文件的扩展名猜测源文件的语言。 |
-v | 打印较多信息,显示编译器调用的程序。 |
-### | 与 -v 类似,但选项被引号括住,并且不执行命令。 |
-E | 仅作预处理,不进行编译、汇编和链接。如上图所示。 |
-S | 仅编译到汇编语言,不进行汇编和链接。如上图所示。 |
-c | 编译、汇编到目标代码,不进行链接。如上图所示。 |
-pipe | 使用管道代替临时文件。 |
-combine | 将多个源文件一次性传递给汇编器。 |
更多有用的GCC选项:
命令 | 描述 |
---|---|
-l library -llibrary |
进行链接时搜索名为library的库。 例子: $ gcc test.c -lm -o test |
-Idir | 把dir加入到搜索头文件的路径列表中。 例子: $ gcc test.c -I../inc -o test |
-Ldir | 把dir加入到搜索库文件的路径列表中。 例子: $ gcc -I/home/foo -L/home/foo -ltest test.c -o test |
-Dname | 预定义一个名为name的宏,值为1。 例子: $ gcc -DTEST_CONFIG test.c -o test |
-Dname=definition | 预定义名为name,值为definition的宏。 |
-ggdb -ggdblevel |
为调试器 gdb 生成调试信息。level可以为1,2,3,默认值为2。 |
-g -glevel |
生成操作系统本地格式的调试信息。-g 和 -ggdb 并不太相同, -g 会生成 gdb 之外的信息。level取值同上。 |
-s | 去除可执行文件中的符号表和重定位信息。用于减小可执行文件的大小。 |
-M | 告诉预处理器输出一个适合make的规则,用于描述各目标文件的依赖关系。对于每个 源文件,预处理器输出 一个make规则,该规则的目标项(target)是源文件对应的目标文件名,依赖项(dependency)是源文件中 `#include引用的所有文件。生成的规则可 以是单行,但如果太长,就用`\'-换行符续成多行。规则 显示在标准输出,不产生预处理过的C程序。 |
-C | 告诉预处理器不要丢弃注释。配合`-E'选项使用。 |
-P | 告诉预处理器不要产生`#line'命令。配合`-E'选项使用。 |
-static | 在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上 无效。 |
-nostdlib | 不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。 |
Warnings | |
-Wall | 会打开一些很有用的警告选项,建议编译时加此选项。 |
-W -Wextra |
打印一些额外的警告信息。 |
-w | 禁止显示所有警告信息。 |
-Wshadow | 当一个局部变量遮盖住了另一个局部变量,或者全局变量时,给出警告。很有用的选项,建议打开。 -Wall 并不会打开此项。 |
-Wpointer-arith | 对函数指针或者void *类型的指针进行算术操作时给出警告。也很有用。 -Wall 并不会打开此项。 |
-Wcast-qual | 当强制转化丢掉了类型修饰符时给出警告。 -Wall 并不会打开此项。 |
-Waggregate-return | 如果定义或调用了返回结构体或联合体的函数,编译器就发出警告。 |
-Winline | 无论是声明为 inline 或者是指定了-finline-functions 选项,如果某函数不能内联,编译器都将发出警告。如果你的代码含有很多 inline 函数的话,这是很有用的选项。 |
-Werror | 把警告当作错误。出现任何警告就放弃编译。 |
-Wunreachable-code | 如果编译器探测到永远不会执行到的代码,就给出警告。也是比较有用的选项。 |
-Wcast-align | 一旦某个指针类型强制转换导致目标所需的地址对齐增加时,编译器就发出警告。 |
-Wundef | 当一个没有定义的符号出现在 #if 中时,给出警告。 |
-Wredundant-decls | 如果在同一个可见域内某定义多次声明,编译器就发出警告,即使这些重复声明有效并且毫无差别。 |
Optimization | |
-O0 | 禁止编译器进行优化。默认为此项。 |
-O -O1 |
尝试优化编译时间和可执行文件大小。 |
-O2 | 更多的优化,会尝试几乎全部的优化功能,但不会进行“空间换时间”的优化方法。 |
-O3 | 在 -O2 的基础上再打开一些优化选项:-finline-functions, -funswitch-loops 和 -fgcse-after-reload 。 |
-Os | 对生成文件大小进行优化。它会打开 -O2 开的全部选项,除了会那些增加文件大小的。 |
-finline-functions | 把所有简单的函数内联进调用者。编译器会探索式地决定哪些函数足够简单,值得做这种内联。 |
-fstrict-aliasing | 施加最强的别名规则(aliasing rules)。 |
Standard | |
-ansi | 支持符合ANSI标准的C程序。这样就会关闭GNU C中某些不兼容ANSI C的特性。 |
-std=c89 -iso9899:1990 |
指明使用标准 ISO C90 作为标准来编译程序。 |
-std=c99 -std=iso9899:1999 |
指明使用标准 ISO C99 作为标准来编译程序。 |
-std=c++98 | 指明使用标准 C++98 作为标准来编译程序。 |
-std=gnu9x -std=gnu99 |
使用 ISO C99 再加上 GNU 的一些扩展。 |
-fno-asm | 不把asm, inline或typeof当作关键字,因此这些词可以用做标识符。用 __asm__, __inline__和__typeof__能够替代它们。 `-ansi' 隐含声明了`-fno-asm'。 |
-fgnu89-inline | 告诉编译器在 C99 模式下看到 inline 函数时使用传统的 GNU 句法。 |
C options | |
-fsigned-char -funsigned-char |
把char定义为有/无符号类型,如同signed char/unsigned char。 |
-traditional | 尝试支持传统C编译器的某些方面。详见GNU C手册。 |
-fno-builtin -fno-builtin-function |
不接受没有 __builtin_ 前缀的函数作为内建函数。 |
-trigraphs | 支持ANSI C的三联符( trigraphs)。`-ansi'选项隐含声明了此选项。 |
-fsigned-bitfields -funsigned-bitfields |
如果没有明确声明`signed'或`unsigned'修饰符,这些选项用来定义有符号位域或无符号位域。缺省情况下,位域是有符号的,因为它们继承的基本整数类型,如int,是有符号数。 |
-Wstrict-prototypes | 如果函数的声明或定义没有指出参数类型,编译器就发出警告。很有用的警告。 |
-Wmissing-prototypes | 如果没有预先声明就定义了全局函数,编译器就发出警告。即使函数定义自身提供了函数原形也会产生这个警告。这个选项 的目的是检查没有在头文件中声明的全局函数。 |
-Wnested-externs | 如果某extern声明出现在函数内部,编译器就发出警告。 |
C++ options | |
-ffor-scope | 从头开始执行程序,也允许进行重定向。 |
-fno-rtti | 关闭对 dynamic_cast 和 typeid 的支持。如果你不需要这些功能,关闭它会节省一些空间。 |
-Wctor-dtor-privacy | 当一个类没有用时给出警告。因为构造函数和析构函数会被当作私有的。 |
-Wnon-virtual-dtor | 当一个类有多态性,而又没有虚析构函数时,发出警告。-Wall会开启这个选项。 |
-Wreorder | 如果代码中的成员变量的初始化顺序和它们实际执行时初始化顺序不一致,给出警告。 |
-Wno-deprecated | 使用过时的特性时不要给出警告。 |
-Woverloaded-virtual | 如果函数的声明隐藏住了基类的虚函数,就给出警告。 |
Machine Dependent Options (Intel) | |
-mtune=cpu-type | 为指定类型的 CPU 生成代码。cpu-type可以是:i386,i486,i586,pentium,i686,pentium4 等等。 |
-msse -msse2 -mmmx -mno-sse -mno-sse2 -mno-mmx |
使用或者不使用MMX,SSE,SSE2指令。 |
-m32 -m64 |
生成32位/64位机器上的代码。 |
-mpush-args -mno-push-args |
(不)使用 push 指令来进行存储参数。默认是使用。 |
-mregparm=num | 当传递整数参数时,控制所使用寄存器的个数。 |
三。 gcc编译动态链接库
动态链接库:在UNIX环境中创建共享库 (shared library)。UNIX下,共享库以so为后缀(shared object)。共享库与Windows下的DLL类似,是在程序运行时动态连接。多个进程可以连接同一个共享库。
静态库:将库直接链接到执行文件中 使用静态库的应用程序会非常大
定义个add方法 一个头文件定义 一个实现类 编译成动态链接库
头文件 my.h
#include
#include
int add(int p1,int p2);
c文件 my.c
#include "my.h"
int add(int p1,int p2){
return p1+p2;
}
将该my.c编译成 so文件(注意生成文件命名规则 比如lib开头 .so结尾 中间my就是库名称 将来连接直接使用库名称)
gcc my.c -shared -fPIC -o libmy.so
添加一个main.c 提供调用该动态库的逻辑
#include "my.h"
void main(){
int number=add(111,333);
char str[25];
sprintf(str, "%d", number);
printf(str);
}
如果直接编译(不会自动连接so文件)
[root@localhost dso]# gcc main.c -o main
/tmp/ccebwBvM.o:在函数‘main’中:
main.c:(.text+0x13):对‘add’未定义的引用
collect2: 错误:ld 返回 1
正确编译 指定库的位置 同时指定应用库的名称
gcc main.c -L. -lmy -o main
编译成功 使用 ./main运行 发现报错
[root@localhost dso]# ./main
./main: error while loading shared libraries: libmy.so: cannot open shared object file: No such file or directory
虽然编译时指定了库的路径和名称 但是运行时 默认只会到 /lib /lib64 /usr/lib /usr/share/lib等目录下 或者是
LD_LIBRARY_PATH 环境变量下指定的目录中找
查看 main的依赖
[root@localhost dso]# ldd main
linux-vdso.so.1 => (0x00007ffdd0670000)
libmy.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fe387a01000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe387dce000)
将libmy.so拷贝到/lib64目录下
[root@localhost dso]# cp ./libmy.so /lib64
[root@localhost dso]# ldd ./main
linux-vdso.so.1 => (0x00007ffd7cd49000)
libmy.so => /lib64/libmy.so (0x00007f67e1186000)
libc.so.6 => /lib64/libc.so.6 (0x00007f67e0db9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f67e1388000)
[root@localhost dso]# ./main
444