引言:为什么选择C语言作为编程起点
C语言作为一门诞生于20世纪70年代的高级编程语言,至今仍然是计算机科学教育和系统级开发的基石。它不仅是许多现代编程语言(如C++、Java、C#)的前身,更是理解计算机底层工作原理的最佳桥梁。学习C语言能够帮助初学者建立坚实的编程基础,掌握内存管理、指针操作等核心概念,为后续学习更高级的语言和技术打下坚实基础。
C语言的优势在于其高效性、灵活性和可移植性。它允许程序员直接操作硬件资源,因此被广泛应用于操作系统、嵌入式系统、游戏开发和高性能计算等领域。通过系统学习C语言,你将不仅学会编写代码,更能理解程序是如何在计算机硬件上运行的,这种深入的理解是其他高级语言难以提供的。
第一部分:C语言基础语法入门
1.1 C语言程序的基本结构
每个C程序都由一个或多个函数组成,其中必须包含一个名为
main
的主函数。程序从
main
函数开始执行。下面是一个最简单的C程序示例:
#include
int main() {
// printf是标准库函数,用于输出文本到控制台
printf(“Hello, World!\n”);
return 0;
}
代码解析:
#include
:预处理指令,包含标准输入输出头文件,使我们可以使用
printf
等函数。
int main()
:主函数声明,
int
表示函数返回一个整数值。
{ … }
:函数体,包含实际要执行的代码。
printf(“Hello, World!\n”);
:输出字符串,
\n
表示换行符。
return 0;
:返回状态码,0表示程序正常结束。
1.2 数据类型与变量
C语言提供了多种基本数据类型,用于存储不同种类的数据:
#include
int main() {
// 整数类型
int age = 25;
unsigned int positiveNumber = 100;
// 浮点类型
float height = 1.75f; // f后缀表示float类型
double weight = 65.5; // double精度更高
// 字符类型
char grade = ‘A’;
// 布尔类型(C99标准引入)
_Bool isStudent = 1; // 1表示true,0表示false
// 输出各种类型的变量
printf(“年龄: %d\n”, age);
printf(“身高: %.2f米\n”, height);
printf(“成绩等级: %c\n”, grade);
return 0;
}
关键概念:
变量声明
:
数据类型 变量名;
变量初始化
:
数据类型 变量名 = 初始值;
类型修饰符
:
short
、
long
、
signed
、
unsigned
可以修饰整数类型改变其范围
类型转换
:C语言支持隐式类型转换和显式类型转换(强制类型转换)
1.3 运算符与表达式
C语言提供了丰富的运算符,包括算术、关系、逻辑、位运算等:
#include
int main() {
int a = 10, b = 3;
// 算术运算符
printf(“a + b = %d\n”, a + b); // 13
printf(“a – b = %d\n”, a – b); // 7
printf(“a * b = %d\n”, a * b); // 30
printf(“a / b = %d\n”, a / b); // 3(整数除法)
printf(“a %% b = %d\n”, a % b); // 1(取余)
// 关系运算符
printf(“a > b: %d\n”, a > b); // 1(真)
printf(“a == b: %d\n”, a == b); // 0(假)
// 逻辑运算符
int x = 1, y = 0;
printf(“x && y: %d\n”, x && y); // 0(逻辑与)
printf(“x || y: %d\n”, x || y); // 1(逻辑或)
printf(“!x: %d\n”, !x); // 0(逻辑非)
// 自增自减运算符
int c = 5;
printf(“c++: %d\n”, c++); // 输出5,然后c变为6
printf(“++c: %d\n”, ++c); // c先变为7,然后输出7
return 0;
}
1.4 控制流语句
1.4.1 条件语句
#include
int main() {
int score;
printf(“请输入你的分数(0-100): “);
scanf(“%d”, &score); // 从键盘读取输入
// if-else if-else 结构
if (score >= 90) {
printf(“优秀\n”);
} else if (score >= 80) {
printf(“良好\n”);
} else if (score >= 60) {
printf(“及格\n”);
} else {
printf(“不及格\n”);
}
// switch-case 语句
char grade;
switch (score / 10) {
case 10:
case 9:
grade = ‘A’;
break;
case 8:
grade = ‘B’;
break;
case 7:
grade = ‘C’;
break;
case 6:
grade = ‘D’;
break;
default:
grade = ‘F’;
}
printf(“等级: %c\n”, grade);
return 0;
}
1.4.2 循环语句
#include
int main() {
// for循环示例:计算1到100的和
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
printf("1到100的和: %d\n", sum);
// while循环示例:读取用户输入直到输入负数
int number;
printf("请输入数字(输入负数结束):\n");
while (1) { // 无限循环
scanf("%d", &number);
if (number < 0) {
break; // 跳出循环
}
printf("你输入了: %d\n", number);
}
// do-while循环示例:至少执行一次
int input;
do {
printf("请输入一个正数: ");
scanf("%d", &input);
} while (input <= 0);
printf("有效输入: %d\n", input);
// 嵌套循环:打印乘法表
printf("9x9乘法表:\n");
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
printf("%dx%d=%d\t", i, j, i*j);
}
printf("\n");
}
return 0;
}
第二部分:C语言核心概念深入
2.1 数组与字符串
2.1.1 一维数组
#include
int main() {
// 数组声明与初始化
int scores[5] = {85, 92, 78, 96, 88};
// 访问数组元素
printf(“第三个学生的成绩: %d\n”, scores[2]); // 78
// 遍历数组
printf(“所有学生成绩: “);
for (int i = 0; i < 5; i++) {
printf("%d ", scores[i]);
}
printf("\n");
// 计算平均分
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += scores[i];
}
printf("平均分: %.1f\n", (float)sum / 5);
return 0;
}
2.1.2 二维数组
#include
int main() {
// 3行4列的二维数组,存储学生成绩
int grades[3][4] = {
{78, 85, 92, 88},
{95, 89, 91, 87},
{82, 79, 85, 90}
};
// 遍历二维数组
printf(“学生成绩表:\n”);
for (int i = 0; i < 3; i++) {
printf("学生%d: ", i+1);
for (int j = 0; j < 4; j++) {
printf("%d ", grades[i][j]);
}
printf("\n");
}
// 计算每个学生的平均分
for (int i = 0; i < 3; i++) {
int sum = 0;
for (int j = 0; j < 4; j++) {
sum += grades[i][j];
}
printf("学生%d的平均分: %.1f\n", i+1, (float)sum / 4);
}
return 0;
}
2.1.3 字符串处理
#include
#include
int main() {
// 字符串的两种定义方式
char name1[] = “Alice”; // 自动计算长度,包括’\0′
char name2[10] = “Bob”; // 指定长度
char name3[20]; // 空字符串
// 字符串输入输出
printf(“请输入你的名字: “);
scanf(“%s”, name3); // 注意:scanf遇到空格会停止
printf(“你好, %s!\n”, name3);
// 字符串长度
printf(“name1的长度: %lu\n”, strlen(name1));
// 字符串复制
char name4[20];
strcpy(name4, name1);
printf(“复制后的字符串: %s\n”, name4);
// 字符串连接
char fullName[50] = “Hello, “;
strcat(fullName, name1);
printf(“连接后的字符串: %s\n”, fullName);
// 字符串比较
int result = strcmp(name1, name2);
if (result == 0) {
printf(“字符串相等\n”);
} else if (result < 0) {
printf("%s 在 %s 之前\n", name1, name2);
} else {
printf("%s 在 %s 之后\n", name1, name2);
}
// 手动实现字符串复制函数
void my_strcpy(char *dest, const char *src) {
while ((*dest++ = *src++) != '\0');
}
char test1[20];
my_strcpy(test1, "ManualCopy");
printf("手动复制结果: %s\n", test1);
return 0;
}
2.2 函数
2.2.1 函数定义与调用
#include
// 函数声明(原型)
int add(int a, int b);
void printMessage(char *msg);
int factorial(int n);
int main() {
// 函数调用
int sum = add(5, 3);
printf(“5 + 3 = %d\n”, sum);
printMessage(“Hello from main!”);
int num = 5;
printf(“%d! = %d\n”, num, factorial(num));
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
void printMessage(char *msg) {
printf(“Message: %s\n”, msg);
}
// 递归函数:计算阶乘
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
2.2.2 函数参数传递方式
#include
// 值传递:函数内修改不影响原变量
void incrementByValue(int x) {
x++;
printf(“函数内x: %d\n”, x);
}
// 地址传递:函数内修改会影响原变量
void incrementByReference(int *x) {
(*x)++;
printf(“函数内*x: %d\n”, *x);
}
// 数组作为参数
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 交换两个数的值(必须使用指针)
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int num = 10;
// 值传递
incrementByValue(num);
printf("调用后num: %d\n", num); // 仍然是10
// 地址传递
incrementByReference(&num);
printf("调用后num: %d\n", num); // 变为11
// 数组传递
int arr[] = {1, 2, 3, 4, 5};
printArray(arr, 5);
// 交换测试
int x = 5, y = 8;
printf("交换前: x=%d, y=%d\n", x, y);
swap(&x, &y);
printf("交换后: x=%d, y=%d\n", x, y);
return 0;
}
2.3 指针
2.3.1 指针基础
#include
int main() {
int num = 42;
int *ptr = # // ptr指向num的地址
printf(“num的值: %d\n”, num); // 42
printf(“num的地址: %p\n”, &num); // 地址值
printf(“ptr的值: %p\n”, ptr); // 与&num相同
printf(“ptr指向的值: %d\n”, *ptr); // 42
// 通过指针修改变量
*ptr = 100;
printf(“通过指针修改后num: %d\n”, num); // 100
// 指针的算术运算
int arr[] = {10, 20, 30, 40, 50};
int *p = arr; // 指向数组第一个元素
printf(“数组元素: “);
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 等价于arr[i]
}
printf("\n");
return 0;
}
2.3.2 指针与数组、函数
#include
// 使用指针遍历数组
void printArrayUsingPointer(int *arr, int size) {
for (int i = 0; i < size; i++) {
// arr[i] 等价于 *(arr + i)
printf("%d ", *(arr + i));
}
printf("\n");
}
// 动态内存分配示例
#include
int main() {
// 指针与数组的关系
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf(“通过指针访问数组:\n”);
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d, *(ptr + %d) = %d\n",
i, arr[i], i, *(ptr + i));
}
// 动态分配内存
int *dynamicArray = (int*)malloc(5 * sizeof(int));
if (dynamicArray == NULL) {
printf("内存分配失败\n");
return 1;
}
// 初始化动态数组
for (int i = 0; i < 5; i++) {
dynamicArray[i] = i * 10;
}
printArrayUsingPointer(dynamicArray, 5);
// 释放内存
free(dynamicArray);
return 0;
}
2.4 结构体与共用体
2.4.1 结构体定义与使用
#include
#include
// 定义学生结构体
struct Student {
int id;
char name[50];
float score;
char grade;
};
// 定义日期结构体
struct Date {
int year;
int month;
int day;
};
// 定义员工结构体(包含另一个结构体)
struct Employee {
int id;
char name[50];
struct Date hireDate;
float salary;
};
int main() {
// 结构体变量声明与初始化
struct Student stu1 = {101, “张三”, 85.5, ‘B’};
struct Student stu2;
// 访问结构体成员
stu2.id = 102;
strcpy(stu2.name, “李四”);
stu2.score = 92.0;
stu2.grade = ‘A’;
// 输出结构体信息
printf(“学生1: ID=%d, 姓名=%s, 分数=%.1f, 等级=%c\n”,
stu1.id, stu1.name, stu1.score, stu1.grade);
printf(“学生2: ID=%d, 姓名=%s, 分数=%.1f, 等级=%c\n”,
stu2.id, stu2.name, stu2.score, stu2.grade);
// 结构体数组
struct Student class[3] = {
{101, “王五”, 88.0, ‘B’},
{102, “赵六”, 95.5, ‘A’},
{103, “钱七”, 76.0, ‘C’}
};
// 计算班级平均分
float sum = 0;
for (int i = 0; i < 3; i++) {
sum += class[i].score;
}
printf("班级平均分: %.1f\n", sum / 3);
// 结构体指针
struct Student *stuPtr = &stu1;
printf("通过指针访问: %s, %.1f\n", stuPtr->name, stuPtr->score);
// 包含结构体的结构体
struct Employee emp = {
1001,
“孙八”,
{2020, 5, 15},
8000.0
};
printf(“员工: %s, 入职日期: %d-%d-%d, 薪水: %.2f\n”,
emp.name, emp.hireDate.year, emp.hireDate.month,
emp.hireDate.day, emp.salary);
return 0;
}
2.4.2 共用体与枚举
#include
// 枚举类型
enum Weekday {
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
};
// 共用体:同一内存区域存储不同类型数据
union Data {
int i;
float f;
char str[20];
};
int main() {
// 枚举使用
enum Weekday today = Wednesday;
printf(“今天是星期%d\n”, today + 1); // 输出3
// 共用体使用
union Data data;
data.i = 10;
printf(“data.i: %d\n”, data.i);
data.f = 220.5;
printf(“data.f: %.1f\n”, data.f);
// 注意:共用体只能存储一个成员的值
strcpy(data.str, “C Programming”);
printf(“data.str: %s\n”, data.str);
printf(“data.i: %d (被覆盖了)\n”, data.i); // 值已被覆盖
printf(“共用体大小: %lu 字节\n”, sizeof(data));
return 0;
}
第三部分:数据结构与算法
3.1 基础数据结构实现
3.1.1 链表
#include
#include
// 链表节点结构
struct Node {
int data;
struct Node *next;
};
// 创建新节点
struct Node* createNode(int data) {
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf(“内存分配失败\n”);
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 在链表头部插入节点
void insertAtHead(struct Node **head, int data) {
struct Node *newNode = createNode(data);
newNode->next = *head;
*head = newNode;
}
// 在链表尾部插入节点
void insertAtTail(struct Node **head, int data) {
struct Node *newNode = createNode(data);
if (*head == NULL) {
*head = newNode;
return;
}
struct Node *temp = *head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
}
// 打印链表
void printList(struct Node *head) {
struct Node *temp = head;
while (temp != NULL) {
printf(“%d -> “, temp->data);
temp = temp->next;
}
printf(“NULL\n”);
}
// 删除链表中的节点
void deleteNode(struct Node **head, int key) {
struct Node *temp = *head, *prev = NULL;
// 如果头节点就是要删除的节点
if (temp != NULL && temp->data == key) {
*head = temp->next;
free(temp);
return;
}
// 查找要删除的节点
while (temp != NULL && temp->data != key) {
prev = temp;
temp = temp->next;
}
// 如果没找到
if (temp == NULL) {
printf(“节点 %d 未找到\n”, key);
return;
}
// 从链表中移除节点
prev->next = temp->next;
free(temp);
}
// 释放整个链表
void freeList(struct Node **head) {
struct Node *current = *head;
struct Node *next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
*head = NULL;
}
int main() {
struct Node *head = NULL;
// 插入节点
insertAtTail(&head, 1);
insertAtTail(&head, 2);
insertAtTail(&head, 3);
insertAtHead(&head, 0);
insertAtTail(&head, 4);
printf(“初始链表: “);
printList(head);
// 删除节点
deleteNode(&head, 2);
printf(“删除节点2后: “);
printList(head);
deleteNode(&head, 0);
printf(“删除节点0后: “);
printList(head);
// 释放链表
freeList(&head);
printf(“链表已释放\n”);
return 0;
}
3.1.2 栈(Stack)
#include
#include
#include
#define MAX_SIZE 100
// 栈结构
struct Stack {
int data[MAX_SIZE];
int top;
};
// 初始化栈
void initStack(struct Stack *s) {
s->top = -1;
}
// 检查栈是否为空
bool isEmpty(struct Stack *s) {
return s->top == -1;
}
// 检查栈是否已满
bool isFull(struct Stack *s) {
return s->top == MAX_SIZE – 1;
}
// 入栈
bool push(struct Stack *s, int value) {
if (isFull(s)) {
printf(“栈已满,无法入栈\n”);
return false;
}
s->data[++(s->top)] = value;
return true;
}
// 出栈
bool pop(struct Stack *s, int *value) {
if (isEmpty(s)) {
printf(“栈为空,无法出栈\n”);
return false;
}
*value = s->data[(s->top)–];
return true;
}
// 查看栈顶元素
bool peek(struct Stack *s, int *value) {
if (isEmpty(s)) {
return false;
}
*value = s->data[s->top];
return true;
}
// 打印栈
void printStack(struct Stack *s) {
if (isEmpty(s)) {
printf(“栈为空\n”);
return;
}
printf(“栈内容(从顶到底): “);
for (int i = s->top; i >= 0; i–) {
printf(“%d “, s->data[i]);
}
printf(“\n”);
}
int main() {
struct Stack s;
initStack(&s);
// 入栈测试
push(&s, 10);
push(&s, 20);
push(&s, 30);
printStack(&s);
// 查看栈顶
int topValue;
if (peek(&s, &topValue)) {
printf(“栈顶元素: %d\n”, topValue);
}
// 出栈测试
int value;
while (pop(&s, &value)) {
printf(“出栈: %d\n”, value);
}
return 0;
}
3.1.3 队列(Queue)
#include
#include
#include
#define MAX_SIZE 100
// 队列结构
struct Queue {
int data[MAX_SIZE];
int front;
int rear;
int size;
};
// 初始化队列
void initQueue(struct Queue *q) {
q->front = 0;
q->rear = -1;
q->size = 0;
}
// 检查队列是否为空
bool isQueueEmpty(struct Queue *q) {
return q->size == 0;
}
// 检查队列是否已满
bool isQueueFull(struct Queue *q) {
return q->size == MAX_SIZE;
}
// 入队
bool enqueue(struct Queue *q, int value) {
if (isQueueFull(q)) {
printf(“队列已满,无法入队\n”);
return false;
}
q->rear = (q->rear + 1) % MAX_SIZE;
q->data[q->rear] = value;
q->size++;
return true;
}
// 出队
bool dequeue(struct Queue *q, int *value) {
if (isQueueEmpty(q)) {
printf(“队列为空,无法出队\n”);
return false;
}
*value = q->data[q->front];
q->front = (q->front + 1) % MAX_SIZE;
q->size–;
return true;
}
// 打印队列
void printQueue(struct Queue *q) {
if (isQueueEmpty(q)) {
printf(“队列为空\n”);
return;
}
printf(“队列内容: “);
int i = q->front;
int count = 0;
while (count < q->size) {
printf(“%d “, q->data[i]);
i = (i + 1) % MAX_SIZE;
count++;
}
printf(“\n”);
}
int main() {
struct Queue q;
initQueue(&q);
// 入队测试
enqueue(&q, 10);
enqueue(&q, 20);
enqueue(&q, 30);
printQueue(&q);
// 出队测试
int value;
if (dequeue(&q, &value)) {
printf(“出队: %d\n”, value);
}
printQueue(&q);
// 继续出队
while (!isQueueEmpty(&q)) {
dequeue(&q, &value);
printf(“出队: %d\n”, value);
}
return 0;
}
3.2 基础排序算法
3.2.1 冒泡排序
#include
// 冒泡排序实现
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
// 每次循环将最大的元素"冒泡"到最后
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 优化版本:添加提前退出标志
void bubbleSortOptimized(int arr[], int n) {
bool swapped;
for (int i = 0; i < n - 1; i++) {
swapped = false;
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
// 如果这一轮没有发生交换,说明数组已经有序
if (!swapped) break;
}
}
// 打印数组
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);
printf("原始数组: ");
printArray(arr, n);
bubbleSort(arr, n);
printf("排序后数组: ");
printArray(arr, n);
return 0;
}
3.2.2 选择排序
#include
// 选择排序实现
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
// 假设当前位置是最小的
int minIndex = i;
// 在未排序部分找到最小元素
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 将最小元素交换到当前位置
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);
printf("原始数组: ");
printArray(arr, n);
selectionSort(arr, n);
printf("排序后数组: ");
printArray(arr, n);
return 0;
}
3.2.3 插入排序
#include
// 插入排序实现
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i]; // 当前要插入的元素
int j = i - 1;
// 将大于key的元素向后移动
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j–;
}
// 插入key到正确位置
arr[j + 1] = key;
}
}
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {12, 11, 13, 5, 6};
int n = sizeof(arr) / sizeof(arr[0]);
printf("原始数组: ");
printArray(arr, n);
insertionSort(arr, n);
printf("排序后数组: ");
printArray(arr, n);
return 0;
}
3.2.4 快速排序
#include
// 交换两个元素
void swap(int* a, int* b) {
int t = *a;
*a = *b;
*b = t;
}
// 分区函数:Lomuto分区方案
int partition(int arr[], int low, int high) {
int pivot = arr[high]; // 选择最后一个元素作为基准
int i = low – 1; // 较小元素的索引
for (int j = low; j < high; j++) {
// 如果当前元素小于等于基准
if (arr[j] <= pivot) {
i++;
swap(&arr[i], &arr[j]);
}
}
// 将基准放到正确位置
swap(&arr[i + 1], &arr[high]);
return i + 1;
}
// 快速排序主函数
void quickSort(int arr[], int low, int high) {
if (low < high) {
// pi是分区索引,arr[pi]现在在正确位置
int pi = partition(arr, low, high);
// 递归排序分区的两部分
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr) / sizeof(arr[0]);
printf("原始数组: ");
printArray(arr, n);
quickSort(arr, 0, n - 1);
printf("排序后数组: ");
printArray(arr, n);
return 0;
}
3.3 基础查找算法
3.3.1 线性查找
#include
// 线性查找实现
int linearSearch(int arr[], int n, int key) {
for (int i = 0; i < n; i++) {
if (arr[i] == key) {
return i; // 返回找到的索引
}
}
return -1; // 未找到
}
int main() {
int arr[] = {2, 3, 4, 10, 40};
int n = sizeof(arr) / sizeof(arr[0]);
int key = 10;
int result = linearSearch(arr, n, key);
if (result != -1) {
printf("元素 %d 在数组中的索引是 %d\n", key, result);
} else {
printf("元素 %d 不在数组中\n", key);
}
return 0;
}
3.3.2 二分查找
#include
// 二分查找实现(迭代版本)
int binarySearchIterative(int arr[], int n, int key) {
int left = 0;
int right = n – 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == key) {
return mid;
}
if (arr[mid] < key) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
// 二分查找实现(递归版本)
int binarySearchRecursive(int arr[], int left, int right, int key) {
if (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == key) {
return mid;
}
if (arr[mid] > key) {
return binarySearchRecursive(arr, left, mid – 1, key);
}
return binarySearchRecursive(arr, mid + 1, right, key);
}
return -1;
}
int main() {
// 注意:二分查找要求数组必须有序
int arr[] = {2, 3, 4, 10, 40, 50, 60, 70};
int n = sizeof(arr) / sizeof(arr[0]);
int key = 40;
// 迭代版本
int result1 = binarySearchIterative(arr, n, key);
if (result1 != -1) {
printf(“迭代查找: 元素 %d 在索引 %d\n”, key, result1);
} else {
printf(“迭代查找: 元素 %d 不在数组中\n”, key);
}
// 递归版本
int result2 = binarySearchRecursive(arr, 0, n – 1, key);
if (result2 != -1) {
printf(“递归查找: 元素 %d 在索引 %d\n”, key, result2);
} else {
printf(“递归查找: 兒素 %d 不在数组中\n”, key);
}
return 0;
}
第四部分:高级主题与实战技巧
4.1 文件操作
#include
#include
int main() {
// 写入文件
FILE *fp = fopen(“test.txt”, “w”);
if (fp == NULL) {
printf(“无法创建文件\n”);
return 1;
}
fprintf(fp, “Hello, File!\n”);
fprintf(fp, “This is a test file.\n”);
fprintf(fp, “Line number: %d\n”, 3);
fclose(fp);
// 读取文件
fp = fopen(“test.txt”, “r”);
if (fp == NULL) {
printf(“无法打开文件\n”);
return 1;
}
char buffer[100];
printf(“文件内容:\n”);
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf(“%s”, buffer);
}
fclose(fp);
// 二进制文件操作
// 写入结构体数组
struct Student {
int id;
char name[20];
float score;
};
struct Student students[3] = {
{101, “Alice”, 85.5},
{102, “Bob”, 92.0},
{103, “Charlie”, 78.5}
};
FILE *binfp = fopen(“students.dat”, “wb”);
if (binfp == NULL) {
printf(“无法创建二进制文件\n”);
return 1;
}
fwrite(students, sizeof(struct Student), 3, binfp);
fclose(binfp);
// 读取二进制文件
binfp = fopen(“students.dat”, “rb”);
if (binfp == NULL) {
printf(“无法打开二进制文件\n”);
return 1;
}
struct Student readStudents[3];
fread(readStudents, sizeof(struct Student), 3, binfp);
fclose(binfp);
printf(“\n从二进制文件读取的学生信息:\n”);
for (int i = 0; i < 3; i++) {
printf("ID: %d, 姓名: %s, 分数: %.1f\n",
readStudents[i].id, readStudents[i].name, readStudents[i].score);
}
return 0;
}
4.2 动态内存管理
#include
#include
int main() {
// 动态分配单个变量
int *p = (int*)malloc(sizeof(int));
if (p == NULL) {
printf(“内存分配失败\n”);
return 1;
}
*p = 42;
printf(“动态分配的整数: %d\n”, *p);
free(p);
// 动态分配数组
int size = 5;
int *arr = (int*)malloc(size * sizeof(int));
if (arr == NULL) {
printf(“内存分配失败\n”);
return 1;
}
// 初始化数组
for (int i = 0; i < size; i++) {
arr[i] = i * 10;
}
// 打印数组
printf("动态数组: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 重新分配内存(扩大)
int newSize = 8;
int *newArr = (int*)realloc(arr, newSize * sizeof(int));
if (newArr == NULL) {
printf("重新分配失败\n");
free(arr);
return 1;
}
arr = newArr;
// 初始化新增的元素
for (int i = size; i < newSize; i++) {
arr[i] = i * 10;
}
size = newSize;
// 打印扩展后的数组
printf("扩展后的数组: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 释放内存
free(arr);
// 动态分配二维数组
int rows = 3, cols = 4;
int **matrix = (int**)malloc(rows * sizeof(int*));
if (matrix == NULL) {
printf("二维数组内存分配失败\n");
return 1;
}
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
printf("行内存分配失败\n");
// 释放已分配的行
for (int j = 0; j < i; j++) {
free(matrix[j]);
}
free(matrix);
return 1;
}
}
// 初始化二维数组
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
// 打印二维数组
printf("动态二维数组:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%2d ", matrix[i][j]);
}
printf("\n");
}
// 释放二维数组
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
4.3 预处理器与宏定义
#include
// 宏定义
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))
#define PRINT_INT(x) printf(#x ” = %d\n”, x)
// 条件编译
#define DEBUG 1
// 带参数的宏
#define FOREACH(arr, n, action) \
for (int i = 0; i < (n); i++) { \
action; \
}
// 文件包含可以嵌套,这里演示条件编译
#if DEBUG
#define LOG(msg) printf("DEBUG: %s\n", msg)
#else
#define LOG(msg)
#endif
// 错误处理宏
#define CHECK_PTR(ptr) \
if ((ptr) == NULL) { \
printf("内存分配失败: %s:%d\n", __FILE__, __LINE__); \
exit(1); \
}
int main() {
// 使用宏常量
float radius = 5.0;
float area = PI * radius * radius;
printf("圆面积: %.2f\n", area);
// 使用带参数的宏
int x = 10, y = 20;
printf("MAX(%d, %d) = %d\n", x, y, MAX(x, y));
printf("SQUARE(%d) = %d\n", x, SQUARE(x));
// 使用字符串化宏
int a = 100, b = 200;
PRINT_INT(a);
PRINT_INT(b);
// 使用条件编译宏
LOG("程序开始执行");
// 使用自定义循环宏
int arr[] = {1, 2, 3, 4, 5};
int n = 5;
printf("数组元素: ");
FOREACH(arr, n, printf("%d ", arr[i]))
printf("\n");
// 使用检查指针宏
int *ptr = (int*)malloc(sizeof(int));
CHECK_PTR(ptr);
*ptr = 42;
printf("分配的值: %d\n", *ptr);
free(ptr);
// 预定义宏
printf("文件名: %s\n", __FILE__);
printf("行号: %d\n", __LINE__);
printf("编译日期: %s\n", __DATE__);
printf("编译时间: %s\n", __TIME__);
return 0;
}
4.4 实战项目:学生管理系统
#include
#include
#include
#define MAX_STUDENTS 100
#define NAME_LENGTH 50
// 学生结构体
typedef struct {
int id;
char name[NAME_LENGTH];
float score;
} Student;
// 系统状态
typedef struct {
Student students[MAX_STUDENTS];
int count;
} StudentSystem;
// 函数声明
void addStudent(StudentSystem *system);
void displayAllStudents(StudentSystem *system);
void searchStudent(StudentSystem *system, int id);
void deleteStudent(StudentSystem *system, int id);
void sortStudents(StudentSystem *system);
void saveToFile(StudentSystem *system);
void loadFromFile(StudentSystem *system);
void showMenu();
int main() {
StudentSystem system;
system.count = 0;
// 尝试从文件加载数据
loadFromFile(&system);
int choice;
do {
showMenu();
printf(“请输入选择: “);
scanf(“%d”, &choice);
switch (choice) {
case 1:
addStudent(&system);
break;
case 2:
displayAllStudents(&system);
break;
case 3: {
int id;
printf(“请输入学生ID: “);
scanf(“%d”, &id);
searchStudent(&system, id);
break;
}
case 4: {
int id;
printf(“请输入要删除的学生ID: “);
scanf(“%d”, &id);
deleteStudent(&system, id);
break;
}
case 5:
sortStudents(&system);
break;
case 6:
saveToFile(&system);
printf(“数据已保存\n”);
break;
case 0:
printf(“感谢使用学生管理系统!\n”);
break;
default:
printf(“无效选择,请重新输入\n”);
}
} while (choice != 0);
return 0;
}
void showMenu() {
printf(“\n========== 学生管理系统 ==========\n”);
printf(“1. 添加学生\n”);
printf(“2. 显示所有学生\n”);
printf(“3. 查找学生\n”);
printf(“4. 删除学生\n”);
printf(“5. 按成绩排序\n”);
printf(“6. 保存数据\n”);
printf(“0. 退出\n”);
printf(“==================================\n”);
}
void addStudent(StudentSystem *system) {
if (system->count >= MAX_STUDENTS) {
printf(“系统已满,无法添加\n”);
return;
}
Student *stu = &system->students[system->count];
printf(“请输入学生ID: “);
scanf(“%d”, &stu->id);
// 检查ID是否已存在
for (int i = 0; i < system->count; i++) {
if (system->students[i].id == stu->id) {
printf(“该ID已存在\n”);
return;
}
}
printf(“请输入学生姓名: “);
scanf(“%s”, stu->name);
printf(“请输入学生成绩: “);
scanf(“%f”, &stu->score);
system->count++;
printf(“学生添加成功\n”);
}
void displayAllStudents(StudentSystem *system) {
if (system->count == 0) {
printf(“没有学生数据\n”);
return;
}
printf(“\n%-10s %-20s %-10s\n”, “ID”, “姓名”, “成绩”);
printf(“====================================\n”);
for (int i = 0; i < system->count; i++) {
printf(“%-10d %-20s %-10.1f\n”,
system->students[i].id,
system->students[i].name,
system->students[i].score);
}
}
void searchStudent(StudentSystem *system, int id) {
for (int i = 0; i < system->count; i++) {
if (system->students[i].id == id) {
printf(“\n找到学生:\n”);
printf(“ID: %d\n”, system->students[i].id);
printf(“姓名: %s\n”, system->students[i].name);
printf(“成绩: %.1f\n”, system->students[i].score);
return;
}
}
printf(“未找到ID为 %d 的学生\n”, id);
}
void deleteStudent(StudentSystem *system, int id) {
int index = -1;
for (int i = 0; i < system->count; i++) {
if (system->students[i].id == id) {
index = i;
break;
}
}
if (index == -1) {
printf(“未找到ID为 %d 的学生\n”, id);
return;
}
// 移动元素覆盖要删除的元素
for (int i = index; i < system->count – 1; i++) {
system->students[i] = system->students[i + 1];
}
system->count–;
printf(“学生删除成功\n”);
}
// 冒泡排序按成绩降序
void sortStudents(StudentSystem *system) {
if (system->count < 2) {
printf("学生数量不足,无需排序\n");
return;
}
for (int i = 0; i < system->count – 1; i++) {
for (int j = 0; j < system->count – i – 1; j++) {
if (system->students[j].score < system->students[j + 1].score) {
// 交换学生
Student temp = system->students[j];
system->students[j] = system->students[j + 1];
system->students[j + 1] = temp;
}
}
}
printf(“按成绩排序完成\n”);
displayAllStudents(system);
}
void saveToFile(StudentSystem *system) {
FILE *fp = fopen(“students.dat”, “wb”);
if (fp == NULL) {
printf(“无法打开文件进行写入\n”);
return;
}
// 先写入学生数量
fwrite(&system->count, sizeof(int), 1, fp);
// 再写入学生数据
fwrite(system->students, sizeof(Student), system->count, fp);
fclose(fp);
}
void loadFromFile(StudentSystem *system) {
FILE *fp = fopen(“students.dat”, “rb”);
if (fp == NULL) {
// 文件不存在,可能是第一次运行
return;
}
// 读取学生数量
if (fread(&system->count, sizeof(int), 1, fp) != 1) {
fclose(fp);
return;
}
// 读取学生数据
if (fread(system->students, sizeof(Student), system->count, fp) != system->count) {
system->count = 0; // 读取失败
}
fclose(fp);
if (system->count > 0) {
printf(“已从文件加载 %d 条学生记录\n”, system->count);
}
}
第五部分:学习路径与最佳实践
5.1 C语言学习路线图
阶段1:基础语法(1-2周)
掌握基本数据类型、运算符、控制流
理解函数定义和调用
熟悉输入输出函数
练习:编写计算器、猜数字游戏
阶段2:核心概念(2-3周)
深入理解数组、字符串
掌握指针概念和应用
学习结构体和共用体
练习:学生成绩管理系统、通讯录
阶段3:高级特性(2-3周)
动态内存管理
文件操作
预处理器和宏
练习:文件加密工具、数据持久化应用
阶段4:数据结构与算法(3-4周)
实现链表、栈、队列
掌握排序和查找算法
理解算法复杂度分析
练习:实现自己的数据结构库
阶段5:项目实战(持续)
综合项目开发
代码优化和调试
学习使用调试工具
参与开源项目
5.2 编码规范与最佳实践
命名规范
变量名使用小写字母加下划线:
student_count
常量使用大写字母:
MAX_SIZE
函数名使用小写字母加下划线:
calculate_average
结构体名使用大驼峰:
StudentInfo
代码组织
每个函数不超过50行
使用头文件分离接口和实现
合理使用空行和缩进
添加必要的注释
错误处理
检查所有内存分配
验证文件操作结果
处理边界条件
使用assert进行调试
性能优化
避免不必要的类型转换
使用位运算替代乘除法
减少函数调用开销
合理使用const和restrict
5.3 调试技巧
#include
#include
// 调试宏
#define DEBUG 1
#if DEBUG
#define LOG(fmt, …) printf(“[DEBUG] %s:%d ” fmt “\n”, __FILE__, __LINE__, ##__VA_ARGS__)
#define CHECK_RANGE(val, min, max) \
assert((val) >= (min) && (val) <= (max))
#else
#define LOG(fmt, ...)
#define CHECK_RANGE(val, min, max)
#endif
// 带调试信息的函数
int divide(int a, int b) {
LOG("divide(%d, %d) called", a, b);
// 检查除数不为零
if (b == 0) {
LOG("错误:除数为零");
return -1;
}
// 检查范围
CHECK_RANGE(a, -1000, 1000);
CHECK_RANGE(b, -1000, 1000);
return a / b;
}
int main() {
// 测试正常情况
int result = divide(10, 2);
printf("10 / 2 = %d\n", result);
// 测试错误情况
result = divide(10, 0);
if (result == -1) {
printf("除法失败\n");
}
// 测试边界
result = divide(1000, 1);
printf("1000 / 1 = %d\n", result);
return 0;
}
5.4 进阶学习资源
经典书籍
《C程序设计语言》(K&R)
《C陷阱与缺陷》
《C专家编程》
《深入理解计算机系统》
在线资源
C标准文档(C11/C17)
GCC/Clang编译器文档
Stack Overflow
GitHub开源项目
实践项目
实现一个简单的Shell
编写一个文本编辑器
开发一个简单的HTTP服务器
实现一个小型数据库
结语
C语言作为一门经典的编程语言,其学习过程不仅是掌握语法,更是理解计算机系统工作原理的过程。通过系统学习本文介绍的内容,从基础语法到高级特性,再到数据结构与算法,你将建立起坚实的编程基础。
记住,编程是一门实践性很强的技能。理论学习固然重要,但更重要的是动手实践。建议你在学习每个概念时都编写相应的代码进行验证,并尝试解决实际问题。随着经验的积累,你将逐渐掌握C语言的精髓,并能够开发出高效、可靠的软件系统。
保持好奇心,持续学习,不断实践,你一定能够精通C语言,在编程的道路上走得更远!