搜索
File 2026.10.19编程

C 语言基础知识

Date / 日期
Updated / 更新
2026.10.19
Reading / 阅读
47 Min
Words / 字数
9214
Category / 分类
编程C 语言
Tags / 标签
#编程#C 语言

C 语言基础知识

· 更新于 2026年10月19日· 约 47 分钟· 9214 字

说在前面

我迄今为止学习过许多的编程语言,比如有 C/C++ 、 Python 、 lua 以及现在正在一点一点学习的 Rust ,但是要说其中我自认为掌握的最好的,我觉得只有 C 和 Python 能排得上号。

而在学习了各种弯弯绕绕的编程教程,写了大大小小的一些项目之后,我觉得我在这几个编程语言中感受到了一些共性的东西,而这种“共性”也在我学习其他编程语言的过程中带来了很大的帮助(除了 Rust ,我到现在还是不太习惯 Rust 的那一套,可能等我进一步学习之后会有更进一步的想法吧),所以这篇文章就是我觉得应该在当下先把我心中的这种感觉记录下来,当然也会提到一些 C 语言的基础知识。

NOTE

免责声明

  1. 这篇文章只代表我的个人看法,不一定正确(应该说肯定不全正确),对编程的学习不应该拘泥于教程里的东西,最有用的永远是直接上手完成一些程序的编写,完成一两个完整的项目永远是比看几十篇教程更加有用的。
  2. 这篇文章的内容是基于我在使用的操作系统(Linux)编写的,在其他的系统和平台,内容中的一些细节可能会有所不同,但我相信大体的框架和思路应当是大差不差的。

1. 关于编程

主流困境

每个人刚开始学习编程的时候,总是会遇到各种各样的配置问题。我刚开始学习变成的时候还在使用 Windows 系统,当时看到了一本变成相关的书,于是一时兴起想要学习 C 语言的编程。不过当时看着教程都是一脸懵:

MSVC 是什么?IDE 是什么?文件扩展名又是什么?

最后总算跟着教程一步一步配置好之后,我的学习热情已经消磨了一大半,直到几天之后才总算又然起了热情开始真的学习 C 语言的基础知识。

什么是编程?

其实编程并没有那么高大上,笼统一点来讲,编程只有三个步骤:

TikZ 源码
\node[draw, align=center] (A) at (0,0) {代码文件};
\node[draw, align=center] (B) at (3,0) {编译器};
\node[draw, align=center] (C) at (6,0) {可执行文件};
\draw[-{Stealth[length=3mm]}] (A) -- (B);
\draw[-{Stealth[length=3mm]}] (B) -- (C);
\node[draw, align=center] (D) at (1.5,-1) {报错};
\draw[-{Stealth[length=3mm]}] (B) -- (3,-1) -- (D);
\draw[-{Stealth[length=3mm]}] (D) -- (0,-1) -- (A);

编写代码

编写代码本质上就是进行文本编辑,代码文件本质上就是一个个有不同的“拓展名”的文本文件。

NOTE

这里可以多提一嘴“文件拓展名”的概念:

我不是很喜欢 windows 对文件的“拓展名”的表现形式,他表现的好像文件拓展名就是绝对的决定一个文件的性质东西(想一下每次我们修改一个文件的拓展名的时候 windows 都会弹出来的那个恼人的弹窗),相比之下,linux 系统的这种“一切皆文件”的处理逻辑就比较清晰了:

在 linux 中,文件只分为几种类型(普通文件、文件目录等),我们可以用 ls -l 来查看每个文件的具体信息

Bash
$ ls -l
.rwxr-xr-x 16k neowang 15 Oct 01:35 󰡯 main
.rw-r--r-- 80 neowang 15 Oct 01:37 main.c

可以看到最前面会列出文件的权限,main 和 main.c 的分别是 .rwxr-xr-x 和 .rw-r--r-- ,其中的第一个字符表示的就是文件类型,在这个例子中,两个文件的第一个字符是 . ,意思就是他们都是普通文件(文本文件)。

这时候有人就会问了: 不对啊,这里的 main.c 明明是C语言的代码文件,main 明明是编译出来的可执行文件

这种说法并没有错,只不过更高一层,在最底层的系统眼里,这两个都只是简单的文本文件。我们可以尝试着用文本编辑器打开这两个文件

Bash
$ vim main.c
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
Bash
$ vim main
# 输出太长了我就不全放出来了

运行这个就可以看到,文本编辑器是可以打开 main 文件的,只不过里面的编码不是我们可以理解的

编译代码

上一节提到了代码文件本质就是文本文件,那为什么我们写的代码文件计算机就不能直接执行,而编译出来的文件计算机就可以正确的执行呢?

其实,我们再回去看刚才的权限,main.c 是 .rw-r--r-- ,main 是 .rwxr-xr-x ,会发现这两个的区别就只有中间的几个 x 。欸,没错!这个 x 就是代表了一个文件在 linux 系统中是 “可执行的” 。如果不相信的话,我们可以尝试给 main.c 文件添加这个 x 权限:

