月落乌啼霜满天,江枫渔火对愁眠。 --枫桥夜泊 / 夜泊枫江
引言:为什么选择C语言?
作为编程世界的基石语言,C语言自1972年诞生以来,一直是系统编程、嵌入式开发和性能关键型应用的首选。它不仅是操作系统的构建基础(如Linux、Windows内核),更是理解计算机底层工作原理的最佳入口。在这篇博客中,我将带你从最简单的"Hello World"开始,逐步深入C语言的奇妙世界。
第一章:Hello World - 程序员的仪式
每个C语言学习者的旅程都从这段经典代码开始:
#include <stdio.h> // 标准输入输出头文件
int main() { // 程序入口函数
printf("Hello, World!\n"); // 输出语句
return 0; // 程序正常结束
}
代码解析:
-
#include <stdio.h>
- 包含标准输入输出库,使我们可以使用printf()
函数 -
int main()
- 程序执行的起点,操作系统从这里开始运行程序 -
printf()
- 格式化输出函数,\n
表示换行符 -
return 0;
- 向操作系统返回0,表示程序成功执行
编译运行这个程序,你将在屏幕上看到:
Hello, World!
第二章:变量与数据类型 - 程序的基础元素
(1) 数据类型:
C语言提供了多种基本数据类型来存储不同种类的数据:
#include <stdio.h>
int main() {
// 整数类型
int age = 25;
short year = 2023;
long population = 7800000000L;
// 浮点类型
float pi = 3.14159f;
double precise_pi = 3.141592653589793;
// 字符类型
char initial = 'A';
// 布尔类型(C99)
_Bool is_cool = 1; // 1表示true,0表示false
// 输出变量值
printf("Age: %d\n", age);
printf("Pi: %.5f\n", pi);
printf("Initial: %c\n", initial);
printf("C is cool? %d\n", is_cool);
return 0;
}
数据类型总结表:
数据类型 | 名称 | 大小(字节) | 格式说明符 | 范围 |
---|---|---|---|---|
char | 字符数据类型 | 1 | %c | -128 到 127 |
short | 短整型 | 2 | %hd | -32,768 到 32,767 |
int | 整型 | 4 | %d | -2,147,483,648 到 2,147,483,647 |
long | 长整型 | 8 | %ld | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
float | 单精度浮点数 | 4 | %f | 约 ±3.4e±38(7位有效数字) |
double | 双精度浮点数 | 8 | %lf | 约 ±1.7e±308(15位有效数字) |
_Bool | 布尔类型(C99) | 1 | %d | 0 或 1 |
计算机存储基本单位
不同字符在计算机中所占大小是不同的;
计算机存储的基本单位是比特(bit),而最常用的单位是字节(Byte)。
以下是常见的存储单位及其关系:
1. 比特(bit,简写为b):是计算机中最小的存储单位,表示一个二进制位,只能是0或1。
2. 字节(Byte,简写为B):1字节等于8比特。字节是计算机中数据处理的基本单位。
更大的单位来表示更大的存储容量:
3. 千字节(Kilobyte, KB):1 KB = 1024 Byte(注意,这里用的是二进制,所以是2的10次方)。
4. 兆字节(Megabyte, MB):1 MB = 1024 KB = 1024 * 1024 Byte。
5. 吉字节(Gigabyte, GB):1 GB = 1024 MB = 1024 * 1024 * 1024 Byte。
6. 太字节(Terabyte, TB):1 TB = 1024 GB = 1024 * 1024 * 1024 * 1024 Byte。
7. 拍字节(Petabyte, PB):1 PB = 1024 TB,以此类推。
如需要了解更多的计算机存储单位的知识,欢迎大家学习以下文章,谢谢
(2) 变量与常量
变量:程序运行期间其值可以改变的量。变量需要定义数据类型,并且分配存储空间。
2.1 变量(Variable) - 程序中的可变数据
1. 变量定义与特性
-
定义:程序运行期间其值可以改变的量
-
特性:
-
需要明确的数据类型
-
必须先声明后使用
-
存储在内存中,有唯一地址
-
生命周期取决于作用域
-
#include <stdio.h>
int main() {
// 变量声明与初始化
int age = 25; // 整型变量
float height = 1.75f; // 浮点型变量
char grade = 'A'; // 字符型变量
char name[] = "Alice"; // 字符数组变量
// 变量值的修改
age = 26;
height = 1.77f;
grade = 'B';
printf("%s is %d years old, %.2f meters tall, grade %c\n",
name, age, height, grade);
return 0;
}
2. 变量命名规则
-
由字母、数字和下划线组成
-
不能以数字开头
-
区分大小写(
age
≠Age
) -
不能使用C语言关键字(如
int
,float
等) -
建议使用驼峰命名法(
studentAge
)或下划线命名法(student_age
)
3. 变量作用域
#include <stdio.h>
int globalVar = 100; // 全局变量 - 整个程序可见
void demoFunction() {
int localVar = 50; // 局部变量 - 仅在函数内可见
static int staticVar = 0; // 静态变量 - 函数调用间保持值
staticVar++;
printf("Static variable: %d\n", staticVar);
}
int main() {
int localVar = 20; // main函数的局部变量
printf("Global variable: %d\n", globalVar);
printf("Local variable: %d\n", localVar);
demoFunction(); // 输出: Static variable: 1
demoFunction(); // 输出: Static variable: 2
// 块作用域变量
{
int blockVar = 30;
printf("Block variable: %d\n", blockVar);
}
// printf("%d\n", blockVar); // 错误!blockVar在此不可见
return 0;
}
Global variable: 100
Local variable: 20
Static variable: 1
Static variable: 2
Block variable: 30
常量:程序运行期间其值不能改变的量。常量也分为不同的类型,如整型常量、浮点常量、字符常量、字符串常量等。另外,C语言中还可以用#define和const来定义常量。
2.2 常量(Constant) - 程序中的不变数据
1. 常量类型与定义方式
常量类型 | 定义方式 | 示例 |
---|---|---|
字面常量 | 直接值 | 42 , 3.14 , 'A' |
const 常量 | const 关键字 | const int MAX = 100; |
#define 常量 | 预处理器指令 | #define PI 3.14159 |
枚举常量 | enum 关键字 | enum Color {RED, GREEN} |
2. 常量使用示例
#include <stdio.h>
// 使用#define定义常量
#define MAX_USERS 100
#define PI 3.14159
#define WELCOME_MSG "Welcome to C Programming!"
// 使用const定义常量
const int MIN_AGE = 18;
const double GRAVITY = 9.8;
// 使用enum定义常量
enum Weekdays {MON=1, TUE, WED, THU, FRI, SAT, SUN};
int main() {
// 字面常量
printf("Integer literal: %d\n", 42);
printf("Float literal: %.2f\n", 3.14);
printf("Character literal: %c\n", 'X');
printf("String literal: %s\n", "Hello World!");
// const常量
printf("\nConst constants:\n");
printf("Minimum age: %d\n", MIN_AGE);
printf("Gravity constant: %.1f m/s²\n", GRAVITY);
// #define常量
printf("\n#define constants:\n");
printf("Max users: %d\n", MAX_USERS);
printf("PI value: %.5f\n", PI);
printf("%s\n", WELCOME_MSG);
// 枚举常量
printf("\nEnum constants:\n");
printf("Monday: %d\n", MON);
printf("Friday: %d\n", FRI);
// 尝试修改常量(将导致错误)
// MIN_AGE = 16; // 错误:不能修改const常量
// MAX_USERS = 200; // 错误:不能修改#define常量
return 0;
}
Integer literal: 42
Float literal: 3.14
Character literal: X
String literal: Hello World!
Const constants:
Minimum age: 18
Gravity constant: 9.8 m/s²
#define constants:
Max users: 100
PI value: 3.14159
Welcome to C Programming!
Enum constants:
Monday: 1
Friday: 5
3. 常量与指针的配合使用
#include <stdio.h>
int main() {
// 指向常量的指针(不能通过指针修改值)
const int ID = 12345;
const int *ptr1 = &ID;
// *ptr1 = 54321; // 错误:不能修改指向的值
// 常量指针(不能修改指针指向的地址)
int value = 100;
int *const ptr2 = &value;
*ptr2 = 200; // 可以修改值
printf("Value: %d\n", value);
// 不能修改指针指向
int another = 300;
// ptr2 = &another; // 错误:ptr2是常量指针
// 指向常量的常量指针
const float PI = 3.14159;
const float *const ptr3 = &PI;
// *ptr3 = 3.14; // 错误:不能修改值
// ptr3 = NULL; // 错误:不能修改指针
return 0;
}
2.3、 变量与常量的核心区别
特性 | 变量 | 常量 |
---|---|---|
值可变性 | 可以改变 | 不可改变 |
定义方式 | 数据类型 + 变量名 | const /#define /enum |
内存分配 | 运行时分配内存 | 可能直接替换(#define) |
作用域 | 取决于声明位置 | 取决于定义位置 |
典型用途 | 存储计算结果、用户输入等 | 存储固定值、配置参数等 |
2.4、实战应用场景
1. 配置管理系统(常量应用)
#include <stdio.h>
// 系统配置常量
#define MAX_CONNECTIONS 5
#define TIMEOUT 30
#define SERVER_IP "192.168.1.100"
const double VERSION = 2.5;
enum LogLevel {INFO, WARNING, ERROR};
void connectToServer() {
printf("Connecting to %s...\n", SERVER_IP);
printf("Timeout set to %d seconds\n", TIMEOUT);
}
int main() {
// 当前连接数(变量)
int currentConnections = 0;
printf("System Version: %.1f\n", VERSION);
printf("Max connections: %d\n", MAX_CONNECTIONS);
// 模拟连接
while(currentConnections < MAX_CONNECTIONS) {
connectToServer();
currentConnections++;
if(currentConnections % 10 == 0) {
printf("Current connections: %d\n", currentConnections);
}
}
printf("Reached maximum connections: %d\n", MAX_CONNECTIONS);
return 0;
}
2. 温度转换程序(变量应用)
#include <stdio.h>
// 常量定义
#define FAHRENHEIT_BASE 32.0
#define CONVERSION_FACTOR (5.0/9.0)
int main() {
// 变量定义
float celsius, fahrenheit;
printf("Enter temperature in Fahrenheit: ");
scanf("%f", &fahrenheit);
// 转换计算(使用变量)
celsius = (fahrenheit - FAHRENHEIT_BASE) * CONVERSION_FACTOR;
printf("%.1f°F = %.1f°C\n", fahrenheit, celsius);
// 使用变量存储不同温度值
float roomTempF = 68.0;
float roomTempC = (roomTempF - FAHRENHEIT_BASE) * CONVERSION_FACTOR;
printf("Room temperature: %.1f°F / %.1f°C\n", roomTempF, roomTempC);
return 0;
}
3. 枚举常量在状态机中的应用
#include <stdio.h>
// 定义电梯状态枚举常量
enum ElevatorState {
STOPPED,
MOVING_UP,
MOVING_DOWN,
DOOR_OPEN
};
const char* stateNames[] = {
"STOPPED",
"MOVING_UP",
"MOVING_DOWN",
"DOOR_OPEN"
};
int main() {
// 当前状态变量
enum ElevatorState currentState = STOPPED;
int currentFloor = 1;
int targetFloor = 5;
printf("Elevator initial state: %s at floor %d\n",
stateNames[currentState], currentFloor);
// 状态转换
if(currentFloor < targetFloor) {
currentState = MOVING_UP;
} else if(currentFloor > targetFloor) {
currentState = MOVING_DOWN;
}
printf("Elevator now: %s\n", stateNames[currentState]);
// 模拟移动
while(currentFloor != targetFloor) {
if(currentState == MOVING_UP) {
currentFloor++;
} else {
currentFloor--;
}
printf("Current floor: %d\n", currentFloor);
}
currentState = STOPPED;
printf("Arrived at floor %d. State: %s\n", currentFloor, stateNames[currentState]);
return 0;
}
2.5 、最佳实践与常见错误
变量使用准则:
-
尽量缩小变量的作用域
-
初始化所有变量
-
避免使用全局变量(除非必要)
-
使用有意义的变量名
-
注意变量类型转换
常量使用准则:
-
使用
const
替代#define
(类型安全) -
常量命名全大写(如
MAX_SIZE
) -
使用枚举代替魔数(magic numbers)
-
整型常量添加后缀(如
100L
表示long) -
浮点常量添加后缀(如
3.14f
表示float)
常见错误示例:
#include <stdio.h>
int main() {
// 错误1:未初始化变量
int uninitialized;
printf("%d\n", uninitialized); // 输出随机值
// 错误2:修改常量
const int FIXED = 100;
// FIXED = 200; // 编译错误
// 错误3:越界访问
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[5]); // 越界访问
// 错误4:混淆=和==
int x = 5;
if(x = 10) { // 总是为真(赋值而非比较)
printf("x is 10\n");
}
return 0;
}
理解变量与常量的区别是掌握C语言的基础。变量提供了程序运行时的灵活性,而常量则确保了核心逻辑的稳定性。合理使用两者,可以构建出既灵活又可靠的程序系统。
第三章:控制流程 - 程序的决策能力
控制流程是编程中的核心概念,它决定了程序如何根据条件执行不同的代码块,或者重复执行某些任务。在C语言中,主要有以下控制结构:
(1) 条件语句
1. if 语句
最基本的条件判断结构,当条件为真时执行代码块
#include <stdio.h>
int main() {
int age = 18;
// 基本if语句
if (age >= 18) {
printf("You are an adult.\n");
}
return 0;
}
2. if-else 语句
当条件为真时执行一个代码块,否则执行另一个代码块
#include <stdio.h>
int main() {
int number = 7;
if (number % 2 == 0) {
printf("%d is even.\n", number);
} else {
printf("%d is odd.\n", number);
}
return 0;
}
3. if-else if 语句
处理多个条件分支
#include <stdio.h>
int main() {
int score = 85;
if (score >= 90) {
printf("Grade: A\n");
} else if (score >= 80) {
printf("Grade: B\n");
} else if (score >= 70) {
printf("Grade: C\n");
} else if (score >= 60) {
printf("Grade: D\n");
} else {
printf("Grade: F\n");
}
return 0;
}
4. 三元运算符 (?:)
简洁的条件表达式,适合简单的条件赋值
#include <stdio.h>
int main() {
int a = 10, b = 20;
// 使用三元运算符找出最大值
int max = (a > b) ? a : b;
printf("Max value: %d\n", max);
// 更复杂的例子
printf("The number is %s\n", (a % 2 == 0) ? "even" : "odd");
return 0;
}
5. switch 语句
处理多个固定值的条件分支,比多个if-else更清晰
#include <stdio.h>
int main() {
char operator;
double num1, num2;
printf("Enter operator (+, -, *, /): ");
scanf(" %c", &operator);
printf("Enter two operands: ");
scanf("%lf %lf", &num1, &num2);
switch (operator) {
case '+':
printf("%.1lf + %.1lf = %.1lf\n", num1, num2, num1 + num2);
break;
case '-':
printf("%.1lf - %.1lf = %.1lf\n", num1, num2, num1 - num2);
break;
case '*':
printf("%.1lf * %.1lf = %.1lf\n", num1, num2, num1 * num2);
break;
case '/':
if (num2 != 0.0)
printf("%.1lf / %.1lf = %.1lf\n", num1, num2, num1 / num2);
else
printf("Error! Division by zero is not allowed.\n");
break;
default:
printf("Error! Invalid operator.\n");
}
return 0;
}
switch语句要点:
-
case
后必须是整型常量表达式 -
break
防止"贯穿"执行下一个case -
default
处理未匹配的情况
(2) 循环语句
1. while 循环
当条件为真时重复执行代码块
#include <stdio.h>
int main() {
int count = 1;
// 打印1到5
while (count <= 5) {
printf("%d ", count);
count++;
}
printf("\n");
return 0;
}
2. do-while 循环
先执行一次代码块,然后检查条件,适合需要至少执行一次的场景
#include <stdio.h>
int main() {
int number;
// 确保用户输入正数
do {
printf("Enter a positive number: ");
scanf("%d", &number);
if (number <= 0) {
printf("Invalid input. Please try again.\n");
}
} while (number <= 0);
printf("You entered: %d\n", number);
return 0;
}
3. for 循环
最常用的循环结构,适合已知循环次数的场景
#include <stdio.h>
int main() {
// 基本for循环
for (int i = 1; i <= 5; i++) {
printf("%d ", i);
}
printf("\n");
// 更复杂的例子:打印乘法表
for (int row = 1; row <= 9; row++) {
for (int col = 1; col <= row; col++) {
printf("%d*%d=%-2d ", col, row, col * row);
}
printf("\n");
}
return 0;
}
4. 无限循环与退出条件
使用while(1)
或for(;;)
创建无限循环,配合break
退出
#include <stdio.h>
int main() {
int sum = 0, number;
printf("Enter numbers to sum (0 to exit):\n");
// 无限循环,直到用户输入0
while (1) {
printf("Enter a number: ");
scanf("%d", &number);
if (number == 0) {
break; // 退出循环
}
sum += number;
}
printf("Total sum: %d\n", sum);
return 0;
}
(3) 循环控制语句
1. break 语句
立即退出当前循环或switch语句
#include <stdio.h>
int main() {
// 查找第一个能被7整除的数
for (int i = 1; i <= 100; i++) {
if (i % 7 == 0) {
printf("Found: %d\n", i);
break; // 找到后立即退出循环
}
}
return 0;
}
2. continue 语句
跳过当前循环的剩余部分,进入下一次迭代
#include <stdio.h>
int main() {
// 打印1-10的奇数
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // 跳过偶数
}
printf("%d ", i);
}
printf("\n");
return 0;
}
3. goto 语句
无条件跳转到指定标签(谨慎使用,容易导致代码混乱)
#include <stdio.h>
int main() {
int number;
printf("Enter a positive number: ");
scanf("%d", &number);
if (number <= 0) {
goto error; // 跳转到错误处理
}
printf("Valid number: %d\n", number);
return 0;
error: // 标签
printf("Error! Number must be positive.\n");
return 1;
}
(4) 嵌套控制结构
1. 嵌套if语句
#include <stdio.h>
int main() {
int age = 25;
char license = 'Y';
if (age >= 18) {
if (license == 'Y') {
printf("You can drive a car.\n");
} else {
printf("You need a driver's license.\n");
}
} else {
printf("You are too young to drive.\n");
}
return 0;
}
2. 循环中的条件判断
#include <stdio.h>
int main() {
// 找出1-100的质数
for (int num = 2; num <= 100; num++) {
int is_prime = 1; // 假设是质数
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) {
is_prime = 0; // 不是质数
break;
}
}
if (is_prime) {
printf("%d ", num);
}
}
printf("\n");
return 0;
}
(5) 控制结构最佳实践
-
避免深层嵌套:嵌套超过3层应考虑重构
-
使用switch替代多个if-else:提高可读性
-
循环边界检查:防止数组越界
-
避免无限循环:确保有退出条件
-
谨慎使用goto:只在错误处理等特殊场景使用
-
使用括号明确范围:即使只有一行代码
-
添加注释说明复杂逻辑
(6) 综合应用:简易计算器
#include <stdio.h>
int main() {
char choice;
double num1, num2;
do {
printf("\nCalculator Menu:\n");
printf("1. Addition (+)\n");
printf("2. Subtraction (-)\n");
printf("3. Multiplication (*)\n");
printf("4. Division (/)\n");
printf("5. Exit\n");
printf("Enter your choice: ");
scanf(" %c", &choice);
if (choice == '5') {
printf("Exiting calculator...\n");
break;
}
if (choice < '1' || choice > '5') {
printf("Invalid choice! Please try again.\n");
continue;
}
printf("Enter two numbers: ");
scanf("%lf %lf", &num1, &num2);
switch (choice) {
case '1':
printf("Result: %.2lf + %.2lf = %.2lf\n", num1, num2, num1 + num2);
break;
case '2':
printf("Result: %.2lf - %.2lf = %.2lf\n", num1, num2, num1 - num2);
break;
case '3':
printf("Result: %.2lf * %.2lf = %.2lf\n", num1, num2, num1 * num2);
break;
case '4':
if (num2 != 0.0) {
printf("Result: %.2lf / %.2lf = %.2lf\n", num1, num2, num1 / num2);
} else {
printf("Error! Division by zero is not allowed.\n");
}
break;
}
} while (1); // 无限循环,直到用户选择退出
return 0;
}
通过掌握这些控制结构,你可以编写出能够根据不同条件执行不同操作、重复执行任务直到满足条件的程序。这些是构建复杂程序的基础,建议通过实际编写各种小程序来加深理解。
第四章:函数 - 模块化编程的基石
函数是C语言程序的基本构建块,它允许你将代码组织成可重用、可维护的模块。本章将深入探讨函数的各个方面,并通过代码示例帮助你全面掌握函数的使用。
(1)函数的基本概念
1. 什么是函数?
-
完成特定任务的独立代码块
-
有输入(参数)和输出(返回值)
-
提高代码复用性和可读性
2. 函数的结构
返回类型 函数名(参数列表) {
// 函数体
return 返回值; // 可选
}
(2)函数的定义与调用
1. 简单函数示例
#include <stdio.h>
// 函数声明(告诉编译器函数的存在)
void greet();
// 函数定义
void greet() {
printf("Hello, welcome to C programming!\n");
}
int main() {
greet(); // 函数调用
return 0;
}
2. 带参数的函数
#include <stdio.h>
// 计算平方
int square(int num) {
return num * num;
}
int main() {
int number = 5;
int result = square(number);
printf("%d squared is %d\n", number, result);
return 0;
}
3. 带返回值的函数
#include <stdio.h>
// 查找最大值
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int main() {
int x = 10, y = 20;
printf("The max of %d and %d is %d\n", x, y, max(x, y));
return 0;
}
(3)函数参数详解
1. 值传递(默认行为)
#include <stdio.h>
// 值传递 - 不影响原始变量
void increment(int num) {
num++;
printf("Inside function: %d\n", num);
}
int main() {
int value = 5;
increment(value);
printf("Outside function: %d\n", value); // 仍为5
return 0;
}
2. 指针传递(修改原始变量)
#include <stdio.h>
// 指针传递 - 可以修改原始变量
void real_increment(int *num) {
(*num)++;
printf("Inside function: %d\n", *num);
}
int main() {
int value = 5;
real_increment(&value);
printf("Outside function: %d\n", value); // 变为6
return 0;
}
3. 数组作为参数
#include <stdio.h>
// 数组作为参数(实际传递指针)
void print_array(int arr[], int size) {
printf("Array elements: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 修改数组元素
void double_array(int *arr, int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2;
}
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
print_array(numbers, size);
double_array(numbers, size);
print_array(numbers, size);
return 0;
}
(4)函数原型与头文件
1. 函数原型的重要性
#include <stdio.h>
// 函数原型声明(放在文件顶部)
double calculate_average(int array[], int size);
int main() {
int scores[] = {85, 90, 78, 92, 88};
int size = sizeof(scores) / sizeof(scores[0]);
double avg = calculate_average(scores, size);
printf("Average score: %.2f\n", avg);
return 0;
}
// 函数定义(放在main后面)
double calculate_average(int array[], int size) {
int sum = 0;
for (int i = 0; i < size; i++) {
sum += array[i];
}
return (double)sum / size;
}
2. 使用头文件组织函数
math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
// 函数原型
int factorial(int n);
int gcd(int a, int b);
int lcm(int a, int b);
#endif
math_utils.c
#include "math_utils.h"
// 计算阶乘
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// 计算最大公约数
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
// 计算最小公倍数
int lcm(int a, int b) {
return (a * b) / gcd(a, b);
}
main.c
#include <stdio.h>
#include "math_utils.h"
int main() {
int a = 5, b = 7;
printf("%d! = %d\n", a, factorial(a));
printf("GCD of %d and %d is %d\n", a, b, gcd(a, b));
printf("LCM of %d and %d is %d\n", a, b, lcm(a, b));
return 0;
}
(5)递归函数
1. 基本递归:阶乘
#include <stdio.h>
int factorial(int n) {
// 基线条件
if (n <= 1) return 1;
// 递归调用
return n * factorial(n - 1);
}
int main() {
int num = 5;
printf("%d! = %d\n", num, factorial(num));
return 0;
}
2. 经典递归:斐波那契数列
#include <stdio.h>
int fibonacci(int n) {
// 基线条件
if (n <= 0) return 0;
if (n == 1) return 1;
// 递归调用
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main() {
printf("Fibonacci sequence: ");
for (int i = 0; i < 10; i++) {
printf("%d ", fibonacci(i));
}
printf("\n");
return 0;
}
3. 递归应用:汉诺塔问题
#include <stdio.h>
void hanoi(int n, char from, char to, char aux) {
if (n == 1) {
printf("Move disk 1 from %c to %c\n", from, to);
return;
}
hanoi(n - 1, from, aux, to);
printf("Move disk %d from %c to %c\n", n, from, to);
hanoi(n - 1, aux, to, from);
}
int main() {
int disks = 3;
printf("Solution for Tower of Hanoi with %d disks:\n", disks);
hanoi(disks, 'A', 'C', 'B');
return 0;
}
(6)高级函数技术
1. 函数指针
#include <stdio.h>
// 简单数学操作
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int main() {
// 声明函数指针
int (*operation)(int, int);
int x = 10, y = 5;
// 使用函数指针
operation = add;
printf("%d + %d = %d\n", x, y, operation(x, y));
operation = subtract;
printf("%d - %d = %d\n", x, y, operation(x, y));
operation = multiply;
printf("%d * %d = %d\n", x, y, operation(x, y));
return 0;
}
2. 回调函数
#include <stdio.h>
// 回调函数类型
typedef void (*Callback)(int);
// 处理数据的函数
void process_data(int data, Callback callback) {
printf("Processing data: %d\n", data);
int result = data * 2;
callback(result); // 调用回调函数
}
// 回调函数实现
void print_result(int result) {
printf("Result: %d\n", result);
}
void save_result(int result) {
printf("Saving result to file: %d\n", result);
}
int main() {
process_data(5, print_result);
process_data(10, save_result);
return 0;
}
3. 可变参数函数
#include <stdio.h>
#include <stdarg.h>
// 计算任意数量整数的平均值
double average(int count, ...) {
va_list args;
va_start(args, count);
double sum = 0;
for (int i = 0; i < count; i++) {
sum += va_arg(args, int);
}
va_end(args);
return sum / count;
}
int main() {
printf("Average of 1, 2, 3: %.2f\n", average(3, 1, 2, 3));
printf("Average of 10, 20, 30, 40: %.2f\n", average(4, 10, 20, 30, 40));
return 0;
}
(7)函数设计最佳实践
1. 函数设计原则
-
单一职责原则:一个函数只做一件事
-
合理命名:动词+名词(如
calculate_sum()
) -
参数控制:不超过5个参数
-
函数长度:不超过50行
-
避免副作用:减少全局变量使用
2. 错误处理技术
#include <stdio.h>
#include <stdlib.h>
// 安全除法函数
int safe_divide(int dividend, int divisor, int *result) {
if (divisor == 0) {
return 0; // 错误状态
}
*result = dividend / divisor;
return 1; // 成功状态
}
int main() {
int a = 10, b = 0;
int result;
if (safe_divide(a, b, &result)) {
printf("%d / %d = %d\n", a, b, result);
} else {
printf("Error: Division by zero!\n");
}
return 0;
}
(8)综合应用:计算器程序
#include <stdio.h>
#include <stdlib.h>
// 函数原型
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);
void display_menu();
int main() {
char choice;
double num1, num2, result;
while (1) {
display_menu();
printf("Enter your choice: ");
scanf(" %c", &choice);
if (choice == '5') break;
printf("Enter two numbers: ");
scanf("%lf %lf", &num1, &num2);
switch (choice) {
case '1':
result = add(num1, num2);
break;
case '2':
result = subtract(num1, num2);
break;
case '3':
result = multiply(num1, num2);
break;
case '4':
if (num2 != 0) {
result = divide(num1, num2);
} else {
printf("Error: Division by zero!\n");
continue;
}
break;
default:
printf("Invalid choice!\n");
continue;
}
printf("Result: %.2lf\n\n", result);
}
printf("Calculator closed. Goodbye!\n");
return 0;
}
// 显示菜单
void display_menu() {
printf("\nCalculator Menu:\n");
printf("1. Addition\n");
printf("2. Subtraction\n");
printf("3. Multiplication\n");
printf("4. Division\n");
printf("5. Exit\n");
}
// 加法
double add(double a, double b) {
return a + b;
}
// 减法
double subtract(double a, double b) {
return a - b;
}
// 乘法
double multiply(double a, double b) {
return a * b;
}
// 除法
double divide(double a, double b) {
return a / b;
}
(9)函数调试技巧
1. 调试打印函数
#include <stdio.h>
// 调试宏
#ifdef DEBUG
#define DEBUG_PRINT(fmt, ...) printf("DEBUG: " fmt, ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
// 复杂计算函数
int complex_calculation(int a, int b) {
DEBUG_PRINT("Entering complex_calculation with a=%d, b=%d\n", a, b);
int result = 0;
for (int i = 0; i < a; i++) {
result += b * i;
DEBUG_PRINT("Iteration %d: result=%d\n", i, result);
}
DEBUG_PRINT("Exiting complex_calculation with result=%d\n", result);
return result;
}
int main() {
// 编译时加上 -DDEBUG 启用调试输出
int x = complex_calculation(3, 5);
printf("Final result: %d\n", x);
return 0;
}
2. 断言检查
#include <stdio.h>
#include <assert.h>
// 计算阶乘(带断言)
int factorial(int n) {
// 前置条件:n必须是非负整数
assert(n >= 0);
if (n == 0) return 1;
return n * factorial(n - 1);
}
int main() {
printf("5! = %d\n", factorial(5));
// printf("-1! = %d\n", factorial(-1)); // 会触发断言失败
return 0;
}
通过本章的学习,你应该掌握了函数的定义、使用和高级技巧。函数是C语言模块化编程的核心,合理使用函数可以:
提高代码复用性
简化复杂问题
增强代码可读性
便于团队协作
简化调试过程
在接下来的编程实践中,尝试将复杂任务分解为多个小函数,遵循单一职责原则,你会逐渐体会到函数带来的巨大优势。
第五章:数组 - 数据集合的处理
数组是存储多个相同类型元素的连续内存空间:
#include <stdio.h>
int main() {
// 一维数组
int numbers[5] = {2, 4, 6, 8, 10};
printf("Array elements: ");
for(int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
// 二维数组(矩阵)
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
printf("\nMatrix:\n");
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}
第六章:指针 - C语言的精髓
指针是C语言最强大也最容易出错的概念:
#include <stdio.h>
int main() {
int num = 42;
int *ptr = # // ptr指向num的地址
printf("Value of num: %d\n", num); // 42
printf("Address of num: %p\n", &num); // 内存地址
printf("Value via pointer: %d\n", *ptr); // 42
// 修改指针指向的值
*ptr = 100;
printf("New value of num: %d\n", num); // 100
// 指针与数组
int arr[3] = {10, 20, 30};
int *arr_ptr = arr; // 指向数组第一个元素
printf("\nArray via pointer:\n");
for(int i = 0; i < 3; i++) {
printf("Element %d: %d\n", i, *(arr_ptr + i));
}
return 0;
}
指针使用注意事项:
-
始终初始化指针(设置为
NULL
或有效地址) -
检查指针是否为
NULL
后再解引用 -
注意指针算术(加减操作基于指向类型的大小)
-
避免野指针(指向已释放内存的指针)
第七章:结构体 - 自定义数据类型
结构体允许我们将不同类型的数据组合在一起:
#include <stdio.h>
#include <string.h>
// 定义结构体类型
struct Student {
int id;
char name[50];
float gpa;
};
int main() {
// 创建结构体实例
struct Student student1;
student1.id = 101;
strcpy(student1.name, "Alice Johnson");
student1.gpa = 3.8f;
// 结构体指针
struct Student *student_ptr = &student1;
// 访问结构体成员
printf("Student ID: %d\n", student1.id);
printf("Student Name: %s\n", student_ptr->name); // 指针使用->运算符
printf("GPA: %.2f\n", student_ptr->gpa);
return 0;
}
学习路线图:从Hello World到精通
下一步计划
在接下来的博客中,我将深入探讨:
-
动态内存管理(malloc、calloc、realloc、free)
-
文件操作与数据持久化
-
C语言标准库深度解析
-
常用数据结构实现(链表、栈、队列、二叉树)
-
多文件编程与模块化设计
-
调试技巧与常见错误分析
结语
C语言的学习就像攀登一座高山 - 从Hello World这个起点出发,每一步都充满挑战但也收获满满。记住编程大师的名言:
"The only way to learn a new programming language is by writing programs in it." - Dennis Ritchie (C语言之父)
准备好你的编译器,让我们在代码的世界里继续探索!欢迎在评论区分享你的第一个C程序和学习心得。