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)引入进来,这样你才能用 printf、scanf 这些函数。
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>,提供了 bool、true、false。不引入这个头文件的话,也可以直接用 int,0 表示假,非 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 : 值2 是 if-else 的简写形式,条件为真返回值1,否则返回值2:
int a = 10, b = 20;
int max = (a > b) ? a : b; // max = 20位运算
位运算在处理二进制相关问题时非常高效,在算法题中可能会遇到。详细的位运算原理可以参考 常用的位操作,这里了解一下基本用法就行:
控制流
条件判断 if / else
for 循环
for 循环是最常用的循环,由三部分组成:for (初始化; 条件; 更新)。
while 循环
while 循环适合循环次数不确定的场景:
do-while 和 while 的区别是:do-while 先执行一次再判断条件,所以循环体至少会执行一次。实际中 do-while 用得很少,了解一下就行。
break 和 continue
break 跳出整个循环,continue 跳过当前这轮、进入下一轮:
switch 语句
switch 适合对一个变量做多个值的判断,比一长串 if-else 更清晰:
注意每个 case 后面要加 break,否则会"穿透"到下一个 case 继续执行。
小结
这篇介绍了 C 语言的基础语法:程序结构、输入输出、数据类型、运算符和控制流。这些是所有 C 代码的基石。
下一篇我们来详细讲讲 C 语言的 头文件与标准库,看看 #include 到底在做什么,以及标准库都给我们提供了哪些好用的工具。