Bash
$ chmod +x main.c
$ ls -la
.rwxr-xr-x 16k neowang 15 Oct 01:35 󰡯 main
.rwxr-xr-x 80 neowang 15 Oct 01:37 main.c
$ ./main.c
./main.c: 行 3: 未预期的记号 "(" 附近有语法错误
./main.c: 行 3: `int main() {'
$ chmod -x main.c

你看,当我们用 chmod 命令给 main.c 文件添加了 x 权限之后,它的权限确实就变成了 .rwxr-xr-x ,并且也可以执行了(只不过执行之后计算机并不认得我们写的代码(也就是文本)

而这就是为什么我们需要编译,编译这一步中,编译器会根据自己的算法,把我们写的、人类可读(brainfuck 可不可读就是另一回事了)的代码文件,编译成给计算机看的、机器能听懂的指令,并且给它加上可执行权限,这样我们执行 ./main 的时候,计算机就能够按照我们想的来进行一些行为了。

NOTE

对于一些解释性的编程语言,我们也可以理解成编译器会直接实时地把我们的代码一条一条的翻译给计算机执行,所以本质上大差不差

修改代码

上面提到的所有过程都是建立在我们的代码没有出现问题的前提上的,那么如果我们在编写代码的过程中出现了错误怎么办呢?

  1. 可能是我们的代码不符合这门语言的语法规范

    这种时候就要用到编译器的另一项极其重要的功能了:报错

    不同语言的编译器报错的信息有所不同,有的语言的编译器能在报错的同时提供具有很大帮助的说明和建议(比如还没发布的 Jai ,我很期待这门语言),而有的编译器就只能提供基础的错误和位置;甚至同一个语言的不同编译器的报错能力都有所不同。但是无论如何,一门成熟的编程语言的珍贵的编译器都应当具有相当程度的报错的能力,而根据这些报错的信息,我们程序员就可以尝试定位到我们编写的代码中存在的错误,并且尝试去纠正她

  2. 可能是编译出来的结果不符合我们的预期

    对于一些小项目,我们基本上都可以把执行的行为和我们编写的代码对应上,并且在出现非预期的行为的时候定位并修改代码。

    如果是面对比较大型的项目,错误难以直观的定位的时候,就需要用到 “调试” 的概念,调试这个概念涵盖了很多的行为,这里我暂时不做太多展开,后面会逐渐提到一些具体的调试方法。

总结一下

上面说了这么多,其实总结起来就一句话,编程的过程本质就是编写文本文件。

我们可以用任何文本编辑器(大到完整的 Visual Studio,小到 windows 默认的记事本)来编写任意拓展名的文件,最后交给编译器来编译。

而我们挑选不同的文本编辑器、使用不同的文件拓展名这一行为,只是为了让我们编程的过程更加地轻松,更有效率(一些文本编辑器会根据文件拓展名来判断其中代码的语言,并且给用户提供代码的语法高亮)

所以,在学习编程之前,我们实际上最需要的,就只有一个对应语言的编译器(linux 一般用自带的 gcc ,GNU C/C++ Compiler)

C 语言代码的基础知识

main() 函数

每个 C 语言的代码都有一个叫做 main 的函数,这个函数的返回值必须是整数(为了让操作系统知道程序是否执行完毕),所以,一个最简单的 C 语言程序大概长成这样:

main.c
int main() {
return 0;
}

有的人一开始可能不太理解为什么一定要写成这样:为什么一定要返回一个整数?返回的可以是其他的整数吗?

其实是可以的,这里的 return 是程序运行到 main 函数的最后将一个数值返回给操作系统的意思,我们可以用 echo $? (输出上一个程序的推出状态码)来验证:

Bash
$ ./main
$ echo $?
0

所以,我们就可以用这个返回码来实现最简单的整数计算器:

main.c
int main() {
int a = 5;
int b = 12;
int c = 4;
return a * b / 2;
}
Bash
$ gcc main.c -o main && ./main
$ echo $?
30

可以看到,返回的确实是 (5 * 12)/4 = 30

NOTE

不过这里需要说明的是,我们一般在操作系统中约定俗成的认为,返回码为 0 的时候代表程序正常执行结束了,返回其他值(常用的是 -1 )的时候代表程序没有正常的结束,所以我们在这里实现的这种用返回码来运算的例子只能用来帮助我们理解 C 语言的代码组成,在真实的生产环境中不推荐这么操作。

头文件

上面的这种操作返回码来实现计算的方式虽然简单,但是有很多的弊端:

  • 系统看到返回码不为 0 ,可能会不断的提示程序出错

  • 我们运行程序不能直接看到结果的输出,必须用 echo 命令来读返回码

  • 返回码只能是整数,无法进行(理论上能,但是太过复杂)小数等运算

这种时候,一个能让程序直接吧运算的结果输出给我们的函数,就很有吸引力了

我们先输入下面这样的代码,之后我会解释:

main.c
#include<stdio.h>
int main() {
int a = 5;
int b = 12;
int c = 4;
printf("The result is : %d\n", a*b/c);
return 0;
}

然后我们编译并且执行它:

Bash
$ gcc main.c -o main && ./main
The result is : 15
$ echo $?
0

可以看到,它确实输出了正确的结果

接下来,我来解释一下这串代码

  1. printf :这是 C 语言的格式化输出(f 代表的就是 format)函数,它能把你传给他的第一个字符串格式化并且输出到我们的命令行
  2. %d :这些是可以被 printf 函数处理的占位符,每一个这样的占位符都要在后面对应的给出具体的变量值,这样 printf 函数才能正确的格式化输出的内容
  3. #include<stdio.h> :这个就是我们说的头文件,一个头文件里面包含了一些函数和方法的声明,而 printf 函数的声明就是在 stdio.h 这个头文件里面(standard input output 标准输入输出)

Comment评论

添加新评论