CS Basic Note 1

计算机系统基础 课程笔记

Keruone
2025-1-23

课程PPT
课程视频


编译过程

源文件 编译方法 目标文件
.c gcc -E:预处理 .i
.c/.i gcc -S:汇编 .s
.c/.i/.s gcc -c:编译 .o
.c/.i/.s/.o gcc:链接 .out

.c 源文件
.i 预处理文件
.s 汇编文件
.o 目标文件
.out 可执行文件

gcc 输出 out文件的命令为: gcc -o out a.c
其中 -o 表示输出文件的名称为 out

查看命令含义

man 命令
tldr 命令

gcc 编译过程

原始输出
keruone@Ubuntu20:~/xuehao$ gcc a.c --verbose
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.4.0-1ubuntu1~20.04.3' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --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-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --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=/build/gcc-9-05ho5U/gcc-9-9.4.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.3) 
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/9/cc1 -quiet -v -imultiarch x86_64-linux-gnu a.c -quiet -dumpbase a.c -mtune=generic -march=x86-64 -auxbase a -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccOmYDRk.s
GNU C17 (Ubuntu 9.4.0-1ubuntu1~20.04.3) version 9.4.0 (x86_64-linux-gnu)
    compiled by GNU C version 9.4.0, GMP version 6.2.0, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.22.1-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/9/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/9/include
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
GNU C17 (Ubuntu 9.4.0-1ubuntu1~20.04.3) version 9.4.0 (x86_64-linux-gnu)
    compiled by GNU C version 9.4.0, GMP version 6.2.0, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.22.1-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: d50f9baaf86b1ab1ea2b5e982c779df7
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/cc6jqJDl.o /tmp/ccOmYDRk.s
GNU汇编版本 2.34 (x86_64-linux-gnu) 使用BFD版本 (GNU Binutils for Ubuntu) 2.34
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper -plugin-opt=-fresolution=/tmp/ccx8hAck.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/9/../../.. /tmp/cc6jqJDl.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
解释及参数含义
原始输出 解释及参数含义
keruone@Ubuntu20:~/xuehao$ gcc a.c --verbose 使用 gcc 编译 a.c 文件,并启用详细输出模式(--verbose)。
--verbose:显示详细的编译过程信息。
Using built-in specs. 使用 GCC 内置的默认配置。
COLLECT_GCC=gcc 当前使用的 GCC 编译器。
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper 使用的 LTO(链接时优化)包装器路径。
LTO:链接时优化,允许在链接阶段进行跨模块优化。
OFFLOAD_TARGET_NAMES=nvptx-none:hsa 支持的卸载目标(如 GPU 加速)。
nvptx-none:NVIDIA PTX 卸载目标。
hsa:异构系统架构卸载目标。
OFFLOAD_TARGET_DEFAULT=1 默认启用卸载目标支持。
Target: x86_64-linux-gnu 目标平台为 x86_64 架构的 Linux 系统。
Configured with: ../src/configure -v ... GCC 编译器的配置选项,包括支持的编程语言、路径、优化选项等。
--enable-languages:支持的编程语言(如 C、C++ 等)。
--prefix:安装路径。
--enable-shared:启用共享库支持。
--enable-threads=posix:启用 POSIX 线程支持。
Thread model: posix 使用的线程模型为 POSIX 线程。
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.3) GCC 版本信息。
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64' 当前 GCC 调用的编译选项。
-v:显示详细输出。
-mtune=generic:优化代码以适应通用 CPU 架构。
-march=x86-64:生成针对 x86-64 架构的代码。
/usr/lib/gcc/x86_64-linux-gnu/9/cc1 -quiet -v ... 调用 GCC 的 C 编译器前端 cc1,将 a.c 编译为汇编代码 /tmp/ccOmYDRk.s
-quiet:减少输出信息。
-v:显示详细输出。
-dumpbase:指定生成的汇编文件的基础名称。
-mtune=generic:优化代码以适应通用 CPU 架构。
-march=x86-64:生成针对 x86-64 架构的代码。
GNU C17 (Ubuntu 9.4.0-1ubuntu1~20.04.3) version 9.4.0 ... 使用的 C 语言标准为 C17,编译器版本为 9.4.0。
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 GCC 的垃圾回收启发式参数设置。
ggc-min-expand:控制垃圾回收的最小扩展比例。
ggc-min-heapsize:控制垃圾回收的最小堆大小。
ignoring nonexistent directory ... 忽略不存在的目录路径。
#include "..." search starts here: 开始搜索用户自定义头文件路径。
#include <...> search starts here: 开始搜索系统头文件路径。
/usr/lib/gcc/x86_64-linux-gnu/9/include 系统头文件搜索路径之一。
/usr/local/include 系统头文件搜索路径之一。
/usr/include/x86_64-linux-gnu 系统头文件搜索路径之一。
/usr/include 系统头文件搜索路径之一。
End of search list. 头文件搜索路径结束。
Compiler executable checksum: d50f9baaf86b1ab1ea2b5e982c779df7 编译器可执行文件的校验和。
as -v --64 -o /tmp/cc6jqJDl.o /tmp/ccOmYDRk.s 调用汇编器 as,将汇编代码 /tmp/ccOmYDRk.s 编译为目标文件 /tmp/cc6jqJDl.o
-v:显示详细输出。
--64:生成 64 位目标文件。
-o:指定输出文件名。
GNU汇编版本 2.34 (x86_64-linux-gnu) 使用BFD版本 (GNU Binutils for Ubuntu) 2.34 使用的汇编器版本信息。
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/ 编译器工具链的搜索路径。
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../:/lib/:/usr/lib/ 库文件的搜索路径。
/usr/lib/gcc/x86_64-linux-gnu/9/collect2 ... 调用链接器 collect2,将目标文件 /tmp/cc6jqJDl.o 链接为最终的可执行文件。
-plugin:指定 LTO 插件。
-plugin-opt:传递给插件的选项。
-dynamic-linker:指定动态链接器。
-pie:生成位置无关的可执行文件。
-z now:启用立即绑定。
-z relro:启用只读重定位。
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64' 最终 GCC 调用的编译选项。

