1、GCC的发展

  • GNU(意为非洲牛羚)项目,又称革奴计划,是由Richard Stallman在1983创办。
  • 1985年,Richard Stallman又创立了自由软件基金会(Free Software Foundation, 简称FSF) 来为GNU提供技术、法律以及财政支持。
  • 编译器GCC就是GNU开发出来的一款编译器软件,GCC是GNU CC的简称。
  • GCC符合ANSI C标准,能够编译C、C++、Object C等语言编写的程序。GCC还是一个交叉平台编译器,能够在当前CPU平台为多种不同架构的硬件平台开发软件,因此适合嵌入式领域的开发编译。
  • GCC是免费的,可移植。

    2、gcc的语法结构

    gcc 的基本语法: :::info gcc [options] [filenames] :::
  1. 常用编译选项
  • -c:只编译不连接,生成目标文件".o"
  • -S:只编译不汇编,生成汇编代码。
  • -E:只进行预编译,不做其他处理。(相当于头文件与宏定义处理)
  • -o file:指定输出文件(一般就是二进制的文件)
  • -v:打印出编译器内部编译过程的信息和编译器的版本。
  • -std=name:指定C语言的标准(比如C99)
  • -l dir:在头文件的搜索路径列表中添加dir目录。

练习:新建本节文件目录结构如下,我们将使用 GCC 编译器对src/hello.c进行编译。

  1. b07@SB:~/c/chapter2$ tree
  2. .
  3. ├── bin
  4. ├── include
  5. ├── obj
  6. └── src
  7. └── hello.c
  8. 4 directories, 1 file
  9. b07@SB:~/c/chapter2$ cat ./src/hello.c
  10. #include <stdio.h>
  11. int main() {
  12. printf("Hello World\n");
  13. return 0;
  14. }
  15. # 1. 查看 GCC 版本
  16. b07@SB:~/c/chapter2$ gcc -v
  17. Using built-in specs.
  18. COLLECT_GCC=gcc
  19. COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper
  20. OFFLOAD_TARGET_NAMES=nvptx-none
  21. OFFLOAD_TARGET_DEFAULT=1
  22. Target: x86_64-linux-gnu
  23. Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
  24. Thread model: posix
  25. gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
  26. # 2. 默认 gcc 编译标准(此处为 2011)
  27. b07@SB:~/c/chapter2$ gcc -E -dM - </dev/null | grep "STDC_VERSION"
  28. #define __STDC_VERSION__ 201112L
  29. # 3. 编译链接一气呵成
  30. b07@SB:~/c/chapter2$ gcc -o ./bin/hello ./src/hello.c
  31. b07@SB:~/c/chapter2$ ls bin
  32. hello
  33. b07@SB:~/c/chapter2$ ./bin/hello # 必须使用 ./ 表明当前路径下
  34. Hello World
  35. # 4. 编译目标文件和指定编译标准 -std
  36. b07@SB:~/c/chapter2$ gcc -std=c99 -o obj/hello.o -c src/hello.c # -std必须在-o前面,-c只编译不链接
  37. b07@SB:~/c/chapter2$ ls obj/
  38. hello.o
  39. b07@SB:~/c/chapter2$ gcc -o bin/hello2 obj/hello.o # 目标文件再进行链接
  40. b07@SB:~/c/chapter2$ ./bin/hello2
  41. Hello World
  1. 优化选项
  • -O:减小代码的长度和执行时间,效果等价于-O1,其中包括线程跳转和延迟退栈。
  • -O2:除完成所有的-O1级别优化,还进行一些额外的调整工作,如处理器指令调度等。(一般都是这个)
  • -O3:除完成所有-O2级别优化,还包括循环展开和一些其他与处理器特性相关的优化工作。(用的不多,编译时间长)

