目录
一、Linux 编译器:gcc/g++ 的使用
1.1、知识拓展:
1、在 Linux 系统中,不仅可以编写 C 和 C++ 的程序,还可以编写 Python,PHP,Shell,Java 的程序、
2、函数库一般分为静态库和动态库,在基础 IO 部分会讲解如何去制作静态库和动态库,以及如何去使用他们、
3、程序(代码)本质上是文本的形式,在经过预处理,编译,汇编之后将会转化为机器语言(二进制指令),转换为机器语言(二进制指令)的原因是因为,计算机只认识二进制,那么计算机为什么只认识二进制呢,这是因为,本质上并不是计算机只认识二进制,而是计算机中的各种硬件/组件只认识二进制,计算机在最初被设计时,无论是CPU,磁盘,触发器还是内存等硬件/组件,他们在设计的时候只是用来存储两态信息,其实,除了二进制计算机外,还存在其他进制的计算机,但是二进制计算机比较主流,因为二进制计算机更简单,二进制在硬件电路设计上比较简单,所以,最好也要将程序(代码)转换为二进制指令(机器语言),使得两者统一起来,从而更加方便的使用、
4、gcc 编译器和 g++ 编译器的编译过程和选项是完全一样的、
[HJM@hjmlcc ~]$ whoami
HJM
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp
[HJM@hjmlcc ~]$ vim lcc.c // vim编辑器不会根据文件名的后缀来区分文件的类型、
[HJM@hjmlcc ~]$ cat lcc.c
#include<stdio.h>
int main()
{
printf("%d\n",100);
return 0;
}
[HJM@hjmlcc ~]$ gcc lcc.c
//C语言代码需要使用gcc编译器来编译,gcc编译器会根据文件名的后缀来区分文件的类型、
[HJM@hjmlcc ~]$ ls
a.out lcc.c lcc.cpp // Linux 系统下默认生成的可执行程序的名字为:a.out、
[HJM@hjmlcc ~]$ ./a.out
100
[HJM@hjmlcc ~]$ vim lcc.cpp // vim编辑器不会根据文件名的后缀来区分文件的类型、
[HJM@hjmlcc ~]$ cat lcc.cpp
#include<iostream>
using namespace std;
int main()
{
cout<<"hello,c++\n"<<endl;
return 0;
}
[HJM@hjmlcc ~]$ g++ lcc.cpp
//C++代码需要使用g++编译器来编译,g++编译器会根据文件名的后缀来区分文件的类型、
[HJM@hjmlcc ~]$ ls
a.out lcc.c lcc.cpp
[HJM@hjmlcc ~]$ ./a.out
hello,c++
[HJM@hjmlcc ~]$
//注意1:
//若root用户和普通用户均未安装g++编译器,可以通过sudo的方法进行安装,也可以切换到root用户,输入指令:yum install -y gcc-c++ 的方法来安装、
//注意2:
//gcc编译器只能用来编译C语言代码,而g++编译器不仅可以编译C++代码,也可以编译C语言代码,因为C++兼容C语言、
[HJM@hjmlcc ~]$ whoami
HJM
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp
[HJM@hjmlcc ~]$ vim lcc.c
[HJM@hjmlcc ~]$ cat lcc.c
#include<stdio.h>
int main()
{
printf("%d\n",100); //预处理指令是以#号开头的代码行、
return 0;
}
[HJM@hjmlcc ~]$ gcc lcc.c -o mylcc
//Linux系统下默认生成的可执行程序的名字为:a.out,而上述指令代表:不使用Linux系统下默认生成的可
//执行程序名字:a.out,而是指定生成的可执行程序名字为:mylcc、
lcc.c lcc.cpp mylcc
[HJM@hjmlcc ~]$ gcc -E lcc.c -o lcc.i
//其中:
//-E:从当前开始,当预处理阶段完成,就停止、
//-o lcc.i:把预处理得到的结果写到临时普通文件lcc.i中、
//不管是在Linux还是Wiondows中,当使用gcc或g++编译器编译完代码后,源代码不受任何影响、
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i mylcc
[HJM@hjmlcc ~]$ vim lcc.c
//如下图所示:
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i mylcc
[HJM@hjmlcc ~]$ cat lcc.c
#include<stdio.h>
#define M 10
#define DEBUG //定义宏的时候不一定非要赋值、
int main()
{
printf("%d\n",100);
printf("%d\n",M);
//printf("%d\n",M);
//printf("%d\n",M);
//printf("%d\n",M);
#ifdef DEBUG
printf("hello, debug\n");
#else
printf("hello,release\n");
#endif
return 0;
}
[HJM@hjmlcc ~]$ gcc -E lcc.c -o lcc.i
[HJM@hjmlcc ~]$ vim lcc.c
如上图所示,在分屏状态下,若普通文件 lcc.i 和普通文件 lcc.c 均处于命令模式下,ctrl+快速两次w,便可实现在两个屏幕之间的切换、
[HJM@hjmlcc ~]$ ls /usr/include
//在Linux系统中,C语言的头文件库(C语言自带的头文件)基本上都在这个路径下,不排除它们将来会安装
//在其他路径下,具体在后面进行阐述、
aio.h dlfcn.h gconv.h krad.h mcheck.h nfs python2.7 stdint.h ucs
aliases.h drm gelf.h krb5 mellanox nlist.h python3.6m stdio_ext.h uct
alloca.h dwarf.h getopt.h krb5.h memory.h nl_types.h rdma stdio.h ulimit.h
a.out.h elf.h gio-unix-2.0 langinfo.h mft nss.h re_comp.h stdlib.h unistd.h
argp.h elfutils glib-2.0 lastlog.h misc numacompat1.h regex.h string.h ustat.h
argz.h endian.h glob.h libdb mntent.h numa.h regexp.h strings.h utime.h
ar.h envz.h gnu libelf.h monetary.h numaif.h resolv.h sys utmp.h
arpa err.h gnu-versions.h libgen.h mqueue.h obstack.h rpc syscall.h utmpx.h
asm errno.h grp.h libintl.h mstflint openssl rpcsvc sysexits.h valgrind
asm-generic error.h gshadow.h libio.h mtcr_ul paths.h sched.h syslog.h values.h
assert.h et gssapi libiptc mtd pcrecpparg.h scsi systemd verto.h
bits execinfo.h gssapi.h libipulog net pcrecpp.h search.h tar.h verto-module.h
byteswap.h fcntl.h gssrpc libmnl netash pcre.h selinux termio.h video
c++ features.h iconv.h libnl3 netatalk pcreposix.h semaphore.h termios.h wait.h
com_err.h fenv.h ieee754.h libudev.h netax25 pcre_scanner.h sepol tgmath.h wchar.h
complex.h FlexLexer.h ifaddrs.h limits.h netdb.h pcre_stringpiece.h setjmp.h thread_db.h wctype.h
cpio.h fmtmsg.h inttypes.h link.h neteconet poll.h sgtty.h time.h wordexp.h
cpufreq.h fnmatch.h ip6tables.h linux netinet printf.h shadow.h ttyent.h xen
crypt.h fpu_control.h iptables locale.h netipx profile.h signal.h uapi xlocale.h
ctype.h fstab.h iptables.h lzma netiucv protocols sound uchar.h xtables.h
db_185.h fts.h kadm5 lzma.h netpacket pthread.h spawn.h ucm xtables-version.h
db.h ftw.h kdb.h malloc.h netrom pty.h stab.h ucontext.h zconf.h
dirent.h _G_config.h keyutils.h math.h netrose pwd.h stdc-predef.h ucp zlib.h
[HJM@hjmlcc ~]$
//任何一款编译器,其内部都必须能通过一定的方式知道所包含的头文件所在的路径,而在Linux系统下,
//gcc编译器默认有自己的头文件(C语言自带的头文件)搜索路径,即: /usr/include 和 当前路径下(一
//般用来搜索我们自定义的头文件)、
语言特性越新,老的编译器就无法支持,本质原因并不是老的编译器不支持,而是老的编译器缺少部分配套的头文件和库、
[HJM@hjmlcc ~]$ whoami
HJM
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i mylcc
[HJM@hjmlcc ~]$ gcc -S lcc.i -o lcc.s //也可以写成:gcc -S lcc.c -o lcc.s ,但最好不要这样写、
//其中:
//-S:从当前开始,当编译阶段完成,就停止、
//-o lcc.s:把编译得到的结果写到临时普通文件lcc.s中,一般来说,后缀为.s或.asm的文件(普通文件)一
//般用来存放汇编语言、
//不管是在Linux还是Wiondows中,当使用gcc或g++编译器编译完代码后,源代码不受任何影响、
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i lcc.s mylcc
[HJM@hjmlcc ~]$ vim lcc.s
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i lcc.s mylcc
[HJM@hjmlcc ~]$ gcc -c lcc.s -o lcc.o
//其中:
//-c:从当前开始,当汇编阶段完成,就停止、
//-o lcc.o:把汇编得到的结果写到临时普通文件lcc.o中、
//不管是在Linux还是Wiondows中,当使用gcc或g++编译器编译完代码后,源代码不受任何影响、
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc
[HJM@hjmlcc ~]$ vim lcc.o
[HJM@hjmlcc ~]$ od lcc.o //二进制文件(普通)查看工具、
0000000 042577 043114 000402 000001 000000 000000 000000 000000
0000020 000001 000076 000001 000000 000000 000000 000000 000000
0000040 000000 000000 000000 000000 001500 000000 000000 000000
0000060 000000 000000 000100 000000 000000 000100 000015 000014
0000100 044125 162611 062276 000000 137400 000000 000000 000270
0000120 000000 164000 000000 000000 005276 000000 137400 000000
...
...
0003020 000000 000000 000000 000000 001030 000000 000000 000000
0003040 000030 000000 000000 000000 000000 000000 000000 000000
0003060 000001 000000 000000 000000 000000 000000 000000 000000
0003100 000021 000000 000003 000000 000000 000000 000000 000000
0003120 000000 000000 000000 000000 001330 000000 000000 000000
0003140 000141 000000 000000 000000 000000 000000 000000 000000
0003160 000001 000000 000000 000000 000000 000000 000000 000000
0003200
[HJM@hjmlcc ~]$
//二进制普通文件,即lcc.o目标文件并不是可执行程序,没有办法运行,因为在源文件lcc.c中,我们使用了
//库函数,但是截至目前,虽然我们包含了对应的头文件,只是对使用的这些库函数进行了声明,但是我们还
//找不到使用的这些库函数的定义,即:生成的二进制普通目标文件还未与链接库(C标准库,主要指C标准动
//态库)关联(链接)起来、
[HJM@hjmlcc ~]$ clear
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc
[HJM@hjmlcc ~]$ ./lcc.o
-bash: ./lcc.o: Permission denied
[HJM@hjmlcc ~]$ ll
total 48
-rw-rw-r-- 1 HJM HJM 309 Nov 2 10:15 lcc.c
-rw-rw-r-- 1 HJM HJM 96 Nov 2 10:33 lcc.cpp
-rw-rw-r-- 1 HJM HJM 16921 Nov 2 10:15 lcc.i
-rw-rw-r-- 1 HJM HJM 1664 Nov 2 14:53 lcc.o
-rw-rw-r-- 1 HJM HJM 594 Nov 2 10:49 lcc.s
-rwxrwxr-x 1 HJM HJM 8360 Nov 2 09:24 mylcc
[HJM@hjmlcc ~]$ chmod u+x lcc.o
[HJM@hjmlcc ~]$ ll
total 48
-rw-rw-r-- 1 HJM HJM 309 Nov 2 10:15 lcc.c
-rw-rw-r-- 1 HJM HJM 96 Nov 2 10:33 lcc.cpp
-rw-rw-r-- 1 HJM HJM 16921 Nov 2 10:15 lcc.i
-rwxrw-r-- 1 HJM HJM 1664 Nov 2 14:53 lcc.o
-rw-rw-r-- 1 HJM HJM 594 Nov 2 10:49 lcc.s
-rwxrwxr-x 1 HJM HJM 8360 Nov 2 09:24 mylcc
[HJM@hjmlcc ~]$ ./lcc.o
-bash: ./lcc.o: cannot execute binary file //不是可执行文件(普通文件)、
[HJM@hjmlcc ~]$ ldd lcc.o
not a dynamic executable
[HJM@hjmlcc ~]$ chmod u-x lcc.o
[HJM@hjmlcc ~]$ ll
total 48
-rw-rw-r-- 1 HJM HJM 309 Nov 2 10:15 lcc.c
-rw-rw-r-- 1 HJM HJM 96 Nov 2 10:33 lcc.cpp
-rw-rw-r-- 1 HJM HJM 16921 Nov 2 10:15 lcc.i
-rw-rw-r-- 1 HJM HJM 1664 Nov 2 14:53 lcc.o
-rw-rw-r-- 1 HJM HJM 594 Nov 2 10:49 lcc.s
-rwxrwxr-x 1 HJM HJM 8360 Nov 2 09:24 mylcc
[HJM@hjmlcc ~]$
//拓展:
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ll
total 76
-rwxrwxr-x 1 HJM HJM 8304 Nov 2 15:08 a.out
-rw-rw-r-- 1 HJM HJM 27 Nov 2 15:08 hjm.c
-rw-rw-r-- 1 HJM HJM 151 Nov 2 15:36 hjm.i
-rw-rw-r-- 1 HJM HJM 1232 Nov 2 15:37 hjm.o
-rw-rw-r-- 1 HJM HJM 370 Nov 2 15:36 hjm.s
-rw-rw-r-- 1 HJM HJM 309 Nov 2 10:15 lcc.c
-rw-rw-r-- 1 HJM HJM 96 Nov 2 10:33 lcc.cpp
-rw-rw-r-- 1 HJM HJM 16921 Nov 2 10:15 lcc.i
-rw-rw-r-- 1 HJM HJM 1664 Nov 2 14:53 lcc.o
-rw-rw-r-- 1 HJM HJM 594 Nov 2 10:49 lcc.s
-rwxrwxr-x 1 HJM HJM 8360 Nov 2 09:24 mylcc
[HJM@hjmlcc ~]$ cat hjm.c
//假设下面是C语言代码,此时可以不包含任何头文件,也可以不包含头文件<stdio.h>,因为我们未使用C语
//言的任何库函数,所以我们不需要对这些库函数包含头文件<stdio.h>来进行声明、
int main()
{
return 0;
}
//由上可知我们并没有使用C语言的任何库函数,所以我们也不需要找到关于任何C语言库函数的实现,按道
//理说,当我们生成的二进制普通目标文件即使不与C标准库(主要指C标准动态库)进行链接也是可以的,直
//接运行 ./hjm.o 应该也是可以的,但是发现会报错说是: hjm.o普通文件不是可执行程序,这是因为,当
//我们生成的二进制普通目标文件与C标准库(主要指C标准动态库)进行链接时,并不只是为了C标准库(主要
//指C标准动态库)中有关C语言库函数的定义,除了这一方面外,还会进行其他的一些操作,所以,即使我们
//不需要找到有关C语言的任何库函数的定义,我们也必须让二进制普通目标文件与C标准库(主要是指C标
//准动态库)进行链接才可以,由此可知,当我们写了一段C语言代码,不管该代码中是否使用了C语言的任何
//库函数,其生成的二进制普通目标文件都必须要与C标准库(C标准动态库)进行链接,只有二进制普通目标
//文件是无法直接运行的,只要不是可执行普通文件,则都会报错说是:cannot execute binary file、
[HJM@hjmlcc ~]$ ./hjm.o
-bash: ./hjm.o: Permission denied
[HJM@hjmlcc ~]$ chmod u+x hjm.o
[HJM@hjmlcc ~]$ ./hjm.o
-bash: ./hjm.o: cannot execute binary file
[HJM@hjmlcc ~]$ chmod u-x hjm.o
[HJM@hjmlcc ~]$ ll
total 76
-rwxrwxr-x 1 HJM HJM 8304 Nov 2 15:08 a.out
-rw-rw-r-- 1 HJM HJM 27 Nov 2 15:08 hjm.c
-rw-rw-r-- 1 HJM HJM 151 Nov 2 15:36 hjm.i
-rw-rw-r-- 1 HJM HJM 1232 Nov 2 15:37 hjm.o
-rw-rw-r-- 1 HJM HJM 370 Nov 2 15:36 hjm.s
-rw-rw-r-- 1 HJM HJM 309 Nov 2 10:15 lcc.c
-rw-rw-r-- 1 HJM HJM 96 Nov 2 10:33 lcc.cpp
-rw-rw-r-- 1 HJM HJM 16921 Nov 2 10:15 lcc.i
-rw-rw-r-- 1 HJM HJM 1664 Nov 2 14:53 lcc.o
-rw-rw-r-- 1 HJM HJM 594 Nov 2 10:49 lcc.s
-rwxrwxr-x 1 HJM HJM 8360 Nov 2 09:24 mylcc
[HJM@hjmlcc ~]$
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc
[HJM@hjmlcc ~]$ touch hjm.c
[HJM@hjmlcc ~]$ vim hjm.c
[HJM@hjmlcc ~]$ cat hjm.c
//包含头文件这一操作并不是必须的,所有的包含头文件的操作,本质上是因为想使用头文件所声明的方法,
//即能够让我们写的代码中能够使用所包含的头文件中所声明的方法,我们在写C语言代码时,经常包含头
//文件:#include<stdio.h>,这是因为,我们经常使用库函数:scanf,printf等等,都是库函数,都需要我
//们将<stdio.h>头文件包含进来用来对这些库函数进行声明,但是除了包含对应的头文件外还不够,头文
//件只是用来对这些库函数进行声明的,我们还需要找到这些库函数的实现,所以我们还需要这些库函数的
//链接库(C标准库,主要指C标准动态库),若只针对于C语言而言,链接库就是所谓的C标准库(主要指:C标准
//动态库)、
//如果我们在写C语言代码时,用不到<stdio.h>头文件中所声明的方法,那我们就可以不包含这个头文件,
//此时也能够成功编译、
int main()
{
return 0;
}
[HJM@hjmlcc ~]$ gcc hjm.c //可以成功编译、
[HJM@hjmlcc ~]$ ls
a.out hjm.c lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc
[HJM@hjmlcc ~]$ ./a.out //可以成功运行、
[HJM@hjmlcc ~]$
//注意:要指定lib64平台,不同的平台会有一定的差别、
//C标准库所在的路径如下所示,主要是指:/lib64/libc-2.17.so 和 /lib64/libc.a,分别是C标准动态
//库和C标准静态库,若只针对于C语言而言,其中的库函数的实现一般都在C标准动态库中,C标准库有自己
//的命名规则,在Linux系统中, .so对应的一般是动态库, .a对应的一般是静态库、
//当前为止,我们的Linux系统(不管是云服务器还是虚拟机等等环境)中一般都默认只安装了动态库,未安装静态库,包括C标准库和C++标准库、
[HJM@hjmlcc ~]$ ls /lib64/libc*
/lib64/libc-2.17.so /lib64/libcmdif.a /lib64/libcrack.so.2.9.0 /lib64/libcrypt.so.1
/lib64/libcairo-script-interpreter.so.2 /lib64/libc_nonshared.a /lib64/libcroco-0.6.so.3 /lib64/libc.so
/lib64/libcairo-script-interpreter.so.2.11512.0 /lib64/libcom_err.so /lib64/libcroco-0.6.so.3.0.1 /lib64/libc.so.6
/lib64/libcairo.so.2 /lib64/libcom_err.so.2 /lib64/libcrypt-2.17.so /lib64/libcupscgi.so.1
/lib64/libcairo.so.2.11512.0 /lib64/libcom_err.so.2.1 /lib64/libcrypto.so /lib64/libcupsimage.so.2
/lib64/libcap-ng.so.0 /lib64/libconfig.so.9 /lib64/libcrypto.so.10 /lib64/libcupsmime.so.1
/lib64/libcap-ng.so.0.0.0 /lib64/libconfig++.so.9 /lib64/libcrypto.so.1.0.2k /lib64/libcupsppdc.so.1
/lib64/libcap.so.2 /lib64/libconfig.so.9.1.3 /lib64/libcryptsetup.so.12 /lib64/libcups.so.2
/lib64/libcap.so.2.22 /lib64/libconfig++.so.9.1.3 /lib64/libcryptsetup.so.12.3.0 /lib64/libcurl.so.4
/lib64/libcidn-2.17.so /lib64/libcpupower.so.0 /lib64/libcryptsetup.so.4 /lib64/libcurl.so.4.3.0
/lib64/libcidn.so /lib64/libcpupower.so.0.0.0 /lib64/libcryptsetup.so.4.7.0
/lib64/libcidn.so.1 /lib64/libcrack.so.2 /lib64/libcrypt.so
[HJM@hjmlcc ~]$
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i lcc.o lcc.s
[HJM@hjmlcc ~]$ gcc lcc.o -o mylcc
//gcc编译器不加任何选项,默认会在系统特定的路径(该例子下,主要是指:/lib64/libc*或/usr/lib)下自动
//去寻找我们所要使用的链接库(该例子下,此处链接库指的就是C标准库,主要是C标准动态库),在预处理阶
//段,gcc编译器也会自动去系统特定的路径(该例子下,此处的路径是:/usr/include,主要是指头文件
//<stdio.h>,自动找到该头文件,将其其中的内容拷贝一份,在预处理阶段,将包含的头文件展开)下自动寻找
//所需要的所有头文件,将找到的所有的对应的头文件中的具体内容拷贝一份,在预处理阶段完成头文件的展
//开操作,g++编译器的功能也是类似的、
//不使用Linux系统下默认的可执行程序名:a.out,而是指定可执行程序名为:mylcc、
[HJM@hjmlcc ~]$ ls
lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc
[HJM@hjmlcc ~]$ ./mylcc
100
10
hello, debug
[HJM@hjmlcc ~]$
1.2、如何安装 C/C++ 标准静态库:
[HJM@hjmlcc ~]$ ls lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc [HJM@hjmlcc ~]$ ldd mylcc //查看某一个可执行程序所依赖的库,查看当前的链接状态、 linux-vdso.so.1 => (0x00007ffea3f35000) /$LIB/libonion.so => /lib64/libonion.so (0x00007fe3e6f09000) libc.so.6 => /lib64/libc.so.6 (0x00007fe3e6a22000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fe3e681e000) /lib64/ld-linux-x86-64.so.2 (0x00007fe3e6df0000) [HJM@hjmlcc ~]$ ls /lib64/libc.so.6 /lib64/libc.so.6 [HJM@hjmlcc ~]$ ls /lib64/libc.so.6 -al lrwxrwxrwx 1 root root 12 Oct 30 10:56 /lib64/libc.so.6 -> libc-2.17.so //C标准动态库、 [HJM@hjmlcc ~]$ file mylcc //查看某一个可执行程序的构成、 mylcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c85dc0a29837d6c3f34928dc00025635a5aa6b24, not stripped //由executable可知,这是一个可执行程序,dynamically linked代表动态链接,uses shared libs代表共 //享库,默认情况下,(gcc编译器下)形成的可执行程序就是动态链接的,默认情况下,(g++编译器下)形成的 //可执行程序也是动态链接的,如果想进行静态链接,必须要手动进行操作,如下所示、 [HJM@hjmlcc ~]$ gcc lcc.c -o mylcc2 -static //注意:在安装(可能不需要安装,不需要安装时,就是使用)指令 static 之前,必须保证当前Linux系统下 //已经安装了静态库,由于此时我当前的Linux系统中未安装静态库,因此此时执行上述指令会报错,具体的 //静态库的安装会在后面再进行阐述、 //拓展: //可以在普通用户下通过指令:sudo yum install -y glibc-static 来安装C标准静态库,也可以切换到 //root用户下,通过指令:yum install -y glibc-static 来安装C标准静态库、 //可以在普通用户下通过指令:sudo yum install -y libstdc++-static 来安装C++标准静态库,也可以 //切换到root用户下,通过指令:yum install -y libstdc++-static 来安装C++静态库、 [root@hjmlcc ~]# whoami root [root@hjmlcc ~]# pwd /root [root@hjmlcc ~]# yum install -y glibc-static Loaded plugins: fastestmirror, langpacks Repository cr is listed more than once in the configuration Repository fasttrack is listed more than once in the configuration Repository epel is listed more than once in the configuration Loading mirror speeds from cached hostfile Resolving Dependencies --> Running transaction check ---> Package glibc-static.x86_64 0:2.17-326.el7_9 will be installed --> Finished Dependency Resolution Dependencies Resolved ============================================================================================================================================== Package Arch Version Repository Size ============================================================================================================================================== Installing: glibc-static x86_64 2.17-326.el7_9 updates 1.6 M Transaction Summary ============================================================================================================================================== Install 1 Package Total download size: 1.6 M Installed size: 7.6 M Downloading packages: glibc-static-2.17-326.el7_9.x86_64.rpm | 1.6 MB 00:00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : glibc-static-2.17-326.el7_9.x86_64 1/1 Verifying : glibc-static-2.17-326.el7_9.x86_64 1/1 Installed: glibc-static.x86_64 0:2.17-326.el7_9 Complete! [root@hjmlcc ~]# logout [HJM@hjmlcc ~]$ ls lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc [HJM@hjmlcc ~]$ gcc lcc.c -o mylcc2 -static [HJM@hjmlcc ~]$ ls lcc.c lcc.cpp lcc.i lcc.o lcc.s mylcc mylcc2 [HJM@hjmlcc ~]$ ll total 892 -rw-rw-r-- 1 HJM HJM 309 Nov 2 20:36 lcc.c -rw-rw-r-- 1 HJM HJM 96 Nov 2 10:33 lcc.cpp -rw-rw-r-- 1 HJM HJM 16921 Nov 2 10:15 lcc.i -rw-rw-r-- 1 HJM HJM 1664 Nov 2 14:53 lcc.o -rw-rw-r-- 1 HJM HJM 594 Nov 2 10:49 lcc.s -rwxrwxr-x 1 HJM HJM 8408 Nov 2 20:57 mylcc //8408byte -rwxrwxr-x 1 HJM HJM 861384 Nov 2 21:48 mylcc2 //861384byte [HJM@hjmlcc ~]$
[HJM@hjmlcc ~]$ which ls
alias ls='ls --color=auto'
/usr/bin/ls
[HJM@hjmlcc ~]$ ldd /usr/bin/ls
linux-vdso.so.1 => (0x00007fff195ec000)
/$LIB/libonion.so => /lib64/libonion.so (0x00007fc55c5c6000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fc55c286000)
libcap.so.2 => /lib64/libcap.so.2 (0x00007fc55c081000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fc55be78000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc55baaa000)
//由此可知,指令 ls ,就是使用C语言来写的、
libdl.so.2 => /lib64/libdl.so.2 (0x00007fc55b8a6000)
libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fc55b644000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc55c4ad000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fc55b43f000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc55b223000)
[HJM@hjmlcc ~]$
//Linux系统中,很多的指令都是使用C语言写的,都会使用C标准库(主要是指C标准动态库)、
//所以,若在Linux系统下将C标准库(包括C标准动态库和静态库(主要指C标准动态库))删除,则许多指令都将不能再使用、
//拓展:
//以后缀为 .iso 结尾的文件(普通文件)一般是镜像文件、
1.3、头文件与库文件:
1、头文件:
不管是自己写的头文件还是系统默认自带的头文件,其中包含的一般都是声明(主要指的是函数的声明,自己写的头文件主要包含自己实现的函数的声明,系统默认自带的头文件主要包含的是各种库函数的声明),头文件给我们提供了可以使用的方法的声明,所有的开发环境,具有的语法提示功能,本质上是通过头文件帮我们搜索的,语法提示如下所示:
当输入 printf 时,Linux系统会自动在全部的头文件中搜索所有与 printf 有关的内容,这个过程是自动的,这就是语法提示、
2、库文件:
给我们提供了可以使用的方法的具体实现,以供链接,形成我们自己的可执行程序、
总结:
头文件与库文件之间的关系就类似于我们写的 .h 头文件与 .c 或 .cpp 源文件之间的关系,当我们自己写代码时,包含头文件,链接库文件,就可以形成可执行程序、
当我们自己在写代码时,包含了对应的头文件这一过程就已经可以称为链接过程了,等到寻找库函数的具体实现(定义)时,直接跳转到对应的库中即可,链接不是执行,链接是产生关系、
1.4、静态库,静态链接,动态库,动态链接:
我们的计算机,我们自己的系统中,或在链接库中,会存在两种库,分别为:静态库和动态库,在 Linux 系统中,所有的动态库都是以 .so 结尾的,而在 Windows 系统下,所有的动态库则都是以 .dll 结尾的,在 Linux 系统中,所有的静态库都是以 .a 结尾的,而在 Windows 系统下,所有的静态库都是以 .lib 结尾的,与这两种库相对应的就会存在两种链接方式,分别为:静态链接和动态链接,本章内容中,主要研究的是,我们的二进制普通目标文件是怎么和动态库或静态库链接起来的,也就是主要研究的是:动态链接和静态链接、
静态链接:将库中的相关代码直接拷贝到自己的二进制普通目标文件中,形成可执行程序,也就是说:在静态链接中,在链接阶段,查找对应模块的静态库,而不是动态库,在链接时会依赖一次静态库,当运行时,就不再依赖任何库(包括静态库和动态库),程序或指令可以独立的执行,我们之前使用的程序(指令)几乎都是动态链接(动态库),不发生拷贝,只发生关联、
静态链接和动态链接分别形成的可执行程序的优缺点:
1、动态链接:
优点:该动态库是大家共享的,可以节省资源,在 Linux 系统下,命令会加载到内存里,所以会节省内存资源,除此之外还可以节省磁盘资源、
缺点:一旦该动态库缺失,会导致几乎所有的程序(指令)失效、
2、静态链接:
优点:在链接阶段,查找对应模块的静态库,而不是动态库,在链接时会依赖一次静态库,当运行时,就不再依赖任何库(包括静态库和动态库),程序或指令可以独立的执行、
缺点:浪费资源,导致我们的系统里可能会存在很多重复的代码,导致内存,磁盘等资源被浪费、
在编译器(以 gcc 为例)中,怎么查看生成的可执行程序中到底是静态链接还是动态链接呢?
答:
可以通过形成的可执行程序的体积来进行分辨,静态链接形成的可执行程序的体积一定比动态链接形成的可执行程序的体积要大、
gcc编译器常见的选项:
-E:只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面、
-S:编译到汇编语言不进行汇编和链接、-c: 编译到目标代码、-o: 文件输出到文件、-static: 此选项对生成的文件采用静态链接、-g: 生成调试信息,GNU 调试器可利用该信息、-shared:此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库,在后期讲解动静态库时再进行具体阐述、-O0-O1-O2-O3: 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高,优化级别在讲解信号时再进行具体阐述、-w: 不生成任何警告信息、-Wall:生成所有警告信息、
二、简单 vim 配置
2.1、配置文件的位置
1、在目录 /etc 下面,有个名为 vimrc 的文件,这是系统中公共的vim配置文件,对所有用户都有效、2、而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:".vimrc" ,例如,/root 目录下, 通常已经存在一个 .vimrc 文件,如果不存在,则创建之、3、切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~ 、4、打开自己目录下的 .vimrc 文件,执行 vim .vimrc 、
2.2、常用配置选项,用来测试
1、设置语法高亮: syntax on2、显示行号: set nu3、设置缩进的空格数为4: set shiftwidth=4
2.3、使用插件
要配置好看的 vim,原生的配置可能功能不全,可以选择安装插件来完善配置,保证用户是你要配置的用户,接下来:1、安装 TagList 插件,下载 taglist_xx.zip ,解压完成,将解压出来的 doc 的内容放到~/.vim/doc ,将解压出来的 plugin 下的内容拷贝到 ~/.vim/plugin 、2、在 ~/.vimrc 中添加: let Tlist_Show_One_File=1 let Tlist_Exit_OnlyWindow=1 letTlist_Use_Right_Window=1 、3、安装文件浏览器和窗口管理器插件: WinManager 、4、下载 winmanager.zip,2.X版本以上的、5、解压 winmanager.zip,将解压出来的 doc 的内容放到 ~/.vim/doc,将解压出来的 plugin下的内容拷贝到 ~/.vim/plugin 、6、在 ~/.vimrc 中添加 let g:winManagerWindowLayout=‘FileExplorer|TagList nmap wm :WMToggle<cr> 、7、然后重启vim,打开 ~/XXX.c 或 ~/XXX.cpp , 在 normal 状态下输入 "wm" , 你将看到上图的效果更具体移步: 手把手教你把Vim改装成一个IDE编程环境(图文)_wooin的博客-CSDN博客 ,其他手册,请执行 vimtutor 命令、
2.4、参考资料
https://github.com/wsdjeg/vim-galore-zh_cn
知识拓展一:
- 细化的编译过程具体为:扫描程序-->语法分析-->语义分析-->源代码优化-->代码生成器-->目标代码优化;
- 扫描程序进行词法分析,从左向右,从上往下扫描源程序字符,识别出各个单词,确定单词类型;
- 语法分析是根据语法规则,将输入的语句构建出分析树,或者语法树,也就是我们答案中提到的分析树 parse tree 或者语法树 syntax tree ;
- 语义分析是根据上下文分析函数返回值类型是否对应这种语义检测,可以理解语法分析就是描述一个句子主宾谓是否符合规则,而语义用于检测句子的意思是否是正确的;
- 目标代码生成指的是,把中间代码变换成为特定机器上的低级语言代码;
知识拓展二:
- show break info break 用于查看断点信息;
- set scheduler-locking off 用于后期多线程调试,关闭调度锁(所有线程同步执行);
- info break 或 info b 查看断点信息;
知识拓展三:
- 在 makefile或Makefile 普通文件中,保存了编译器和链接器的参数选项,并且描述了所有源文件之间的关系,make 指令会读取 makefile或Makefile 普通文件中的数据,然后根据规则调用编译器,汇编器,链接器产生最后的输出;
- 在 makefile或Makefile 普通文件里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释;
- 显式规则说明了:如何生成一个或多个普通目标文件(非真正的目标文件)、
- make 指令有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写makefile或Makefile 普通文件,比如源文件与普通目标文件(非真正的目标文件)之间的时间关系判断之类的、
- 在 makefile或Makefile 普通文件中可以定义变量,当 makefile或Makefile 普通文件被执行时,其中的变量都会被扩展到相应的引用位置上,通常使用 $(var) 表示引用变量、
- 文件指示:包含在一个 makefile或Makefile 普通文件中引用另一个 makefile或Makefile 普通文件,类似C语言中的include,即:在 makefile或Makefile 普通文件中,可以使用 include 关键字把别的 makefile或Makefile 普通文件包含进来、
- 注释: makefile或Makefile 普通文件中可以使用 # 在行首表示行注释、
- 默认的情况下,make 命令会在当前目录下按顺序找寻文件名为 " GNUmakefile "、" makefile "、" Makefile " 的普通文件、
知识拓展四:
- make 指令的执行规则是:只生成所有的普通目标文件(非真正的目标文件)中的第一个,当然 make 会根据语法规则,递归生成第一个普通目标文件(非真正的目标文件)的所有依赖对象后再回头生成第一个普通目标文件(非真正的目标文件),生成后退出、
- make 指令在执行 makefile或Makefile 普通文件规则中,根据语法规则,会分析普通目标文件(非真正的目标文件)象与依赖对象的时间信息,判断是否在上一次生成后,源文件发生了修改,若发生了修改才需要重新生成、
- makefile或Makefile 普通文件中的伪普通目标文件(非真正的目标文件)的名称并不代表真正的文件名,与实际存在的同名文件(主要指普通文件)没有相互关系,因此伪普通目标文件(非真正的目标文件)不管同名文件(主要指普通文件)是否存在都会执行对应的生成指令,伪普通目标文件(非真正的目标文件)的作用有两个:1:看似伪普通目标文件(非真正的目标文件)无论如何都要重新生成,2:但本质上并不是为了生成伪普通目标文件(非真正的目标文件),而是为了执行一些指令、
- makefile和Makefile 普通文件中使用关键字 .PHONY 来声明伪普通目标文件(非真正的目标文件)、
知识拓展五:
- 死代码删除是编译最优化技术,指的是移除根本执行不到的代码,或者对程序运行结果没有影响的代码,而并不是删除被注释的代码、
- 内联函数,也叫编译时期展开函数, 指的是建议编译器将内联函数体插入并取代每一处调用函数的地方,从而节省函数调用带来的成本,使用方式类似于宏,但是与宏不同的是内联函数拥有参数类型的校验,以及调试信息,而宏只是文本替换而已、
- for 循环的循环控制变量,通常被 cpu 访问频繁,因此如果调度到寄存器中进行访问则不用每次从内存中取出数据,可以提高访问效率、
- 强度削弱是指执行时间较短的指令等价的替代执行时间较长的指令,比如 num%128 与 num&127 相较,则明显 num&127 更加轻量、
知识拓展六:
- 在 vim/vi 编辑器中的底行/末行模式下输入s/AAA/yyy/g,则可以将 vim/vi 编辑器中的内容中的字符AAA,即:'A''A''A'替换成字符yyy,即:'y''y''y',其中,选项 g 比较常用,通常使用选项 g 表示全部替换,如果不写选项 g 的话,则表示只替换第一次出现字符AAA,即:'A''A''A'的地方、