奇怪的示例代码:

  • 以下代码输出为何?
extern int printf(const char *__restrict __format, ...);

int main(){
    #if aa == bb
        printf("YES\n");
    #else
        printf("NO\n");
    #endif
}

解析:

“`#if aa == bb:“`中“`#if“` 是 C 预处理器指令,用于条件编译。“`aa“` 和 “`bb“` 是未定义的宏。而对于未定义宏,在预处理器中,会被替换为 “`0“`。
因此,“`aa“` 和 “`bb“` 都被替换为 “`0“`,表达式 “`aa == bb“` 等价于 “`0 == 0“`,结果为真“`(1)“`。
所以,预处理器会将 “`#if aa == bb“` 后面的代码块 “`printf(“YES\n”);“` 保留下来,而 “`#else“` 后面的代码块 “`printf(“NO\n”);“` 会被删除。

编译结果:

//#include <stdio.h>
extern int printf(const char *__restrict __format, ...);

int main(){
    #if aa == bb
        printf("YES\n");
    #else
        printf("NO\n");
    #endif
}

宏展开

即通过复制黏贴的方式来替换宏,直到没有宏可以再展开

#include<stdio.h>
#define NAMES(x)\
    x(Tom) x(Jerry) x(Spike)

int main(){
    #define PRINT(x) puts("Hello, " #x "!");    //#x 表示将 x 替换为字符串
    NAMES(PRINT)
}

以下是使用 Markdown 格式整理的内容:


C 程序执行的两个视角

  • 静态:C 代码的连接一段总能对应到一段连接的机器指令。
  • 动态:C 代码执行的状态总能对应到机器的状态。

源代码视角

  • 函数、变量、指针……

机器指令视角

  • 寄存器、内存、地址……

两个视角的共同之处:内存

  • 代码、变量(源代码视角)= 地址 + 长度(机器指令视角)
  • (不大于逻辑) 内存 = 代码 + 数据 + 堆栈
    • 因此,理解 C 程序执行最重要的就是内存模型。

一切皆可取地址

代码案例1

