HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • C 语言入门教程

    • C语言系列教程
    • 第01章 基础概念与编译
    • 第02章 数据类型与变量
    • 第03章 运算符
    • 第04章 输入输出
    • 第05章 流程控制
    • 第06章 数组
    • 第07章 指针
    • 第08章 函数
    • 第09章 构造类型

第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

思考题

  1. 为什么要使用 gcc -Wall 编译选项?
  2. return 0 和 exit(0) 有什么区别?
  3. 预处理、编译、汇编、链接各自的作用是什么?
Prev
C语言系列教程
Next
第02章 数据类型与变量