第01章 基础概念与编译
本章介绍C语言的基础概念、main函数的不同形式、gcc编译过程以及编译器警告信息的解读
1.1 C程序基本结构
一个最简单的C程序结构如下:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
// ... 程序代码
exit(0);
}
关键要点:
#include <stdio.h>: 包含标准输入输出库#include <stdlib.h>: 包含标准库函数int main(void): 程序入口函数exit(0): 正常退出程序
1.2 main函数的不同形式
main函数有多种形式(可能跟编译器有关):
void main(void);
void main(int argc, char **argv); // 或 char *argv[]
int main(int argc, char **argv);
int main(void);
推荐使用:
#include <stdio.h>
#include <stdlib.h>
int main()
{
// ...
exit(0);
}
1.3 gcc编译器:源文件→预处理→编译→汇编→链接→可执行文件
完整编译过程示例
[root@192.168.25.99 c_program]# cat hello.c
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
printf("Hello world!\n");
exit(0);
}
分步编译
cat hello.c
gcc -E hello.c > hello.i ## 预处理
gcc -S hello.i ## 编译 生成 .s 文件
gcc -c hello.s ## 汇编 生成 .o 文件
gcc hello.o -o hello ## 链接 生成可执行文件
./hello
一步编译
[root@192.168.25.99 c_program]# gcc hello.c -o hello ## 一步编译
使用make
[root@192.168.25.99 c_program]# make hello ## 使用make一步编译
cc hello.c -o hello
编译结果
[root@192.168.25.99 c_program]# ll
总用量 16
-rwxr-xr-x. 1 root root 8408 8月 1 03:24 hello
-rw-r--r--. 1 root root 94 8月 1 03:09 hello.c
1.4 vim编辑器小技巧
在vim编辑器下:
ctrl+p可以快速补全shift+k可以调出对应的man手册
1.5 gcc -Wall 提示信息解读
重要提示: 使用 gcc -Wall 可以开启所有警告,帮助发现潜在错误。
实例:缺少头文件导致的警告
[root@192.168.25.99 c_program]# cat a.c
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
int main()
{
FILE * fp;
fp = fopen("tmp", "r"); ## "tmp" 文件不存在
if (fp == NULL)
{
fprintf(stderr, "fopen():%s\n", strerror(errno));
exit(1);
}
puts("ok!");
exit(0);
}
不使用-Wall编译
[root@192.168.25.99 c_program]# gcc a.c # 不会有任何错误提示,执行a.out提示段错误
使用-Wall编译
[root@192.168.25.99 c_program]# 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=]
问题分析:
- 上面提示 fprintf 第三个参数返回值不匹配 "char *"
- 这个警告构成了 error,但是gcc只提示为warning
- 程序运行时报 段错误
解决方法
查看man手册:
[root@192.168.25.99 c_program]# man strerror
#include <string.h>
char *strerror(int errnum);
根据man手册,知道 strerror 返回的是 "char *",返回数据类型没有问题。但是需要引入 <string.h> 头文件。
引入 string.h 之后,程序运行正确。
1.6 main函数的返回值
return 0 vs exit(0)
[root@192.168.25.99 c_program]# cat test.c
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
printf("Hello world!\n");
return 0; // 给父进程看的,本例中是给 shell 看的
//exit(0); // 给父进程看的
}
[root@192.168.25.99 c_program]# gcc test.c -o test
[root@192.168.25.99 c_program]# ./test
Hello world!
[root@192.168.25.99 c_program]# echo $?
0
没有返回值的情况
当没有返回值的时候,虽然程序没报错,但不是默认返回0,因为main返回值是 int,main返回printf()的返回值。
[root@192.168.25.99 c_program]# cat test.c
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
printf("Hello world!\n"); // 返回字符串长度
}
[root@192.168.25.99 c_program]# gcc test.c -o test
[root@192.168.25.99 c_program]# ./test
Hello world!
[root@192.168.25.99 c_program]# echo $?
13
1.7 C语言注释
C语言支持三种注释方式:
// 行注释: //
/* 段注释: */
// 预编译注释:
#if 0
func() {}
#endif
思考题
- 为什么要使用
gcc -Wall编译选项? - return 0 和 exit(0) 有什么区别?
- 预处理、编译、汇编、链接各自的作用是什么?