数字越大速度越快。一般使用-O2,它在编译时间和代码大小之间取得了相对平衡。我们以一个程序演示这几个比优化选项的差别。

  1. b07@SB:~/c/chapter2$ cat src/optimize.c
  2. /*
  3. * filename: optimize.c
  4. * description: 通过 GCC 命令的选项 -O(1/2/3) 对循环程序进行优化
  5. */
  6. #include <stdio.h>
  7. int main() {
  8. double counter, result, temp;
  9. for (counter = 0; counter < 4000.0 * 4000.0 * 4000.0 / 20.0 + 2030;
  10. counter += (5 -3 + 2 + 1) / 4) {
  11. temp = counter / 1239;
  12. result = counter;
  13. }
  14. printf("Result is %lf\n", result);
  15. return 0;
  16. }
  17. # 不优化运行时间
  18. b07@SB:~/c/chapter2$ gcc -o bin/optimizeO1 src/optimize.c
  19. b07@SB:~/c/chapter2$ time ./bin/optimizeO1
  20. Result is 3200002029.000000
  21. real 0m10.675s
  22. user 0m10.672s
  23. sys 0m0.000s
  24. # 2. -O1 优化运行时间
  25. b07@SB:~/c/chapter2$ gcc -O -o bin/optimizeO1 src/optimize.c
  26. b07@SB:~/c/chapter2$ time ./bin/optimizeO1
  27. Result is 3200002029.000000
  28. real 0m4.508s
  29. user 0m4.484s
  30. sys 0m0.000s
  31. # 2. -O2 优化允许时间
  32. b07@SB:~/c/chapter2$ gcc -O2 -o bin/optimizeO2 src/optimize.c
  33. b07@SB:~/c/chapter2$ time ./bin/optimizeO2
  34. Result is 3200002029.000000
  35. real 0m4.742s
  36. user 0m4.719s
  37. sys 0m0.000s
  38. # 3. -O3 优化允许时间
  39. b07@SB:~/c/chapter2$ gcc -O3 -o bin/optimizeO3 src/optimize.c
  40. b07@SB:~/c/chapter2$ time ./bin/optimizeO3
  41. Result is 3200002029.000000
  42. real 0m4.531s
  43. user 0m4.531s
  44. sys 0m0.000s

实验结果可以看到优化比不优化的差距很大,虽然优化级别之间运行时间相差不大,但是一般使用-O2优化。

  1. 警告和出错选项
  • -ansi:支持符合ANSI标准的 C 程序即 C90
  • -pedantic:允许发出 ANSI C 标准所列的全部警告信息。
  • -pedantic-error:允许发出 ANSI C 标准所列的全部错误信息。
  • -w:关闭所有的警告。
  • -Wall:允许发出gcc所提供的所有有用的警告信息(比较关键,对于养成良好的习惯) ```c

    1. 无警告信息

    b07@SB:~/c/chapter2$ cat ./src/hello.c

    include

int main() { printf(“Hello World\n”); return 0; } b07@SB:~/c/chapter2$ gcc -Wall -o bin/hello_world src/hello.c # 无警告信息 b07@SB:~/c/chapter2$ ./bin/hello_world # 正常运行 Hello World

2. 警告信息(hello.c中的 main 函数去掉 return 0;)

b07@SB:~/c/chapter2$ cat src/hello.c

include

int main() { printf(“Hellow World\n”); } b07@SB:~/c/chapter2$ gcc -o bin/hello_world src/hello.c # 默认不警告 b07@SB:~/c/chapter2$ ./bin/hello_world Hello World b07@SB:~/c/chapter2$ gcc -Wall -o bin/hello_world src/hello.c # 加上 -Wall 为什么不警告? b07@SB:~/c/chapter2$ ./bin/hello_world Hello World

  1. :::warning
  2. **Q:**为什么上面没有警告信息输出呢?难道是现在编译器能够容忍你的一些比较常见的错误吗?<br />A
  3. :::
  4. 什么是 ErrorError 分为语法错误和语义错误两种:
  5. 1. **语法错误**:即违反 C 语言规定语法,是无法通过编译生成可执行文件。这是与 warning 最大区别,因其可生成可执行文件。
  6. 1. **语义错误**:即**代码逻辑错误**,编译通过且程序能运行,但是得不到正确结果,例如公式写错或整数除法。这种只能通过调试代码才能进行辨别。
  7. ```bash
  8. # 1. Error 是无法通过编译生成可执行文件
  9. b07@SB:~/c/chapter2$ cat ./src/hello.c
  10. #include <stdio.h>
  11. int main() {
  12. printf("Hello World\n");
  13. retur 0;
  14. }
  15. b07@SB:~/c/chapter2$ ls bin/ # bin 目录是空的
  16. b07@SB:~/c/chapter2$ ls
  17. bin include obj src
  18. b07@SB:~/c/chapter2$ gcc -Wall -o bin/hello_world src/hello.c
  19. src/hello.c: In function ‘main’:
  20. src/hello.c:5:5: error: ‘retur’ undeclared (first use in this function)
  21. retur 0;
  22. ^~~~~
  23. src/hello.c:5:5: note: each undeclared identifier is reported only once for each function it appears in
  24. src/hello.c:5:11: error: expected ‘;’ before numeric constant
  25. retur 0;
  26. ^
  27. b07@SB:~/c/chapter2$ ls bin/ # 依然是空的!
  28. # 2. 语义错误 1-99 求和(整数除法错误)
  29. b07@SB:~/c/chapter2$ cat ./src/semanticsError.c
  30. #include <stdio.h>
  31. int main() {
  32. printf("1+2+...+99=%d\n", 99 / 2 * (1 + 99)); // 整数除法是向下整除(即取商舍余 99/2=49)
  33. return 0;
  34. }
  35. b07@SB:~/c/chapter2$ gcc -Wall -o bin/semanticsError src/semanticsError.c
  36. b07@SB:~/c/chapter2$ ./bin/semanticsError
  37. 1+2+...+99=4900
  38. b07@SB:~/c/chapter2$ cat ./src/semanticsError.c
  39. #include <stdio.h>
  40. int main() {
  41. printf("1+2+...+99=%d\n", 99 * (1 + 99) / 2); // 改变除法顺序即可得到正确结果
  42. return 0;
  43. }
  44. b07@SB:~/c/chapter2$ gcc -Wall -o bin/semanticsError src/semanticsError.c
  45. b07@SB:~/c/chapter2$ ./bin/semanticsError
  46. 1+2+...+99=4950
  1. 制作库文件选项
  • -L dir:在库文件的搜索路径列表中添加dir目录
  • -static:静态链接库。
  • -iname:连接名为name的库文件
  • -shared:表明是使用共用库。

