第01章 C语言基础与编译
1.1 Hello World程序
让我们从最简单的C程序开始:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("Hello world!\n");
exit(0);
}
程序结构解析
- 头文件包含:
#include <stdio.h>- 标准输入输出库 - 主函数:
int main(void)- 程序入口 - 函数体: 用花括号
{}包围的代码块 - 退出程序:
exit(0)- 正常退出(返回0表示成功)
1.2 main函数的形式
C语言中main函数有多种形式(可能与编译器有关):
// 标准形式1:无参数
int main(void)
{
// ...
return 0;
}
// 标准形式2:带命令行参数
int main(int argc, char **argv) // 或 char *argv[]
{
// ...
return 0;
}
// 其他形式(不推荐)
void main(void);
void main(int argc, char **argv);
参数说明
argc: 命令行参数个数(argument count)argv: 命令行参数数组(argument vector)
1.3 程序退出方式
return与exit的区别
int main(void)
{
printf("Hello world!\n");
return 0; // 方式1:返回给父进程(shell)
// exit(0); // 方式2:退出程序,返回给父进程
}
查看程序返回值:
$ ./hello
Hello world!
$ echo $?
0
注意事项
⚠️ 如果main函数没有显式返回值:
- 程序不会报错
- 但返回值不是默认的0
- 实际返回的是最后一条语句的返回值
int main(void)
{
printf("Hello world!\n"); // printf返回字符串长度13
// 没有return或exit,返回值是13而不是0
}
输出:
$ ./test
Hello world!
$ echo $?
13
1.4 编译过程详解
四个编译阶段
C源文件需要经过4个步骤才能变成可执行文件:
源文件(.c) → 预处理(.i) → 编译(.s) → 汇编(.o) → 链接(可执行文件)
分步编译示例
# 查看源代码
$ cat hello.c
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
printf("Hello world!\n");
exit(0);
}
# 1. 预处理:展开宏定义、头文件等
$ gcc -E hello.c > hello.i
# 2. 编译:生成汇编代码
$ gcc -S hello.i # 生成hello.s
# 3. 汇编:生成目标文件
$ gcc -c hello.s # 生成hello.o
# 4. 链接:生成可执行文件
$ gcc hello.o -o hello
# 执行程序
$ ./hello
Hello world!
一步编译
# 直接生成可执行文件
$ gcc hello.c -o hello
# 使用make工具
$ make hello
cc hello.c -o hello
1.5 gcc编译器使用
基本编译选项
# 基本编译
gcc hello.c -o hello
# 开启所有警告(强烈推荐!)
gcc -Wall hello.c -o hello
# 指定C语言标准
gcc -std=c99 hello.c -o hello
gcc -std=c11 hello.c -o hello
-Wall选项的重要性
-Wall选项会显示所有警告信息,帮助发现潜在错误。
示例:隐式函数声明
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
int main()
{
FILE *fp;
fp = fopen("tmp", "r"); // 文件不存在
if (fp == NULL)
{
// 使用strerror但未包含string.h头文件
fprintf(stderr, "fopen():%s\n", strerror(errno));
exit(1);
}
puts("ok!");
exit(0);
}
不使用-Wall编译:
$ gcc a.c
# 编译通过,但运行报段错误
$ ./a.out
段错误
使用-Wall编译:
$ gcc a.c -Wall
a.c: 在函数'main'中:
a.c:11:3: 警告:隐式声明函数'strerror' [-Wimplicit-function-declaration]
fprintf(stderr, "fopen():%s\n", strerror(errno));
^
a.c:11:3: 警告:格式 '%s' expects argument of type 'char *', but argument 3
has type 'int' [-Wformat=]
解决方法: 包含正确的头文件
#include <string.h> // strerror函数需要此头文件
char *strerror(int errnum);
1.6 注释
C语言支持三种注释方式:
// 1. 行注释(C99标准)
int a = 10; // 这是行注释
// 2. 块注释
/*
* 这是块注释
* 可以跨多行
*/
int b = 20;
// 3. 预编译注释(用于调试)
#if 0
void func()
{
// 这段代码不会被编译
}
#endif
预编译注释的优势
- 可以嵌套注释
- 可以快速禁用大段代码
- 常用于调试
1.7 vim编辑器技巧
在vim中编写C代码的实用技巧:
- Ctrl+P: 自动补全
- Shift+K: 查看函数的man手册
# 在vim中,光标移到printf上,按Shift+K
# 等同于在终端执行:
$ man 3 printf
1.8 实践练习
练习1:编译并运行Hello World
- 创建hello.c文件
- 使用gcc -Wall编译
- 查看返回值
练习2:理解编译过程
使用分步编译,查看每个中间文件的内容:
gcc -E hello.c > hello.i
gcc -S hello.i
gcc -c hello.s
gcc hello.o -o hello
练习3:修复警告
编写代码故意不包含某些头文件,使用gcc -Wall找出问题并修复。
1.9 常见错误
错误1:未包含头文件
// 错误:使用printf但未包含stdio.h
int main()
{
printf("Hello\n"); // 隐式函数声明
return 0;
}
错误2:忘记return或exit
int main()
{
printf("Hello\n");
// 忘记return或exit,返回值不确定
}
错误3:语句后缺少分号
int main()
{
printf("Hello\n") // 缺少分号
return 0;
}
1.10 小结
本章学习了:
C程序的基本结构 main函数的多种形式 程序的编译过程(预处理→编译→汇编→链接) gcc编译器的使用,特别是-Wall选项 程序的退出方式(return vs exit) C语言的注释方式
重点提示:
- 养成使用
gcc -Wall的习惯 - 总是包含必要的头文件
- 理解编译的四个阶段
- main函数要有明确的返回值
下一章: 第02章 数据类型与变量