C 语言高级特性与系统编程
高级数据结构、内存管理、文件系统、网络编程与并发模型。
1. 高级数据结构
1.1 链表
#include <stdio.h>
#include <stdlib.h>
// 链表节点结构
typedef struct Node {
int data;
struct Node *next;
}
// 创建新节点
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 插入节点到链表头部
Node* insertAtHead(Node* head, int data) {
Node* newNode = createNode(data);
newNode->next = head;
return newNode;
}
// 插入节点到链表尾部
Node* insertAtTail(Node* head, int data) {
Node* newNode = createNode(data);
if (head == NULL) {
return newNode;
}
Node* temp = head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
return head;
}
// 打印链表
void printList(Node* head) {
Node* temp = head;
while (temp != NULL) {
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// 释放链表内存
void freeList(Node* head) {
Node* temp;
while (head != NULL) {
temp = head;
head = head->next;
free(temp);
}
}
int main() {
Node* head = NULL;
head = insertAtHead(head, 3);
head = insertAtHead(head, 2);
head = insertAtHead(head, 1);
head = insertAtTail(head, 4);
head = insertAtTail(head, 5);
printList(head);
freeList(head);
return 0;
}
1.2 二叉树
#include <stdio.h>
#include <stdlib.h>
// 二叉树节点结构
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
}
// 创建新节点
TreeNode* createTreeNode(int data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
if (newNode == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// 插入节点到二叉搜索树
TreeNode* insertBST(TreeNode* root, int data) {
if (root == NULL) {
return createTreeNode(data);
}
if (data < root->data) {
root->left = insertBST(root->left, data);
} else if (data > root->data) {
root->right = insertBST(root->right, data);
}
return root;
}
// 中序遍历
void inorderTraversal(TreeNode* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf("%d ", root->data);
inorderTraversal(root->right);
}
}
// 前序遍历
void preorderTraversal(TreeNode* root) {
if (root != NULL) {
printf("%d ", root->data);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
}
// 后序遍历
void postorderTraversal(TreeNode* root) {
if (root != NULL) {
postorderTraversal(root->left);
postorderTraversal(root->right);
printf("%d ", root->data);
}
}
// 释放二叉树内存
void freeTree(TreeNode* root) {
if (root != NULL) {
freeTree(root->left);
freeTree(root->right);
free(root);
}
}
int main() {
TreeNode* root = NULL;
root = insertBST(root, 50);
root = insertBST(root, 30);
root = insertBST(root, 70);
root = insertBST(root, 20);
root = insertBST(root, 40);
root = insertBST(root, 60);
root = insertBST(root, 80);
printf("Inorder traversal: ");
inorderTraversal(root);
printf("\n");
printf("Preorder traversal: ");
preorderTraversal(root);
printf("\n");
printf("Postorder traversal: ");
postorderTraversal(root);
printf("\n");
freeTree(root);
return 0;
}
2. 内存管理
2.1 动态内存分配
#include <stdio.h>
#include <stdlib.h>
int main() {
// 分配单个整数的内存
int* ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
*ptr = 10;
printf("Value: %d\n", *ptr);
free(ptr);
// 分配数组的内存
int n = 5;
int* arr = (int*)malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 初始化数组
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// 打印数组
for (int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// 重新分配内存
n = 10;
arr = (int*)realloc(arr, n * sizeof(int));
if (arr == NULL) {
printf("Memory reallocation failed\n");
return 1;
}
// 填充新元素
for (int i = 5; i < n; i++) {
arr[i] = i + 1;
}
// 打印数组
printf("After reallocation:\n");
for (int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
2.2 内存泄漏检测
#include <stdio.h>
#include <stdlib.h>
// 模拟内存泄漏
void memoryLeak() {
int* ptr = (int*)malloc(sizeof(int));
*ptr = 42;
// 没有释放内存,导致内存泄漏
printf("Value: %d\n", *ptr);
// free(ptr); // 注释掉这行,造成内存泄漏
}
int main() {
// 多次调用,造成多次内存泄漏
for (int i = 0; i < 1000; i++) {
memoryLeak();
}
printf("Memory leak demonstration complete\n");
return 0;
}
3. 系统编程
3.1 文件操作
#include <stdio.h>
int main() {
FILE* fp;
char buffer[100];
// 打开文件进行写入
fp = fopen("example.txt", "w");
if (fp == NULL) {
printf("Error opening file\n");
return 1;
}
// 写入内容
fprintf(fp, "Hello, World!\n");
fprintf(fp, "This is a test file.\n");
// 关闭文件
fclose(fp);
// 打开文件进行读取
fp = fopen("example.txt", "r");
if (fp == NULL) {
printf("Error opening file\n");
return 1;
}
// 读取并打印内容
printf("File content:\n");
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}
// 关闭文件
fclose(fp);
return 0;
}
3.2 进程管理
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid;
// 创建子进程
pid = fork();
if (pid < 0) {
// fork 失败
fprintf(stderr, "Fork failed\n");
return 1;
} else if (pid == 0) {
// 子进程
printf("Child process, PID: %d\n", getpid());
printf("Child's parent PID: %d\n", getppid());
// 执行另一个程序
execl("/bin/ls", "ls", "-l", NULL);
// 如果 execl 失败,会执行到这里
fprintf(stderr, "execl failed\n");
return 1;
} else {
// 父进程
printf("Parent process, PID: %d\n", getpid());
printf("Created child process with PID: %d\n", pid);
// 等待子进程结束
wait(NULL);
printf("Child process completed\n");
}
return 0;
}
3.3 线程管理
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// 共享变量
int counter = 0;
// 互斥锁
pthread_mutex_t mutex;
// 线程函数
void* increment(void* arg) {
for (int i = 0; i < 100000; i++) {
// 加锁
pthread_mutex_lock(&mutex);
counter++;
// 解锁
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
// 创建线程
pthread_create(&thread1, NULL, increment, NULL);
pthread_create(&thread2, NULL, increment, NULL);
// 等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
printf("Final counter value: %d\n", counter);
return 0;
}
4. 网络编程
4.1 TCP 服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
char *hello = "Hello from server";
// 创建套接字文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定套接字到端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 开始监听
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d\n", PORT);
// 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取客户端消息
read(new_socket, buffer, BUFFER_SIZE);
printf("Client: %s\n", buffer);
// 发送响应
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 关闭连接
close(new_socket);
close(server_fd);
return 0;
}
4.2 TCP 客户端
#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
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[BUFFER_SIZE] = {0};
// 创建套接字文件描述符
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 转换 IPv4 地址
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
// 发送消息
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 读取响应
read(sock, buffer, BUFFER_SIZE);
printf("Server: %s\n", buffer);
// 关闭连接
close(sock);
return 0;
}
5. 高级特性
5.1 宏和预处理
#include <stdio.h>
// 简单宏
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))
// 带参数的宏
#define SQUARE(x) ((x) * (x))
// 多行宏
#define PRINT_ARRAY(arr, n) \
do { \
for (int i = 0; i < n; i++) { \
printf("%d ", arr[i]); \
} \
printf("\n"); \
} while(0)
// 条件编译
#define DEBUG 1
int main() {
// 使用简单宏
printf("PI = %f\n", PI);
printf("MAX(5, 10) = %d\n", MAX(5, 10));
// 使用带参数的宏
int x = 5;
printf("SQUARE(%d) = %d\n", x, SQUARE(x));
// 使用多行宏
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
PRINT_ARRAY(arr, n);
// 使用条件编译
#ifdef DEBUG
printf("Debug mode is enabled\n");
#else
printf("Debug mode is disabled\n");
#endif
return 0;
}
5.2 函数指针
#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 divide(int a, int b) {
if (b != 0) {
return a / b;
}
return 0;
}
int main() {
// 函数指针声明
int (*operation)(int, int);
int a = 10, b = 5;
// 使用函数指针调用 add 函数
operation = add;
printf("%d + %d = %d\n", a, b, operation(a, b));
// 使用函数指针调用 subtract 函数
operation = subtract;
printf("%d - %d = %d\n", a, b, operation(a, b));
// 使用函数指针调用 multiply 函数
operation = multiply;
printf("%d * %d = %d\n", a, b, operation(a, b));
// 使用函数指针调用 divide 函数
operation = divide;
printf("%d / %d = %d\n", a, b, operation(a, b));
return 0;
}
5.3 位操作
#include <stdio.h>
// 打印二进制表示
void printBinary(unsigned int n) {
for (int i = 31; i >= 0; i--) {
printf("%d", (n >> i) & 1);
if (i % 4 == 0) printf(" ");
}
printf("\n");
}
int main() {
unsigned int a = 0b10101010;
unsigned int b = 0b11001100;
printf("a: ");
printBinary(a);
printf("b: ");
printBinary(b);
// 按位与
printf("a & b: ");
printBinary(a & b);
// 按位或
printf("a | b: ");
printBinary(a | b);
// 按位异或
printf("a ^ b: ");
printBinary(a ^ b);
// 按位取反
printf("~a: ");
printBinary(~a);
// 左移
printf("a << 2: ");
printBinary(a << 2);
// 右移
printf("a >> 2: ");
printBinary(a >> 2);
return 0;
}
6. 最佳实践
6.1 代码风格
- 命名规范:
- 函数和变量使用小写字母,单词之间用下划线分隔
- 常量使用大写字母,单词之间用下划线分隔
- 结构体和类型定义使用大写字母开头,单词之间用下划线分隔
- 缩进:
- 使用 4 个空格进行缩进
- 保持代码块的缩进一致
- 注释:
- 为函数和复杂代码块添加注释
- 注释应该清晰明了,解释代码的功能和实现思路
- 错误处理:
- 检查所有函数调用的返回值
- 对错误情况进行适当的处理
- 使用
errno和perror来处理系统调用错误
6.2 性能优化
- 内存管理:
- 避免频繁的内存分配和释放
- 使用合适的内存分配函数(
malloc、calloc、realloc) - 及时释放不再使用的内存
- 算法选择:
- 选择时间复杂度合适的算法
- 对于大数据集,考虑使用更高效的数据结构
- 编译器优化:
- 使用
-O2或-O3编译选项启用编译器优化 - 避免使用会阻止编译器优化的代码模式
- 系统调用:
- 减少系统调用的次数
- 使用缓冲 I/O 来减少文件操作的系统调用
7. 项目实战
7.1 简单的命令行计算器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 函数声明
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("Usage: %s <operation> <num1> <num2>\n", argv[0]);
printf("Operations: add, subtract, multiply, divide\n");
return 1;
}
char *operation = argv[1];
int num1 = atoi(argv[2]);
int num2 = atoi(argv[3]);
int result;
if (strcmp(operation, "add") == 0) {
result = add(num1, num2);
} else if (strcmp(operation, "subtract") == 0) {
result = subtract(num1, num2);
} else if (strcmp(operation, "multiply") == 0) {
result = multiply(num1, num2);
} else if (strcmp(operation, "divide") == 0) {
if (num2 == 0) {
printf("Error: Division by zero\n");
return 1;
}
result = divide(num1, num2);
} else {
printf("Error: Invalid operation\n");
return 1;
}
printf("Result: %d\n", result);
return 0;
}
// 函数定义
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 divide(int a, int b) {
return a / b;
}
7.2 简单的文件复制程序
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE *source, *destination;
char buffer[1024];
size_t bytesRead;
if (argc != 3) {
printf("Usage: %s <source file> <destination file>\n", argv[0]);
return 1;
}
// 打开源文件
source = fopen(argv[1], "rb");
if (source == NULL) {
printf("Error opening source file\n");
return 1;
}
// 打开目标文件
destination = fopen(argv[2], "wb");
if (destination == NULL) {
printf("Error opening destination file\n");
fclose(source);
return 1;
}
// 复制文件内容
while ((bytesRead = fread(buffer, 1, sizeof(buffer), source)) > 0) {
fwrite(buffer, 1, bytesRead, destination);
}
// 关闭文件
fclose(source);
fclose(destination);
printf("File copied successfully\n");
return 0;
}
8. 常见问题与解决方案
8.1 内存泄漏
问题:程序运行时内存使用持续增长 解决方案:
- 确保所有
malloc、calloc、realloc分配的内存都有对应的free调用 - 使用工具如 Valgrind 来检测内存泄漏
8.2 段错误
问题:程序崩溃,出现 “Segmentation fault” 解决方案:
- 检查是否访问了空指针
- 检查是否数组越界
- 检查是否栈溢出
- 使用 GDB 调试器来定位问题
8.3 文件操作失败
问题:文件打开、读取或写入失败 解决方案:
- 检查文件路径是否正确
- 检查文件权限
- 检查磁盘空间是否充足
- 使用
perror来查看具体的错误信息
8.4 网络连接问题
问题:网络连接失败或超时 解决方案:
- 检查网络连接是否正常
- 检查防火墙设置
- 检查服务器是否正在运行
- 检查端口是否正确
9. 延伸阅读
- C 语言参考手册
- Linux 系统编程
- TCP/IP 网络编程
- C 语言程序设计 通过本教程,你已经了解了 C 语言的高级特性和系统编程技巧。在实际项目中,你可以使用这些技术来开发高性能、可靠的系统级应用程序。