附录

1. Linux 环境搭建

Linux 上开发 C 语言可能对很多零基础的同学来说实在太难,因为他们都不熟悉 Linux,而且更畏惧 vi 编辑器。

  • 编译器:默认情况 Linux 发行版没有安装 GCC 编译器,需要使用包管理器安装。
  • 文本编辑器:这个东西就是写代码用的,类似 Windows 上的记事本程序,Linux 上默认自带 vi 编辑器,但是建议升级为 vim(vim 是其升级版,支持更多人性化操作,但是也不够人性!)

[

](https://www.cnblogs.com/wangxiaobei2019/p/12084735.html)
在使用 vim 进行文档操作时,经常需要跨应用进行复制粘贴,在粘贴大量代码时,出现行错位等各种错乱,查找问题解决办法:

  • 一次性:vim 进入文件后,先 ESC 在出入 :set paste 回车后再按下 i 之后进行粘贴的内容就规矩了
  • 永久性:将当前配置写入 vim 的配置文件中,建议在用户家目录(不必改变 etc 目录下的 vim 系统配置下创建 ~/.vimrc并复制以下内容即可。
    1. set encoding=utf-8
    2. set nocompatible
    3. syntax on
    4. set noerrorbells
    5. set nu
    6. set tabstop=4
    7. set shiftwidth=4
    8. set softtabstop=4
    9. set expandtab
    10. set smartindent
    11. set nowrap
    12. set smartcase
    13. set noswapfile
    14. set nobackup
    15. set undodir=~/.vim/undodir
    16. set undofile
    17. set incsearch
    18. set paste

2. windows 环境搭建

建议 Windows 10 用户使用 Windows Subsystem for Linux(WSL),然后使用 VS Code 即可快速搭建开发环境。

下面使用的是 sublime text3 + MinGW,然后有能力的可以购买 sublime text3,打开速度比 VS Code 快,唯一缺点就是自带终端无法接受输入输出函数,且对小型文件或者头文件一起编译有点麻烦。

2.1 安装编译器

下载完 MinGW 是一个包管理器,需要下载编译器,一般选择三个即可,如下图所示,找不到可在 All packages 中寻找(一般可能下载失败跟网速有关),右键选中后点击 Aplly Changes,最后不要忘记把这个下载目录添加进环境变量!!minW安装编译包.png
比如我这里是D:\mingw_compiler\bin该文件下面就有gcc程序,添加进环境变量则有path=D:\mingw_compiler\bin;。然后如果你能在终端输入gcc -v而不是系统无法找到指定程序错误就是环境安排好了。

  1. C:\Users\15117>gcc -v
  2. Using built-in specs.
  3. COLLECT_GCC=gcc
  4. COLLECT_LTO_WRAPPER=d:/mingw_compiler/bin/../libexec/gcc/mingw32/9.2.0/lto-wrapper.exe
  5. Target: mingw32
  6. Configured with: ../src/gcc-9.2.0/configure --build=x86_64-pc-linux-gnu --host=mingw32 --target=mingw32 --disable-win32-registry --with-arch=i586 --with-tune=generic --enable-static --enable-shared --enable-threads --enable-languages=c,c++,objc,obj-c++,fortran,ada --with-dwarf2 --disable-sjlj-exceptions --enable-version-specific-runtime-libs --enable-libgomp --disable-libvtv --with-libiconv-prefix=/mingw --with-libintl-prefix=/mingw --enable-libstdcxx-debug --disable-build-format-warnings --prefix=/mingw --with-gmp=/mingw --with-mpfr=/mingw --with-mpc=/mingw --with-isl=/mingw --enable-nls --with-pkgversion='MinGW.org GCC Build-20200227-1'
  7. Thread model: win32
  8. gcc version 9.2.0 (MinGW.org GCC Build-20200227-1)

2.2 sublime text3 配置编译系统

其实这不是什么破玩意,上面才是最重要的,有能力的可以跳过这个配置编译系统,因为默认的 sublime text3 是包含有C的系统的(如下图),但是就是它一个自带终端毛病,使得输入无法执行,因此一般需要配置在 cmd 执行的编译系统。
sublime自带C与创建编译系统.png
点击最后的New Build System创建,输入以下内容(百度很久都没法解决,下面是完全可以解决在 cmd 中文乱码问题,强制编译语言集为 gbk 解决)

  1. {
  2. "cmd": ["gcc","-Wall", "${file}","-std=c99", "-o", "${file_path}/${file_base_name}"],
  3. "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
  4. "shell":true,
  5. "working_dir": "${file_path}",
  6. "selector": "source.c, source.c++, source.h",
  7. "encoding":"gbk",
  8. "variants":[
  9. {
  10. "name": "Run",
  11. "cmd": ["cmd", "/c", "gcc", "-fexec-charset=gbk", "-Wall", "-D_GNU_SOURCE", "${file}",
  12. "-std=c99","-o", "${file_path}/${file_base_name}",
  13. "&&", "cmd", "/c", "${file_path}/${file_base_name}"]
  14. },
  15. {
  16. "name": "RunInCommand",
  17. "cmd": ["cmd", "/c", "gcc", "-Wall", "-fexec-charset=gbk", "-D_GNU_SOURCE","${file}",
  18. "-std=c99","-o", "${file_path}/${file_base_name}",
  19. "&&", "start", "cmd", "/c", "${file_path}/${file_base_name} & echo.&pause"]
  20. }
  21. ]
  22. }

目前你可以不知道这些是什么意思,但是你应该能动在Linux中手动 gcc -o bin/helloc src/hello.c这样的格式!

  • "cmd":这里不是cmd,而是sublime text3的终端,即第一个命令就是只编译。
  • "variants":就是sublime text3的编译系统选择参数:
    • "name":"Run":就是使用Windows的cmd运行然后把结果放在sublime text3的终端显示。
    • "name":"RunInCommand":就是打开Windows的cmd运行与交互,最后关闭cmd后回到sublime text3编辑器。

      2.3 运行 C 程序

      第一步编辑 C 程序,让我们写那个老掉牙的Hello Shit world
      1. #include <stdio.h> // 输入输出头文件(printf函数)
      2. int main() {
      3. printf("你好 Shit world"); // 我是注释
      4. return 0; // main函数返回值
      5. }
      然后在sublime text3中按下(默认快捷键,修改另说),有如下三个选项,分别对应上面编译系统的三个命令+参数。
      编译系统选项.png
  1. 只编译:什么都没有输出,但是编译。在当前这个文件下有一个和文件名(不包括后缀名).exe文件。如果你直接点击,你眼速快可以看到一闪而过,因为程序执行完了!而使用拖入 cmd 中就可以看到具体结果,也就是为啥 variant 最后加上一个pause.

    1. C:\Users\15117>D:\C语言\C学习\0.gcc的介绍使用\src\hello.exe
    2. 浣犲ソ Shit world # 乱码(因为第一个没指定编码格式)
  2. 编译 + sublime 终端输出:这个只要记住,在sublime下面黑框框输出就行,不管那么多。并且记住无法执行scanf输入!

sublime终端输出.png :::tips 这个不需要添加 gbk 编码都可以在这个黑框框输出正确的中文,因为 sublime text 任何语言都可以。(有兴趣的可以试下,这里编译系统实现 gbk 强制编码)。
看到最后的计时没?很方便,但是tm的跟在输出的后面,解决这个问题吗,网上搜了麻烦,还不如在函数prinft后面手动添加一个换行符\n,并且prinft函数自动补全也很机智的。 :::

  1. 编译 + cmd 终端输出:这主要就是弥补黑框没法识别输入函数而定制针对 cmd 特定 gbk 编码的选项。

cmd交互输出.png
因为中文操作系统的 cmd 很傻逼,不可以更改字符页,每次修改都是手动的,而且有修改注册表的,非常丑,尤其是最后带到 sublime text3 的终端黑框加上一个cha 956(举例而已)