Unix 环境高级编程(一):开发环境
- 一、Unix操作系统
- 二、Linux操作系统
- 三、GNU编译工具(GCC)
- 1、简介
- 2、基本用法
- 3、文件后缀
- 4、构建过程
- 5、预处理指令
- 6、预定义宏
- 7、环境变量
- 四、静态库
- 1、简介
- 2、创建静态库
- 3、ar 指令
- 4、调用静态库
- 五、共享库
-
- 六、动态加载共享库
- 1、头文件
- 2、加载共享库
- 3、获取函数地址
- 4、卸载共享库
- 5、 获取错误信息
- 七、辅助工具
一、Unix操作系统
二、Linux操作系统
三、GNU编译工具(GCC)
1、简介
GCC是以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC的初衷是为GNU操作系统专门编写一款编译器,现已被大多数类Unix操作系统(如Linux、BSD、MacOS X等)采纳为标准的编译器,甚至在微软的Windows上也可以使用GCC。GCC支持多种计算机体系结构芯片,如x86、ARM、MIPS等,并已被移植到其他多种硬件平台。
GCC原名为GNU C语言编译器(GNU C Compiler),只能处理C语言。但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)。
2、基本用法
gcc [options] [filenames]
-c
-o
-x
-I
-E
-S
-g
-O
-v
-pedantic
-Wall
-Werror
3、文件后缀
.c
.h
.i
.s
.S
.o
.a
.so
4、构建过程
编辑 -> 预编译(预处理)-> 编译 -> 汇编 -> 链接
- 编辑(hello.c)
vim hello.c
- 预编译(hello.i)
gcc -E hello.c -o hello.i
- 编译(hello.s)
gcc -S hello.i
- 汇编(hello.o)
gcc -c hello.s
- 链接(hello)
gcc hello.o -o hello
5、预处理指令
#include
#include_next
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
##
#
#error
#warning
#pragma
#pragma GCC dependency <文件>
#pragma GCC poison <标识>
#pragma pack(1/2/4/8)
#line
For example:
- error.c
#include <stdio.h>
#if (VERSION < 1)
#error "Version is too low!"
#elif (VERSION > 4)
#warning "version is too high!"
#endif
int main(void)
{
printf("Version is :%d\n", VERSION);
return 0;
}
- line.c
#include <stdio.h>
int main(void)
{
printf("line is %d\n", __LINE__);
#line 100
printf("line is %d\n", __LINE__);
return 0;
}
- pragma.c
#include <stdio.h>
#pragma GCC dependency "tmp.c"
int main(void)
{
return 0;
}
#include <stdio.h>
#pragma GCC poison goto float
int main(void)
{
float a;
loop :
goto loop;
return 0;
}
#include <stdio.h>
#pragma pack(1)
struct S1
{
double d;
char c;
int i;
short h;
};
#pragma pack(4)
struct S2
{
double d;
char c;
int i;
short h;
};
#pragma pack(8)
struct S3
{
double d;
char c;
int i;
short h;
};
int main(void)
{
#pragma pack()
printf ("S1: %lu字节\n", sizeof (struct S1));
printf ("S2: %lu字节\n", sizeof (struct S2));
printf ("S3: %lu字节\n", sizeof (struct S3));
return 0;
}
6、预定义宏
__BASE_FILE__
__FILE__
__LINE__
__FUNCTION__
__func__
__DATE__
__TIME__
__INCLUDE_LEVEL__
__cplusplus
For example:
- print.h
#ifndef _PRINT_H
#define _PRINT_H
#include <stdio.h>
void print (void)
{
printf ("__BASE_FILE__ : %s\n", __BASE_FILE__);
printf ("__FILE__ : %s\n", __FILE__);
printf ("__LINE__ : %d\n", __LINE__);
printf ("__FUNCTION__ : %s\n", __FUNCTION__);
printf ("__func__ : %s\n", __func__);
printf ("__DATE__ : %s\n", __DATE__);
printf ("__TIME__ : %s\n", __TIME__);
printf ("__INCLUDE_LEVEL__ : %d\n", __INCLUDE_LEVEL__);
#ifdef __cplusplus
printf ("__cplusplus : %d\n", __cplusplus);
#endif
}
#endif
- predef.h
#ifndef _PREDEF_H
#define _PREDEF_H
#include "print.h"
#endif
- predef.c
#include "predef.h"
int main (void)
{
print ();
return 0;
}
7、环境变量
C_INCLUDE_PATH
CPATH
CPLUS_INCLUDE_PATH
LIBRARY_PATH
LD_LIBRARY_PATH
- 通过gcc的-I选项指定C/C++头文件的附加搜索路径
gcc calc.c cpath.c -I.
- 将当前目录作为C头文件附加搜索路径,添加到CPATH环境变量中
export CPATH=$CPATH:.
echo $CPATH
env | grep CPATH
- 也可以在/.bashrc或/.bash_profile,配置文件中写环境变量,永久有效
export CPATH=$CPATH:.
执行
# source ~/.bashrc 或 # source ~/.bash_profile
- 头文件的三种定位方式
a)#include “目录/xxx.h”
头文件路径发生变化,需要修改源程序
b)C_INCLUDE_PATH/CPATH=目录
同时构建多个工程,可能引发冲突
c)gcc -I目录
既不用改程序,也不会有冲突 - 头文件的作用
a)声明外部变量、函数和类
b)定义宏、类型别名和自定义类型
c)包含其它头文件
d)借助头文件卫士,防止因同一个头文件被多次包含,而引发重定义错 - 包含头文件时需要注意的问题
a)gcc的-I选项
指定头文件附加搜索路径
b)#include <…>
先找-I指定的目录,再找系统目录
c)#include “…”
先找-I指定的目录,再找当前目录,最后找系统目录
d)头文件的系统目录
/usr/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/5.4.0/include
四、静态库
1、简介
- 链接静态库是将库中的被调用代码复制到调用模块中
- 静态库占用空间非常大,不易修改但执行效率高
- 静态库的缺省扩展名是.a
2、创建静态库
- 编辑源代码
vim xxx.c/xxx.h
- 编译成目标文件
gcc -c xxx.c -o xxx.o
- 打包成静态库文件
ar -r libxxx.a xxx.o ...
3、ar 指令
ar指令:ar [选项] 静态库文件名 目标文件列表
-r
-q
-d
-t
-x
4、调用静态库
- 直接调用
gcc main.c libxxx.a
- 通过LIBRARY_PATH环境变量指定库路径
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmath (环境法)
- 通过gcc的-L选项指定库路径
unset LIBRARY_PATH
gcc main.c -lmath -L. (参数法)
- 一般化的方法:
gcc .c/.o -l<库名> -L<库路径>
- 运行
在可执行程序的链接阶段,已将所调用的函数的二进制代码,复制到可执行程序中,因此运行时不需要依赖静态库。
五、共享库
1、简介
- 链接共享库则只是在调用模块中,嵌入被调用代码在库中的(相对)地址
- 共享库占用空间小,易于修改但执行效率略低
- 共享库的缺省扩展名是.so
2、创建共享库
- 编辑源程序
vim xxx.x/xxx.h
- 编译成目标文件
gcc -c -fpic xxx.c
- 链接成共享库文件
gcc -shared xxx.o -o libxxx.so
- PIC (Position Independent Code)
位置无关代码。可执行程序加载它们时,可将其映射到其地址空间的任何位置。
-fPIC
-fpic
3、调用共享库
- 直接调用
gcc main.c libxxx.so
- 通过LIBRARY_PATH环境变量指定库路径
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmath (环境法)
- 通过gcc的-L选项指定库路径
unset LIBRARY_PATH
gcc main.c -lmath -L. (参数法)
- 一般化的方法
gcc .c/.o -l<库名> -L<库路径>
3、运行
运行时需要保证LD_LIBRARY_PATH,环境变量中包含共享库所在的路径。
在可执行程序的链接阶段,并不将所调用函数的二进制代码复制到可执行程序中。
而只是将该函数在共享库中的地址嵌入到可执行程序中,因此运行时需要依赖共享库。
gcc缺省链接共享库,可通过-static选项强制链接静态库。
六、动态加载共享库
1、头文件
#include <dlfcn.h>
2、加载共享库
void* dlopen (const char* filename, int flag);
3、获取函数地址
void* dlsym (void* handle,const char* symbol );
4、卸载共享库
int dlclose (void* handle);
5、 获取错误信息
char* dlerror (void);
七、辅助工具
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)