基础语法
本章介绍 C 语言的基础语法,包括变量、数据类型、运算符和表达式。这些是编写任何 C 程序的基础。
程序结构
一个标准的 C 程序由预处理指令、函数定义和语句组成。让我们从一个简单的程序开始:
#include <stdio.h> // 预处理指令:包含标准输入输出头文件
int main(void) { // main 函数:程序入口
printf("Hello, C!\n"); // 函数调用:输出字符串
return 0; // 返回语句:表示程序正常结束
}
程序执行的流程:
- 预处理器处理
#include指令,将头文件内容插入 - 程序从
main函数开始执行 printf函数输出字符串到控制台return 0表示程序正常结束
main 函数是每个 C 程序的入口点,操作系统调用它来启动程序。返回值 0 表示成功,非零值表示出错。
注释
注释用于解释代码,编译器会忽略注释内容。C 语言支持两种注释方式:
/*
* 多行注释
* 可以跨越多行
* 用于详细的说明
*/
// 单行注释,从 // 到行末的内容都是注释
int main(void) {
// 这是一个单行注释
printf("Hello\n"); // 行末注释
/*
这是多行注释
可以写很多内容
*/
return 0;
}
注释的最佳实践:
- 解释为什么这样做,而不是做了什么
- 保持注释与代码同步更新
- 避免无意义的注释
变量
变量是存储数据的命名内存位置。使用变量前必须先声明。
变量声明与初始化
int age; // 声明一个整型变量
age = 25; // 赋值
int score = 100; // 声明并初始化
int a, b, c; // 同时声明多个变量
int x = 1, y = 2; // 声明并初始化多个变量
C99 标准之后,可以在代码块的任何位置声明变量,但建议在使用前就近声明。
变量命名规则
- 只能包含字母、数字和下划线
- 必须以字母或下划线开头
- 不能使用关键字(如
int、if、while等) - 区分大小写(
age和Age是不同的变量)
int player_score; // 合法:下划线分隔
int PlayerScore; // 合法:驼峰命名
int _count; // 合法:下划线开头
int MAX_SIZE; // 合法:常量风格
// int 2nd_place; // 非法:数字开头
// int my-var; // 非法:包含连字符
// int int; // 非法:使用关键字
命名建议:
- 使用有意义的名称,描述变量的用途
- 保持命名风格一致
- 常量使用全大写字母
- 变量名使用小写字母,单词间用下划线分隔
数据类型
C 语言是强类型语言,每个变量都有确定的数据类型。数据类型决定了变量占用的内存大小和可以存储的值的范围。
整数类型
整数类型用于存储不带小数的数值:
| 类型 | 存储大小 | 值范围 |
|---|---|---|
char | 1 字节 | -128 到 127 或 0 到 255 |
short | 2 字节 | -32,768 到 32,767 |
int | 4 字节 | -2,147,483,648 到 2,147,483,647 |
long | 4 或 8 字节 | 至少 -2,147,483,648 到 2,147,483,647 |
long long | 8 字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
可以使用 sizeof 运算符查看类型或变量的大小:
#include <stdio.h>
int main(void) {
printf("char: %zu 字节\n", sizeof(char));
printf("short: %zu 字节\n", sizeof(short));
printf("int: %zu 字节\n", sizeof(int));
printf("long: %zu 字节\n", sizeof(long));
printf("long long: %zu 字节\n", sizeof(long long));
return 0;
}
有符号与无符号
整数类型默认是有符号的,可以存储正数和负数。使用 unsigned 关键字声明无符号类型,只能存储非负数,但正数范围扩大一倍:
int signed_num = -100; // 有符号整数
unsigned int unsigned_num = 100; // 无符号整数
unsigned short us = 65535; // 无符号短整型
unsigned long ul = 4000000000UL; // 无符号长整型
无符号类型的值范围:
| 类型 | 值范围 |
|---|---|
unsigned char | 0 到 255 |
unsigned short | 0 到 65,535 |
unsigned int | 0 到 4,294,967,295 |
unsigned long long | 0 到 18,446,744,073,709,551,615 |
浮点类型
浮点类型用于存储带小数的数值:
| 类型 | 存储大小 | 有效数字 | 值范围 |
|---|---|---|---|
float | 4 字节 | 6-7 位 | 约 ±3.4E38 |
double | 8 字节 | 15-16 位 | 约 ±1.7E308 |
long double | 10-16 字节 | 18-19 位或更多 | 取决于实现 |
float pi = 3.14159f; // 单精度,f 后缀
double e = 2.718281828; // 双精度,默认类型
long double ld = 3.14159265358979L; // 扩展精度,L 后缀
浮点数的注意事项:
- 浮点数运算可能存在精度误差
- 不要直接比较两个浮点数是否相等
double是浮点运算的默认类型
字符类型
char 类型用于存储单个字符,实际上存储的是字符的 ASCII 码值:
char ch = 'A'; // 字符常量,单引号
char digit = '7'; // 字符 '7',不是数字 7
char newline = '\n'; // 转义字符
printf("字符: %c, ASCII码: %d\n", ch, ch); // 输出: 字符: A, ASCII码: 65
常用转义字符:
| 转义字符 | 含义 |
|---|---|
\n | 换行 |
\t | 水平制表符 |
\\ | 反斜杠 |
\' | 单引号 |
\" | 双引号 |
\0 | 空字符(字符串结束符) |
布尔类型
C99 标准引入了 _Bool 类型和 <stdbool.h> 头文件:
#include <stdbool.h>
bool is_valid = true; // 真
bool is_empty = false; // 假
在 C99 之前,使用整数表示布尔值:0 表示假,非零表示真。
类型限定符
const 限定符表示变量的值不能被修改:
const int MAX_SIZE = 100; // 常量,必须初始化
const double PI = 3.14159; // 常量
// MAX_SIZE = 200; // 错误:不能修改常量
volatile 限定符告诉编译器该变量可能被意外修改,不要优化:
volatile int hardware_register; // 硬件寄存器
运算符
运算符用于执行各种操作,如算术运算、比较、逻辑运算等。
算术运算符
int a = 10, b = 3;
int sum = a + b; // 加法: 13
int diff = a - b; // 减法: 7
int product = a * b; // 乘法: 30
int quotient = a / b; // 整数除法: 3(截断小数部分)
int remainder = a % b; // 取模: 1
double x = 10.0, y = 3.0;
double result = x / y; // 浮点除法: 3.333...
整数除法会截断小数部分,如果需要精确结果,至少有一个操作数应为浮点数。
自增与自减运算符
int i = 5;
i++; // 后置自增,i 变为 6
++i; // 前置自增,i 变为 7
i--; // 后置自减,i 变为 6
--i; // 前置自减,i 变为 5
// 前置与后置的区别
int a = 5;
int b = a++; // b = 5, a = 6(先赋值,后自增)
int c = ++a; // c = 7, a = 7(先自增,后赋值)
关系运算符
关系运算符用于比较两个值,返回 1(真)或 0(假):
int a = 10, b = 5;
int r1 = (a == b); // 等于: 0(假)
int r2 = (a != b); // 不等于: 1(真)
int r3 = (a > b); // 大于: 1(真)
int r4 = (a < b); // 小于: 0(假)
int r5 = (a >= b); // 大于等于: 1(真)
int r6 = (a <= b); // 小于等于: 0(假)
注意区分 =(赋值)和 ==(相等比较)。
逻辑运算符
int a = 1, b = 0;
int r1 = (a && b); // 逻辑与: 0(假)
int r2 = (a || b); // 逻辑或: 1(真)
int r3 = !a; // 逻辑非: 0(假)
int r4 = !b; // 逻辑非: 1(真)
逻辑运算符的短路求值:
int a = 0;
// 如果第一个操作数为假,&& 后面的表达式不会执行
if (a != 0 && 10 / a > 1) { // 不会发生除零错误
// ...
}
位运算符
位运算符直接操作整数的二进制位:
unsigned int a = 0b1100; // 12
unsigned int b = 0b1010; // 10
unsigned int r1 = a & b; // 按位与: 0b1000 (8)
unsigned int r2 = a | b; // 按位或: 0b1110 (14)
unsigned int r3 = a ^ b; // 按位异或: 0b0110 (6)
unsigned int r4 = ~a; // 按位取反
unsigned int r5 = a << 2; // 左移: 0b110000 (48)
unsigned int r6 = a >> 2; // 右移: 0b11 (3)
位运算的常见应用:
// 设置某一位
flags |= (1 << 3);
// 清除某一位
flags &= ~(1 << 3);
// 切换某一位
flags ^= (1 << 3);
// 检查某一位
if (flags & (1 << 3)) {
// 第 3 位为 1
}
赋值运算符
int a = 10;
a += 5; // 等价于 a = a + 5,结果: 15
a -= 3; // 等价于 a = a - 3,结果: 12
a *= 2; // 等价于 a = a * 2,结果: 24
a /= 4; // 等价于 a = a / 4,结果: 6
a %= 4; // 等价于 a = a % 4,结果: 2
a &= 0x0F; // 等价于 a = a & 0x0F
a |= 0x01; // 等价于 a = a | 0x01
条件运算符(三目运算符)
条件运算符是 C 语言中唯一的三元运算符:
int a = 10, b = 20;
int max = (a > b) ? a : b; // 如果 a > b,取 a,否则取 b
等价于:
int max;
if (a > b) {
max = a;
} else {
max = b;
}
sizeof 运算符
sizeof 返回类型或变量的大小(字节数):
int a = 10;
printf("int 大小: %zu\n", sizeof(int)); // 类型
printf("a 的大小: %zu\n", sizeof(a)); // 变量
printf("表达式大小: %zu\n", sizeof(a + 1.0)); // 表达式
运算符优先级
当表达式中有多个运算符时,按优先级顺序计算。优先级从高到低:
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 1 | () [] -> . | 左到右 |
| 2 | ! ~ ++ -- + - * & sizeof | 右到左 |
| 3 | * / % | 左到右 |
| 4 | + - | 左到右 |
| 5 | << >> | 左到右 |
| 6 | < <= > >= | 左到右 |
| 7 | == != | 左到右 |
| 8 | & | 左到右 |
| 9 | ^ | 左到右 |
| 10 | | | 左到右 |
| 11 | && | 左到右 |
| 12 | || | 左到右 |
| 13 | ?: | 右到左 |
| 14 | = += -= *= /= %= &= ^= |= <<= >>= | 右到左 |
| 15 | , | 左到右 |
建议:不确定时使用括号明确优先级。
类型转换
隐式类型转换
编译器自动进行的类型转换:
int a = 10;
double b = 3.5;
double c = a + b; // a 自动转换为 double,结果: 13.5
char ch = 'A';
int code = ch; // char 自动转换为 int
转换规则:较小的类型自动转换为较大的类型。
显式类型转换(强制转换)
使用 (类型) 语法进行强制转换:
int a = 10, b = 3;
double result = (double)a / b; // 结果: 3.333...
double x = 3.7;
int y = (int)x; // 截断,结果: 3
表达式与语句
表达式
表达式是由运算符和操作数组成的式子,可以计算出一个值:
10 // 常量表达式
a // 变量表达式
a + b // 算术表达式
a > b // 关系表达式
a && b // 逻辑表达式
func() // 函数调用表达式
语句
语句是程序执行的基本单位,以分号结尾:
int a = 10; // 声明语句
a = a + 1; // 赋值语句
printf("hello"); // 函数调用语句
; // 空语句
复合语句(代码块)用花括号括起来:
{
int a = 10;
a = a + 1;
printf("%d\n", a);
}
输入输出基础
printf 输出
printf 用于格式化输出:
int age = 25;
double pi = 3.14159;
char ch = 'A';
printf("年龄: %d\n", age); // 输出整数
printf("圆周率: %.2f\n", pi); // 输出浮点数,保留2位小数
printf("字符: %c\n", ch); // 输出字符
printf("字符串: %s\n", "hello"); // 输出字符串
常用格式说明符:
| 格式符 | 说明 |
|---|---|
%d | 有符号十进制整数 |
%u | 无符号十进制整数 |
%f | 浮点数 |
%lf | double 类型 |
%c | 单个字符 |
%s | 字符串 |
%x | 十六进制(小写) |
%X | 十六进制(大写) |
%o | 八进制 |
%p | 指针地址 |
%% | 百分号 |
宽度和精度控制:
printf("%5d\n", 42); // 宽度5,右对齐: " 42"
printf("%-5d\n", 42); // 宽度5,左对齐: "42 "
printf("%05d\n", 42); // 宽度5,用0填充: "00042"
printf("%.2f\n", 3.14159); // 精度2位: "3.14"
printf("%8.2f\n", 3.14); // 宽度8,精度2: " 3.14"
scanf 输入
scanf 用于读取用户输入:
int age;
printf("请输入年龄: ");
scanf("%d", &age); // 注意 & 取地址符
printf("你的年龄是: %d\n", age);
double price;
printf("请输入价格: ");
scanf("%lf", &price); // double 使用 %lf
printf("价格是: %.2f\n", price);
char name[50];
printf("请输入姓名: ");
scanf("%s", name); // 数组名本身就是地址,不需要 &
printf("你好, %s\n", name);
scanf 返回成功读取的项目数,可以用于错误检查:
int a, b;
int result = scanf("%d %d", &a, &b);
if (result == 2) {
printf("读取成功: %d, %d\n", a, b);
} else {
printf("输入格式错误\n");
}
小结
本章介绍了 C 语言的基础语法:
- 程序结构:预处理指令、main 函数、语句
- 变量:声明、初始化、命名规则
- 数据类型:整数、浮点、字符、布尔类型
- 运算符:算术、关系、逻辑、位运算、赋值
- 类型转换:隐式转换和强制转换
- 基本输入输出:printf 和 scanf
下一章将学习 控制流程,包括条件语句和循环语句。