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)
运行程序的时候发生了什么
以下是站在较高抽象层面的描述,大概说明了运行程序的时候发生了什么。
- shell 通知内核运行 hello 程序
- 内核分配一块内存,加载程序到该内存
- 内核跳转到特定的内存地址,从该地址执行机器码
- 如果程序退出,内核释放分配的内存
- shell 得到程序的退出码
最后更新时间为2021年03月06日