C语言实战与系统编程
在掌握了C语言的基础语法和核心特性后,我们将进入实战阶段,学习如何使用C语言进行系统编程、数据结构实现以及嵌入式开发等高级应用。
数据结构基础(C语言实现)
线性表
单链表
单链表是链式存储结构的基本形式,每个节点包含数据域和指向下一个节点的指针。
c
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点结构
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建新节点
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 在链表头部插入节点
Node* insertAtHead(Node* head, int data) {
Node* newNode = createNode(data);
newNode->next = head;
return newNode;
}
// 遍历链表
void traverseList(Node* head) {
Node* current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
// 删除节点
Node* deleteNode(Node* head, int data) {
if (head == NULL) return NULL;
// 如果要删除的是头节点
if (head->data == data) {
Node* temp = head;
head = head->next;
free(temp);
return head;
}
// 查找要删除的节点
Node* current = head;
while (current->next != NULL && current->next->data != data) {
current = current->next;
}
// 删除节点
if (current->next != NULL) {
Node* temp = current->next;
current->next = current->next->next;
free(temp);
}
return head;
}
// 释放链表内存
void freeList(Node* head) {
Node* current = head;
while (current != NULL) {
Node* temp = current;
current = current->next;
free(temp);
}
}栈与队列
栈是后进先出(LIFO)的数据结构,队列是先进先出(FIFO)的数据结构。
c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_SIZE 100
// 栈的实现(顺序存储)
typedef struct {
int data[MAX_SIZE];
int top;
} Stack;
// 初始化栈
void initStack(Stack* stack) {
stack->top = -1;
}
// 判断栈是否为空
bool isStackEmpty(Stack* stack) {
return stack->top == -1;
}
// 判断栈是否已满
bool isStackFull(Stack* stack) {
return stack->top == MAX_SIZE - 1;
}
// 入栈
bool push(Stack* stack, int value) {
if (isStackFull(stack)) {
return false;
}
stack->data[++stack->top] = value;
return true;
}
// 出栈
bool pop(Stack* stack, int* value) {
if (isStackEmpty(stack)) {
return false;
}
*value = stack->data[stack->top--];
return true;
}
// 队列的实现(顺序存储)
typedef struct {
int data[MAX_SIZE];
int front;
int rear;
} Queue;
// 初始化队列
void initQueue(Queue* queue) {
queue->front = 0;
queue->rear = 0;
}
// 判断队列是否为空
bool isQueueEmpty(Queue* queue) {
return queue->front == queue->rear;
}
// 判断队列是否已满
bool isQueueFull(Queue* queue) {
return (queue->rear + 1) % MAX_SIZE == queue->front;
}
// 入队
bool enqueue(Queue* queue, int value) {
if (isQueueFull(queue)) {
return false;
}
queue->data[queue->rear] = value;
queue->rear = (queue->rear + 1) % MAX_SIZE;
return true;
}
// 出队
bool dequeue(Queue* queue, int* value) {
if (isQueueEmpty(queue)) {
return false;
}
*value = queue->data[queue->front];
queue->front = (queue->front + 1) % MAX_SIZE;
return true;
}树与图
二叉树
二叉树是每个节点最多有两个子树的树结构。
c
#include <stdio.h>
#include <stdlib.h>
// 二叉树节点定义
typedef struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
} TreeNode;
// 创建新节点
TreeNode* createTreeNode(int data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// 前序遍历(根-左-右)
void preorderTraversal(TreeNode* root) {
if (root != NULL) {
printf("%d ", root->data);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
}
// 中序遍历(左-根-右)
void inorderTraversal(TreeNode* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf("%d ", root->data);
inorderTraversal(root->right);
}
}
// 后序遍历(左-右-根)
void postorderTraversal(TreeNode* root) {
if (root != NULL) {
postorderTraversal(root->left);
postorderTraversal(root->right);
printf("%d ", root->data);
}
}图的表示
图可以用邻接矩阵或邻接表来表示。
c
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTICES 100
// 邻接矩阵表示图
typedef struct {
int vertices;
int adjMatrix[MAX_VERTICES][MAX_VERTICES];
} GraphMatrix;
// 初始化图
void initGraphMatrix(GraphMatrix* graph, int vertices) {
graph->vertices = vertices;
for (int i = 0; i < vertices; i++) {
for (int j = 0; j < vertices; j++) {
graph->adjMatrix[i][j] = 0;
}
}
}
// 添加边
void addEdgeMatrix(GraphMatrix* graph, int src, int dest) {
if (src >= 0 && src < graph->vertices && dest >= 0 && dest < graph->vertices) {
graph->adjMatrix[src][dest] = 1;
graph->adjMatrix[dest][src] = 1; // 无向图
}
}
// 打印邻接矩阵
void printGraphMatrix(GraphMatrix* graph) {
for (int i = 0; i < graph->vertices; i++) {
for (int j = 0; j < graph->vertices; j++) {
printf("%d ", graph->adjMatrix[i][j]);
}
printf("\n");
}
}
// 邻接表节点
typedef struct AdjListNode {
int vertex;
struct AdjListNode* next;
} AdjListNode;
// 邻接表表示图
typedef struct {
int vertices;
AdjListNode* adjList[MAX_VERTICES];
} GraphList;
// 创建邻接表节点
AdjListNode* createAdjListNode(int vertex) {
AdjListNode* newNode = (AdjListNode*)malloc(sizeof(AdjListNode));
newNode->vertex = vertex;
newNode->next = NULL;
return newNode;
}
// 初始化图
void initGraphList(GraphList* graph, int vertices) {
graph->vertices = vertices;
for (int i = 0; i < vertices; i++) {
graph->adjList[i] = NULL;
}
}
// 添加边
void addEdgeList(GraphList* graph, int src, int dest) {
// 添加边 src -> dest
AdjListNode* newNode = createAdjListNode(dest);
newNode->next = graph->adjList[src];
graph->adjList[src] = newNode;
// 添加边 dest -> src (无向图)
newNode = createAdjListNode(src);
newNode->next = graph->adjList[dest];
graph->adjList[dest] = newNode;
}系统编程入门(Linux环境)
进程基础
在Linux系统中,进程是程序执行的实例。
c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
// 进程创建示例
int processExample() {
pid_t pid = fork();
if (pid < 0) {
// 创建失败
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
printf("子进程: PID = %d, 父进程 PID = %d\n", getpid(), getppid());
// 子进程执行的任务
execlp("/bin/ls", "ls", "-l", NULL);
// 如果execlp执行成功,下面的代码不会执行
printf("execlp执行失败\n");
exit(1);
} else {
// 父进程
printf("父进程: PID = %d, 子进程 PID = %d\n", getpid(), pid);
// 等待子进程结束
int status;
wait(&status);
printf("子进程已结束,退出状态: %d\n", WEXITSTATUS(status));
}
return 0;
}信号处理
信号是进程间通信的一种方式,用于通知进程发生了某种事件。
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
// 信号处理函数
void signalHandler(int signum) {
printf("捕获到信号 %d\n", signum);
if (signum == SIGINT) {
printf("程序将退出\n");
exit(0);
}
}
// 信号处理示例
int signalExample() {
// 注册信号处理函数
signal(SIGINT, signalHandler);
printf("程序运行中,按 Ctrl+C 发送 SIGINT 信号\n");
// 无限循环,等待信号
while(1) {
printf("程序正在运行...\n");
sleep(1);
}
return 0;
}文件系统操作
Linux系统中的文件操作是系统编程的重要部分。
c
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
// 目录操作示例
void directoryExample() {
DIR *dir;
struct dirent *entry;
// 打开当前目录
dir = opendir(".");
if (dir == NULL) {
perror("无法打开目录");
return;
}
printf("当前目录中的文件:\n");
// 读取目录内容
while ((entry = readdir(dir)) != NULL) {
printf("%s\n", entry->d_name);
}
// 关闭目录
closedir(dir);
}
// 文件属性获取示例
void fileStatExample(const char* filename) {
struct stat fileStat;
if (stat(filename, &fileStat) < 0) {
perror("获取文件状态失败");
return;
}
printf("文件: %s\n", filename);
printf("大小: %ld 字节\n", fileStat.st_size);
printf("创建时间: %s", ctime(&fileStat.st_ctime));
printf("修改时间: %s", ctime(&fileStat.st_mtime));
// 判断文件类型
if (S_ISREG(fileStat.st_mode)) {
printf("类型: 普通文件\n");
} else if (S_ISDIR(fileStat.st_mode)) {
printf("类型: 目录\n");
} else if (S_ISLNK(fileStat.st_mode)) {
printf("类型: 符号链接\n");
}
}嵌入式C语言基础
寄存器操作
在嵌入式开发中,直接操作硬件寄存器是常见需求。
c
#include <stdint.h>
// 假设的GPIO寄存器地址
#define GPIO_BASE_ADDR 0x40020000
#define GPIO_MODER_OFFSET 0x00
#define GPIO_ODR_OFFSET 0x14
// 定义寄存器指针
#define GPIOA_MODER (*(volatile uint32_t*)(GPIO_BASE_ADDR + GPIO_MODER_OFFSET))
#define GPIOA_ODR (*(volatile uint32_t*)(GPIO_BASE_ADDR + GPIO_ODR_OFFSET))
// 配置GPIO引脚为输出模式
void gpioInit() {
// 配置PA5为输出模式 (MODER寄存器中,每两位控制一个引脚)
GPIOA_MODER &= ~(0x3 << (5 * 2)); // 清除PA5的模式位
GPIOA_MODER |= (0x1 << (5 * 2)); // 设置PA5为输出模式
}
// 设置GPIO引脚电平
void gpioWrite(int pin, int value) {
if (value) {
GPIOA_ODR |= (1 << pin); // 设置引脚为高电平
} else {
GPIOA_ODR &= ~(1 << pin); // 设置引脚为低电平
}
}位运算
位运算是嵌入式开发中的重要工具。
c
#include <stdio.h>
#include <stdint.h>
// 位操作宏定义
#define SET_BIT(reg, bit) ((reg) |= (1 << (bit)))
#define CLEAR_BIT(reg, bit) ((reg) &= ~(1 << (bit)))
#define TOGGLE_BIT(reg, bit) ((reg) ^= (1 << (bit)))
#define CHECK_BIT(reg, bit) (((reg) >> (bit)) & 1)
// 位操作示例
void bitOperationExample() {
uint8_t reg = 0x00;
printf("初始值: 0x%02X\n", reg);
// 设置第3位
SET_BIT(reg, 3);
printf("设置第3位后: 0x%02X\n", reg);
// 清除第3位
CLEAR_BIT(reg, 3);
printf("清除第3位后: 0x%02X\n", reg);
// 翻转第5位
TOGGLE_BIT(reg, 5);
printf("翻转第5位后: 0x%02X\n", reg);
// 检查第5位
if (CHECK_BIT(reg, 5)) {
printf("第5位为1\n");
} else {
printf("第5位为0\n");
}
}项目实战工具
调试工具:GDB使用
GDB是Linux下强大的调试工具,可以帮助我们调试C程序。
bash
# 编译时加入调试信息
gcc -g -o program program.c
# 启动GDB
gdb ./program
# GDB常用命令
# (gdb) break main # 在main函数设置断点
# (gdb) run # 运行程序
# (gdb) step # 单步执行(进入函数)
# (gdb) next # 单步执行(不进入函数)
# (gdb) print variable # 打印变量值
# (gdb) continue # 继续执行
# (gdb) quit # 退出GDB构建工具:Makefile基础
Makefile用于自动化编译和构建项目。
makefile
# Makefile示例
# 编译器
CC = gcc
# 编译选项
CFLAGS = -Wall -g
# 目标文件
TARGET = myprogram
# 源文件
SRCS = main.c utils.c
# 对象文件
OBJS = $(SRCS:.c=.o)
# 默认目标
all: $(TARGET)
# 链接目标文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
# 编译源文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理目标文件
clean:
rm -f $(OBJS) $(TARGET)
# 安装目标
install: $(TARGET)
cp $(TARGET) /usr/local/bin/
# 伪目标
.PHONY: all clean install版本控制:Git基础
Git是分布式版本控制系统,用于管理代码变更。
bash
# 初始化Git仓库
git init
# 添加文件到暂存区
git add .
# 提交更改
git commit -m "初始提交"
# 查看状态
git status
# 查看提交历史
git log
# 创建分支
git branch feature-branch
# 切换分支
git checkout feature-branch
# 合并分支
git checkout main
git merge feature-branch
# 推送到远程仓库
git push origin main综合项目
小型数据库实现
让我们实现一个简单的基于文件的数据库系统:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_RECORDS 1000
#define MAX_NAME_LEN 50
#define DATABASE_FILE "database.dat"
// 数据记录结构
typedef struct {
int id;
char name[MAX_NAME_LEN];
int age;
float score;
} Record;
// 数据库结构
typedef struct {
Record records[MAX_RECORDS];
int count;
} Database;
// 初始化数据库
void initDatabase(Database* db) {
db->count = 0;
}
// 添加记录
int addRecord(Database* db, int id, const char* name, int age, float score) {
if (db->count >= MAX_RECORDS) {
return -1; // 数据库已满
}
db->records[db->count].id = id;
strncpy(db->records[db->count].name, name, MAX_NAME_LEN - 1);
db->records[db->count].name[MAX_NAME_LEN - 1] = '\0';
db->records[db->count].age = age;
db->records[db->count].score = score;
db->count++;
return 0; // 成功
}
// 查找记录
Record* findRecord(Database* db, int id) {
for (int i = 0; i < db->count; i++) {
if (db->records[i].id == id) {
return &db->records[i];
}
}
return NULL; // 未找到
}
// 删除记录
int deleteRecord(Database* db, int id) {
for (int i = 0; i < db->count; i++) {
if (db->records[i].id == id) {
// 将后面的记录前移
for (int j = i; j < db->count - 1; j++) {
db->records[j] = db->records[j + 1];
}
db->count--;
return 0; // 成功删除
}
}
return -1; // 未找到
}
// 保存数据库到文件
int saveDatabase(Database* db) {
FILE* file = fopen(DATABASE_FILE, "wb");
if (file == NULL) {
return -1;
}
fwrite(&db->count, sizeof(int), 1, file);
fwrite(db->records, sizeof(Record), db->count, file);
fclose(file);
return 0;
}
// 从文件加载数据库
int loadDatabase(Database* db) {
FILE* file = fopen(DATABASE_FILE, "rb");
if (file == NULL) {
return -1;
}
fread(&db->count, sizeof(int), 1, file);
fread(db->records, sizeof(Record), db->count, file);
fclose(file);
return 0;
}
// 打印所有记录
void printAllRecords(Database* db) {
printf("ID\t姓名\t\t年龄\t成绩\n");
printf("----------------------------------------\n");
for (int i = 0; i < db->count; i++) {
printf("%d\t%-15s\t%d\t%.2f\n",
db->records[i].id,
db->records[i].name,
db->records[i].age,
db->records[i].score);
}
}简单HTTP服务器
实现一个处理GET请求的简单HTTP服务器:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
// HTTP响应模板
const char* HTTP_RESPONSE =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%s";
// 简单的HTML页面
const char* HTML_CONTENT =
"<!DOCTYPE html>"
"<html>"
"<head><title>C语言HTTP服务器</title></head>"
"<body>"
"<h1>欢迎来到C语言HTTP服务器</h1>"
"<p>这是一个用C语言实现的简单HTTP服务器</p>"
"<p>当前时间: %s</p>"
"</body>"
"</html>";
// 处理客户端请求
void handleClient(int clientSocket) {
char buffer[BUFFER_SIZE];
ssize_t bytesRead;
// 读取客户端请求
bytesRead = read(clientSocket, buffer, BUFFER_SIZE - 1);
if (bytesRead > 0) {
buffer[bytesRead] = '\0';
printf("收到请求:\n%s\n", buffer);
// 构造响应内容
char currentTime[100];
time_t now = time(NULL);
strftime(currentTime, sizeof(currentTime), "%Y-%m-%d %H:%M:%S", localtime(&now));
char htmlContent[500];
snprintf(htmlContent, sizeof(htmlContent), HTML_CONTENT, currentTime);
char response[BUFFER_SIZE];
snprintf(response, sizeof(response), HTTP_RESPONSE, (int)strlen(htmlContent), htmlContent);
// 发送响应
write(clientSocket, response, strlen(response));
}
// 关闭客户端连接
close(clientSocket);
}
// 启动HTTP服务器
int startHttpServer() {
int serverSocket, clientSocket;
struct sockaddr_in serverAddr, clientAddr;
socklen_t clientAddrLen = sizeof(clientAddr);
// 创建套接字
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
perror("创建套接字失败");
return 1;
}
// 设置服务器地址
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(PORT);
// 绑定套接字
if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
perror("绑定失败");
close(serverSocket);
return 1;
}
// 监听连接
if (listen(serverSocket, 5) < 0) {
perror("监听失败");
close(serverSocket);
return 1;
}
printf("HTTP服务器启动,监听端口 %d\n", PORT);
// 接受客户端连接
while (1) {
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket < 0) {
perror("接受连接失败");
continue;
}
printf("客户端连接: %s:%d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
// 处理客户端请求
handleClient(clientSocket);
}
// 关闭服务器套接字
close(serverSocket);
return 0;
}实践练习
- 实现一个完整的学生成绩管理系统,包含增删改查功能
- 开发一个简单的文本编辑器,支持文件读写和基本编辑功能
- 用函数指针实现一个通用的排序算法库
- 实现一个基于链表的任务调度器
- 开发一个简单的聊天程序(使用socket编程)
通过这些实战项目,你将能够更好地理解C语言在系统编程中的应用,并掌握实际开发中常用的工具和技术。记住,实践是掌握C语言的关键,多写代码、多调试、多思考,你将逐步成长为一名优秀的C语言程序员!