void printptr(void *p) {
  printf("p = %p;  *p = %016lx\n", p, *(long *)p);
}
int x;
int main(int argc, char *argv[]) {
  printptr(main);  // 代码
  printptr(&main);
  printptr(&x);    // 数据
  printptr(&argc); // 堆栈
  printptr(argv);
  printptr(&argv);
  printptr(argv[0]);
}

输出:

p = 0x55678f95d17b;  *p = e5894855fa1e0ff3
p = 0x55678f95d17b;  *p = e5894855fa1e0ff3
p = 0x55678f960014;  *p = 0000000000000000
p = 0x7ffcab646dbc;  *p = 0000000000000001
p = 0x7ffcab646eb8;  *p = 00007ffcab647302
p = 0x7ffcab646db0;  *p = 00007ffcab646eb8
p = 0x7ffcab647302;  *p = 0074756f2e642f2e

代码案例2

int main(int argc, char *argv[]) {
  int (*f)(int, char *[]) = main;
  if (argc != 0) {
    char ***a = &argv, *first = argv[0], ch = argv[0][0];
    printf("arg = \"%s\";  ch = '%c'\n", first, ch);
    assert(***a == ch);
    f(argc - 1, argv + 1);
  }
}

其中argc是命令行参数的个数,argv是命令行参数的数组。

下面介绍一个命令行调用案例,并解释什么参数给到argc,什么参数给到argv(假设该文件为test.out)
./test.out hello world

当我们运行 ./test.out hello world 时,命令行参数的个数 argc 为 3,分别是:

./test.out:第一个参数是程序的名称,它是命令行中第一个参数,并且通常是可执行文件的路径。
hello:第二个参数是 hello
world:第三个参数是 world

argc会记录命令行参数的个数,argv会记录命令行参数的数组。

命令行输入

$ ./test.out hello world

输出为

arg = "./c.out";  ch = '.' 
arg = "Hello";  ch = 'H' 
arg = "Word";  ch = 'W'

这段代码的argc的存储可视为32位(4字节),argv参数为指针所以存储为64位(8字节)。
Q: 为什么 ***a ch?

A:
1. char a = &argv:
argv 是一个 char ** 类型(指向字符串数组的指针)。
&argv 是 argv 的地址,
因此 a 是一个 char *** 类型(指向 argv 的指针)
2. ***a 的解引用过程:
a 是 char *** 类型,指向 argv。
*a 是 argv 本身,类型为 char **。
**a 是 argv[0],类型为 char *(指向第一个参数字符串的指针)。
***a 是 argv[0][0],类型为 char(第一个参数字符串的第一个字符)。
3. ch 的值:
ch 是 argv[0][0],即第一个参数字符串的第一个字符。
4. assert(
a ch):
a 解引用后得到 argv[0][0],也就是 ch。
因此,
a
ch 恒成立,断言总是为真。


其它代码:

  1. 删除当前文件夹下的所有文件和文件夹:
    rm -rf ./*
    
  2. 查看上一条命令的 退出状态码
    echo $?
    
  3. Vim 编辑器中的一个命令,用于将当前文件或选中的内容以 十六进制格式 显示。这个功能通常用于查看或编辑二进制文件。
    :%!xxd
    
    1. vi $(fzf) 是一个结合了 vi 编辑器和 fzf(模糊查找工具)的命令,用于通过模糊查找选择文件并用 vi 打开
      示例
      假设当前目录下有如下文件:

      file1.txt
      file2.txt
      file3.txt
      

      运行 `vi $(fzf)` 后:
      `fzf` 会列出 `file1.txt`、`file2.txt` 和 `file3.txt`。
      用户可以输入 `file2` 来快速定位 `file2.txt`。
      按下回车后,`vi` 会打开 `file2.txt`。

  4. 管道和 wc 命令
    cat file.txt | wc -l
    
    • cat file.txt:输出 file.txt 文件的内容。
    • |:管道符,将前一个命令的输出作为后一个命令的输入。
    • wc -l:统计输入的行数。
    • 示例:
      如果 file.txt 内容如下:

      Line 1
      Line 2
      Line 3
      

      运行 `cat file.txt | wc -l` 后,输出结果为 `3`,表示文件有 3 行。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