C 基础语法

C 语言诞生于 1972 年,是一门非常经典的编程语言,大部分人在大学阶段都学过。

不过说实话,现在实际开发中直接用 C 的场景已经不太多了,大家更多会选择更高级的语言。原因很简单:C 的标准库太简陋了,像内存分配和释放这种事情,更高级的语言会由编译器或运行时自动处理,而 C 需要你手动管理。手动管理虽然给了你最大的自由度,但也非常繁琐,容易出错。

如果只是刷题和学习算法,其实更建议用 C++,因为 C++ 有完善的标准库(动态数组、哈希表、排序等开箱即用),写起来方便很多。不过有些场景(比如考研机试)可能要求只能用 C,所以这里还是完整介绍一下 C 语言的基础。

本站所有的算法代码都配有 C 语言版本,你学完基础之后可以在刷题过程中熟悉和应用这门语言。

下面我们就开始学习 C 语言的基础语法。

Hello World 与程序结构

先看一个最简单的 C 程序:

拆解一下这个结构:

#include <stdio.h>       // 引入标准输入输出库

int main() {             // 程序入口函数
    // 你的代码写在这里
    return 0;            // 返回 0 表示程序正常结束
}

#include <stdio.h> 是预处理指令,告诉编译器把标准输入输出库(standard input/output)引入进来,这样你才能用 printfscanf 这些函数。

int main() 是程序的入口函数,C 程序运行时会自动找到并执行这个函数。int 表示这个函数返回一个整数,return 0 表示程序正常结束。

每条语句末尾要加分号 ;,这是 C 语言的规矩。漏了分号编译器会报错,这是新手最常犯的错误之一。

标准输出

printf 用于在控制台打印内容,通过格式化字符串来控制输出格式:

常用的格式化占位符:

占位符说明示例
%d整数(int)printf("%d", 42);
%ld长整数(long)printf("%ld", 123L);
%lld长长整数(long long)printf("%lld", 123LL);
%f浮点数(double)printf("%f", 3.14);
%c单个字符printf("%c", 'A');
%s字符串printf("%s", "hello");

\n 是换行符。注意 printf 不会自动换行,需要手动加 \n

读取输入

scanf 用来从控制台读取用户输入:

scanf 的关键点:变量前面要加 & 符号。具体原因涉及到指针,后面会讲,现在先记住这个规则。字符数组(如 name)比较特殊,不用加 &

scanf 的常用格式:

格式说明
%d读取一个整数
%lld读取一个 long long
%f读取一个 float%lf 读取 double
%s读取一个字符串(遇到空格或换行就停)
%c读取一个字符

上面的示例中,左下角的 stdin 区域就是模拟的用户输入,你可以修改输入内容再运行试试。

如果需要读取一整行(包括空格),可以用 fgets

循环读取到输入结束

有些题目的输入数据量不确定,需要一直读取到输入结束(EOF)。把 scanf 放到 while 的条件中就行:

当输入结束时,scanf 会返回 EOF(或者返回值不等于期望读取的数量),循环自动退出。

变量与数据类型

基本类型

C 语言最常用的数据类型:

类型说明示例
int整数(32位)int a = 10;
long long长整数(64位)long long b = 9999999999LL;
double浮点数(小数)double c = 3.14;
char单个字符char d = 'A';

几个注意点:

long long 类型的字面量要加 LL 后缀(如 9999999999LL),否则编译器可能把它当成 int,超出范围就会出错。

char 用单引号 'A',字符串用双引号 "Hello",这两个不能搞混。

C 语言原本没有布尔类型,C99 标准引入了 <stdbool.h>,提供了 booltruefalse。不引入这个头文件的话,也可以直接用 int0 表示假,非 0 表示真。

变量声明

C 语言是静态类型语言,变量必须先声明类型才能使用:

// 先声明,后赋值
int x;
x = 10;

// 声明的同时赋值
int y = 20;

// 一行声明多个同类型变量
int a = 1, b = 2, c = 3;

常量

const 声明常量,声明后就不能修改了:

const int MAX_SIZE = 100;
const double PI = 3.14159;

数组

数组用来存储一组相同类型的数据,通过下标(索引)访问,下标从 0 开始:

数组大小在创建后就固定了,不能动态增删元素。算法题中一般根据题目的数据范围开一个足够大的数组就行。

类型转换

小类型可以自动转换为大类型(不丢精度),反过来需要强制转换:

特别注意整数除法:1 / 2 的结果是 0 而不是 0.5,因为两个 int 相除结果还是 int,小数部分直接丢掉。这在算法题中是个常见的坑。

运算符

算术运算符

取余运算符 % 在算法题中非常常用,比如判断奇偶(n % 2 == 0)、循环数组索引(i % n)等。

比较与逻辑运算符

比较运算符返回 1(真)或 0(假):==(等于)、!=(不等于)、<><=>=

逻辑运算符用于组合条件:&&(与)、||(或)、!(非)。

三元运算符

条件 ? 值1 : 值2if-else 的简写形式,条件为真返回值1,否则返回值2:

int a = 10, b = 20;
int max = (a > b) ? a : b;  // max = 20

位运算

位运算在处理二进制相关问题时非常高效,在算法题中可能会遇到。详细的位运算原理可以参考 常用的位操作,这里了解一下基本用法就行:

控制流

条件判断 if / else

for 循环

for 循环是最常用的循环,由三部分组成:for (初始化; 条件; 更新)

while 循环

while 循环适合循环次数不确定的场景:

do-whilewhile 的区别是:do-while 先执行一次再判断条件,所以循环体至少会执行一次。实际中 do-while 用得很少,了解一下就行。

break 和 continue

break 跳出整个循环,continue 跳过当前这轮、进入下一轮:

switch 语句

switch 适合对一个变量做多个值的判断,比一长串 if-else 更清晰:

注意每个 case 后面要加 break,否则会"穿透"到下一个 case 继续执行。

小结

这篇介绍了 C 语言的基础语法:程序结构、输入输出、数据类型、运算符和控制流。这些是所有 C 代码的基石。

下一篇我们来详细讲讲 C 语言的 头文件与标准库,看看 #include 到底在做什么,以及标准库都给我们提供了哪些好用的工具。