C 程序结构

预计时间: 5分钟

Hello World 示例

一个C程序基本上由以下部分组成 -

  • 预处理器指令
  • 函数
  • 变量
  • 语句和表达式
  • 注释

编写源代码文件

创建 hello.c 文件,内容如下

#include <stdio.h>

int main() {
  printf("hello world!\n");

  return 0;
}

上述各个部分的意思是

  • #include 是交给预处理器执行的指令,以#开后面跟指令的名称,stdio.h 头文件中声明了 printf 函数。
  • 尖括号<>表示查找系统头文件所在目录,通常在 /usr/include 中。自己编写的头文件用双引号"",表示先查找当前目录,再查找其他目录。可通过设置编译器命令行选项如 -I 来配置查找位置。
  • #include <stdio.h>表示在编译之前,先把 /usr/include/stdio.h 文件的内容包含到当前文件中。
  • int main() {}表示定义返回类型为 int类型的 main 函数。main 函数也是程序的入口。
  • printf("hello world!\n"); 调用printf函数并传入实参"hello world!\n"
  • return 0;程序结束后,返回给操作系统shell值为0,可在shell下通过ehco $?查看。

编译和链接

先尝试用 gcc 命令编译

gcc hello.c

该命令输出

a.out

a.out 是 assembler output 的缩写,历史上 UNIX 程序汇编程序默认输出保存在 a.out 中

使用 -o 参数重命名输出 hello

gcc hello.c -o hello

再试试 cc 命令编译,cc 是默认 c 编译器的命令,如果安装的是 gcc 则 cc 等同于 gcc。

# 删除 a.out 和 hello
rm -f a.out hello

# 编译链接输出 a.out
cc hello.c

# 编译链接输出 hello
cc hello.c -o hello

结果是一样的

最后再试试 make 命令

# 删除 a.out 和 hello
rm -f a.out hello

# 使用 make 隐规则输出 hello
make hello
# 屏幕上会输出实际执行的命令
# cc     hello.c   -o hello

make 通常配合 Makefile 文件使用,在 Makefile 里描述规则,如果没有 Makefile 文件,则 make 使用内部预定义的隐含规则。

根据隐规则执行如下命令

cc hello.c -o hello

在后面的内容里会再详细介绍 make 工具。

运行

运行程序

./hello

. 点表示当前目录,./hello 表示执行当前目录下的 hello

如果不指定目录,只使用 hello 则 shell 会在 $PATH 中定义的路径搜索。 如果找到则执行,找不到则报错

可以通过下面的命令查看搜索路径

echo $PATH

试验

# hello 复制到 /usr/local/bin/
sudo cp hello /usr/local/bin/

# 直接运行
hello

# 试验完删除
sudo rm -f /usr/local/bin/hello

编译后的文件是什么

显示汇编

objdump -d hello

生成汇编文件 hello.s

gcc -S hello.c

打印共享库依赖

ldd hello

	linux-vdso.so.1 (0x00007ffdaaf9e000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f70c3d0b000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f70c3f0b000)

运行程序的时候发生了什么

以下是站在较高抽象层面的描述,大概说明了运行程序的时候发生了什么。

  1. shell 通知内核运行 hello 程序
  2. 内核分配一块内存,加载程序到该内存
  3. 内核跳转到特定的内存地址,从该地址执行机器码
  4. 如果程序退出,内核释放分配的内存
  5. shell 得到程序的退出码
hello world
最后更新时间为2021年03月06日