编译器背后的故事
练习gcc生成静态库和动态库
1、使用vim生成三个文件
hello.c
main.c
hello.h
2、使用gcc编译hello.c生成hello.o文件
3、创建hello.o 文件的静态库
4、使用静态库
- 方法一
# gcc -o hello main.c -L. –lmyhello
自定义的库时,main.c 还可放在-L.和 –lmyhello 之间,但是不能放在它俩之后,否则会提 示 myhello 没定义,但是是系统的库时,如 g++ -o main(-L/usr/lib) -lpthread main.cpp 就不出错。
- 方法二
#gcc main.c libmyhello.a -o hello
- 方法三
先生成 main.o:gcc -c main.c
再生成可执行文件:gcc -o hello main.o libmyhello.a
动态库连接时也可以这样做
删除静态库文件后依然可以运行
5、由.o 文件创建动态库文件。
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其 文件扩展名为.so。用 gcc 来创建动态库。 在系统提示符下键入以下命令得到动态库文件 libmyhello.so。
#gcc -shared -fPIC -o libmyhello.so hello.o
(-o 不可少)
在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含 这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。我 们先运行 gcc 命令生成目标文件,再运行它看看结果。
# gcc -o hello main.c -L. -lmyhello
(或 #gcc main.c libmyhello.so -o hello 不会出错(没有 libmyhello.so 的话,会出错),但是 接下来./hello 会提示出错,因为虽然连接时用的是当前目录的动态库,但是运行时,是到 /usr/lib 中找库文件的,将文件 libmyhello.so 复制到目录/usr/lib 中就 OK 了)
快看看错误提示,原来是找不到动态库文件 libmyhello.so。程序在运行时, 会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提 示类似上述错误而终止程序运行。我们将文件 libmyhello.so 复制到目录/usr/lib 中,再试 试。
mv libmyhello.so /usr/lib
如果不行我们加入sudo权限
我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的 gcc 命令完全一样, 那当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?抱着对问题必究到底的心情, 来试试看。
先删除除.c 和.h 外的所有文件,恢复成我们刚刚编辑完举例程序状态。
通过上述最后一条 ls 命令,可以发现静态库文件 libmyhello.a 和动态库文件 libmyhello.s o 都已经生成,并都在当前目录中。然后,我们运行 gcc 命令来使用函数库 myhello 生成目 标文件 hello,并运行程序 hello。
gcc -o hello main.c -L. –lmyhello
(动态库和静态库同时存在时,优先使用动态库,当然,如果直接 gcc main.c libmyhello.a -o hello
的话,就是指定为静态库了)
运行的结果中很容易知道,当静态库和动态库同名时,gcc 命令将优先使用动 态库,默认去连/usr/lib 和/lib 等目录中的动态库,将文件 libmyhello.so 复制到目录/usr/lib 中即可
自己编写函数实现静态库和动态库
1、写入需要的.c文件
main.c
sub1.c
sub2.c
sub.h
2、使用gcc分别编译sub1.c和sub2.c生成sub1.o和sub2.o
静态库
1、生成静态库文件
2、键入以下语句生成可执行文件并运行
gcc -o main main.c libmysub.a
3、 静态库文件大小
动态库
1、生成动态库文件
必须先键入gcc -c -fpic sub1.c sub2.c
不然在生成动态库时会报错
生成动态库语句:gcc -shared -o libmysub.so sub1.o sub2.o
2、将main.c与动态库链接并运行发现报错因为在默认目录下找不到所创建的动态库文件
将动态库移动的默认目录下并运行
3、动态库文件大小
gcc编译工具集中各软件的用途
gcc常用命令
1、生成一个示例文本
2、一步到位的编译指令
gcc test.c -o test
直接生成可执行程序
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译 (Compilation)、汇编 (Assembly)和连接(Linking)。
①预处理
gcc -E test.c -o test.i
可以输出 test.i 文件中存放着 test.c 经预处理之后的代码。打开 test.i 文件,看一看,就明白了。后 面那条指令,是直接在命令行窗口中输出预处理后的代码.
②预处理之后,可直接对生成的 test.i 文件编译,生成汇编代码: gcc -S test.i -o test.s
gcc 的-S 选项,表示在程序编译期间,在生成汇编代码后,停止,-o 输出汇编代码文件
③对于上一小节中生成的汇编代码文件 test.s,gas 汇编器负责将其编译为目标文件,如下:
gcc -c test.s -o test.o
④gcc 连接器是 gas 提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生 成可执行文件。附加的目标文件包括静态连接库和动态连接库。 对于上一小节中生成的 test.o,将其与C标准输入输出库进行连接,最终生成程序 test
gcc test.o -o test
在ubuntu中下载安装nasm,对示例代码“hello.asm”编译生成可执行程序,并与“hello world”C代码的编译生成的程序大小进行对比。
1、安装nasm
2、创建一个hello.asm文件使用nasm对hello.asm编译生成hello.o文件,使用ld -s -o hello hello.o
生成一个可执行文件hello,并执行
3、与c语言所生成程序作比较
其他库函数
1、 curse的基本常用函数
initscr(): initscr() 是一般 curses 程式必须先呼叫的函数, 一但这个函数被呼叫之後, 系统将根据终端机的形态并启动 curses 模式.
endwin(): curses 通常以呼叫 endwin() 来结束程式. endwin() 可用来关闭curses 模式, 或是暂时的跳离 curses 模式. 如果您在程式中须要call shell ( 如呼叫 system() 函式 ) 或是需要做 system call, 就必须先以 endwin() 暂时跳离 curses 模式. 最後再以wrefresh() doupdate() 来重返 curses 模式.
cbreak() and nocbreak(): 当 cbreak 模式被开启後, 除了 DELETE 或 CTRL 等仍被视为特殊控制字元外一切输入的字元将立刻被一一读取.当处於 nocbreak 模式时, 从键盘输入的字元将被储存在 buffer 里直到输入 RETURN或 NEWLINE.在较旧版的 curses 须呼叫 crmode(),nocrmode() 来取代 cbreak(),nocbreak()
nl() and nonl(): 用来决定当输入资料时, 按下 RETURN 键是否被对应为 NEWLINE 字元 ( 如 /n ). 而输出资料时, NEWLINE 字元是否被对应为 RETURN 和 LINDFEED系统预设是开启的.
echo() and noecho(): 此函式用来控制从键盘输入字元时是否将字元显示在终端机上.系统预设是开启的.
intrflush(win,bf): 呼叫 intrflush 时须传入两个值, win 为一 WINDOW 型态指标, 通常传入标准输出入萤幕 stdscr. bf 为 TRUE 或 FALSE. 当 bf 为 true 时, 当输入中断字元 ( 如 break) 时, 中断的反应将较为快速.但可能会造成萤幕的错乱.
keypad(win,bf): 呼叫 keypad 时须传入两个值, win 为一 WINDOW 型态指标, 通常传入标准输出入萤幕 stdscr. bf 为 TRUE 或 FALSE. 当开启 keypad 後, 可以使用键盘上的一些特殊字元, 如上下左右>等方向键, curses 会将这些特殊字元转换成 curses.h 内定义的一些特殊键. 这些定义的特殊键通常以 KEY_ 开头.
refresh(): refresh() 为 curses 最常呼叫的一个函式. curses 为了使萤幕输出入达最佳化, 当您呼叫萤幕输出函式企图改变萤幕上的画面时, curses 并不会立刻对萤幕做改变, 而是等到refresh() 呼叫後, 才将刚才所做的变动一次完成. 其馀的资料将维持不变. 以尽可能送最少的字元至萤幕上. 减少萤幕重绘的时间.如果是 initscr() 後第一次呼叫 refresh(), curses 将做清除萤幕的工作.
清除屏幕:
int erase(void);//在屏幕的每个位置写上空白字符
int clear(void);//使用一个终端命令来清除整个屏幕,相当于vi内的Ctrl+L
//内部调用了clearok来执行清屏操作,(在下次调用refresh时可以重现屏幕原文)
int clrtobot(void);//清除光标位置到屏幕结尾的内容
int clrtoeol(void);//清除光标位置到该行行尾的内容
移动光标:
int move(int new_y, int new_x); //移动stdcsr的光标位置
int leaveok(WINDOW *window_ptr,bool leave_flag);
//设置一个标志,用于控制在屏幕刷新后curses将物理光标放置的位置。
窗口移动和更新屏幕:
int mvwin(WINDOW *win, int new_y, int new_x); //移动窗口
int wrefresh(WINDOW *win);
int wclear(WINDOW *win);
int werase(WINDOW *win);
//类似于上面的refresh, clear, erase,但是此时针对特定窗口操作,而不是stdcur
int touchwin(WINDOW *win); //指定该窗口内容已改变、
//下次wrefresh时,需重绘窗口。利用该函数,安排要显示的窗口
int scrollok(WINDOW *win, bool flag); //指定是否允许窗口卷屏
int scroll(WINDOW *win); //把窗口内容上卷一行
2、安装curse库函数
3、curses.h所在目录
4、终端游戏俄罗斯方块
sudo apt-get install bastet//安装俄罗斯方块
bastet//启动游戏