(编写中)C预处理器和C库
条件编译
- 在很多情况下,我们希望程序的其中一部分代码只 有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译。
- 为什么要使用条件编译
- 1)按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。有利于程序的移植和调试。
- 2)条件编译当然也可以用条件语句来实现。 但是用条件语句将会对整个源程序进行编译,生成 的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段 1 或程序段 2,生成的目 标程序较短。 ##if-#else 条件编译指令
- 第一种格式:
- 它的功能是,如常量表达式的值为真(非 0),则将 code1 编译到程序中,否则对 code2 编译到程序中。
- 注意:
- 是将代码编译进可执行程序, 而不是执行代码
- 条件编译后面的条件表达式中不能识别变量,它里面只能识别常量和宏定义
#if 常量表达式
..code1...
#else
..code2...
#endif
#define SCORE 67
#if SCORE > 90
printf("优秀\n");
#else
printf("不及格\n");
#endif
- 第二种格式:
#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif
#define SCORE 67
#if SCORE > 90
printf("优秀\n");
#elif SCORE > 60
printf("良好\n");
#else
printf("不及格\n");
#endif
模块化编程基础
为什么需要模块化编程?
随着程序规模的增长,将所有代码写在一个文件中会导致:
- 代码难以维护
- 功能耦合严重
- 团队协作困难
- 代码复用性差
模块化编程通过将功能拆分到不同的文件中,提高了代码的:
- 可维护性 - 每个模块职责单一
- 可复用性 - 模块可以在多个项目中使用
- 可测试性 - 独立模块更容易测试
头文件和源文件分离
基本概念
- 头文件(.h):包含函数声明、类型定义、宏定义等
- 源文件(.c):包含函数的具体实现
示例:数学工具模块
1. 创建头文件 math_utils.h
#ifndef MATH_UTILS_H // 防止重复包含
#define MATH_UTILS_H
// 函数声明
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
double divide(double a, double b);
// 常量定义
#define PI 3.14159265359
// 结构体定义
typedef struct {
double x;
double y;
} Point;
// 函数声明
double point_distance(const Point* p1, const Point* p2);
#endif // MATH_UTILS_H
2. 创建源文件 math_utils.c
#include <math.h>
#include "math_utils.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;
}
double divide(double a, double b) {
if (b != 0) {
return a / b;
}
return 0.0; // 简单的错误处理
}
double point_distance(const Point* p1, const Point* p2) {
double dx = p1->x - p2->x;
double dy = p1->y - p2->y;
return sqrt(dx * dx + dy * dy);
}
3. 使用模块 main.c
#include <stdio.h>
#include "math_utils.h"
int main() {
// 使用基本数学函数
int result = add(10, 5);
printf("10 + 5 = %d\n", result);
// 使用结构体和函数
Point p1 = {0, 0};
Point p2 = {3, 4};
double distance = point_distance(&p1, &p2);
printf("Distance: %.2f\n", distance);
// 使用常量
printf("PI = %.5f\n", PI);
return 0;
}
4. 编译和链接
# 编译所有源文件
gcc -c math_utils.c -o math_utils.o
gcc -c main.c -o main.o
# 链接生成可执行文件
gcc math_utils.o main.o -lm -o calculator
# 或者一步完成
gcc main.c math_utils.c -lm -o calculator
项目目录结构
推荐的项目结构
project/
├── src/ # 源文件目录
│ ├── main.c
│ ├── utils/
│ │ ├── math_utils.c
│ │ ├── string_utils.c
│ │ └── file_utils.c
│ └── modules/
│ ├── student.c
│ └── database.c
├── include/ # 头文件目录
│ ├── utils/
│ │ ├── math_utils.h
│ │ ├── string_utils.h
│ │ └── file_utils.h
│ └── modules/
│ ├── student.h
│ └── database.h
├── lib/ # 第三方库目录
├── build/ # 编译 输出目录
├── docs/ # 文档目录
├── tests/ # 测试目录
├── examples/ # 示例代码
├── Makefile # 构建脚本
└── README.md # 项目说明
使用Makefile管理编译
# Makefile示例
CC = gcc
CFLAGS = -Wall -Wextra -Iinclude
SRCDIR = src
OBJDIR = build
TARGET = myapp
# 查找所有.c文件
SOURCES = $(wildcard $(SRCDIR)/*.c $(SRCDIR)/*/*.c)
OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -o $@
$(OBJDIR)/%.o: $(SRCDIR)/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(OBJDIR) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/bin/
.PHONY: help
help:
@echo "Available targets:"
@echo " all - Build the application"
@echo " clean - Remove build files"
@echo " install - Install to system"
@echo " help - Show this help"
使用第三方库
1. 使用系统库
大多数Linux系统都预装了常用的C库:
// 使用数学库
#include <math.h>
// 使用线程库
#include <pthread.h>
// 编译时需要链接
// gcc main.c -lm -lpthread -o myapp
2. 手动安装第三方库
以安装cJSON库为例:
# 下载源码
git clone https://github.com/DaveGamble/cJSON.git
cd cJSON
# 编译和安装
mkdir build
cd build
cmake ..
make
sudo make install
3. 使用第三方库示例
#include <stdio.h>
#include <cjson/cJSON.h>
int main() {
// 创建JSON对象
cJSON *json = cJSON_CreateObject();
cJSON *name = cJSON_CreateString("张三");
cJSON *age = cJSON_CreateNumber(25);
cJSON *skills = cJSON_CreateArray();
// 添加技能数组
cJSON_AddItemToArray(skills, cJSON_CreateString("C"));
cJSON_AddItemToArray(skills, cJSON_CreateString("Python"));
// 组装JSON对象
cJSON_AddItemToObject(json, "name", name);
cJSON_AddItemToObject(json, "age", age);
cJSON_AddItemToObject(json, "skills", skills);
// 输出JSON字符串
char *json_string = cJSON_Print(json);
printf("JSON: %s\n", json_string);
// 解析JSON字符串
cJSON *parsed = cJSON_Parse(json_string);
if (parsed != NULL) {
cJSON *name_item = cJSON_GetObjectItem(parsed, "name");
if (cJSON_IsString(name_item)) {
printf("Name: %s\n", name_item->valuestring);
}
}
// 清理内存
free(json_string);
cJSON_Delete(json);
cJSON_Delete(parsed);
return 0;
}
编译命令:
gcc main.c -lcjson -o json_demo
创建静态库和动态库
静态库(.a文件)
# 编译目标文件
gcc -c math_utils.c -o math_utils.o
gcc -c string_utils.c -o string_utils.o
# 创建静态库
ar rcs libmyutils.a math_utils.o string_utils.o
# 使用静态库
gcc main.c -L. -lmyutils -o myapp
动态库(.so文件)
# 编译位置无关代码
gcc -c -fPIC math_utils.c -o math_utils.o
gcc -c -fPIC string_utils.c -o string_utils.o
# 创建动态库
gcc -shared -o libmyutils.so math_utils.o string_utils.o
# 使用动态库
gcc main.c -L. -lmyutils -o myapp
# 运行时需要设置库路径
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./myapp
包管理工具
使用vcpkg管理依赖
# 安装vcpkg
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
# 安装库
./vcpkg install cjson
./vcpkg install curl
./vcpkg install sqlite3
# 在项目中使用
export VCPKG_ROOT=/path/to/vcpkg
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
实践项目 :学生管理系统
让我们创建一个模块化的学生管理系统作为练习:
项目结构
student_manager/
├── src/
│ ├── main.c
│ ├── student.c
│ ├── database.c
│ └── ui.c
├── include/
│ ├── student.h
│ ├── database.h
│ └── ui.h
├── data/
│ └── students.dat
└── Makefile
学生模块 include/student.h
#ifndef STUDENT_H
#define STUDENT_H
#define MAX_NAME_LENGTH 50
typedef struct {
int id;
char name[MAX_NAME_LENGTH];
int age;
float score;
} Student;
// 学生操作函数
Student* student_create(int id, const char* name, int age, float score);
void student_destroy(Student* student);
void student_print(const Student* student);
int student_compare_by_id(const Student* a, const Student* b);
int student_compare_by_score(const Student* a, const Student* b);
#endif
数据库模块 include/database.h
#ifndef DATABASE_H
#define DATABASE_H
#include "student.h"
typedef struct StudentDB StudentDB;
// 数据库操作函数
StudentDB* db_create(void);
void db_destroy(StudentDB* db);
int db_add_student(StudentDB* db, const Student* student);
Student* db_find_student(StudentDB* db, int id);
int db_remove_student(StudentDB* db, int id);
void db_list_all(const StudentDB* db);
int db_save_to_file(const StudentDB* db, const char* filename);
int db_load_from_file(StudentDB* db, const char* filename);
#endif
主要练习
- 模块化设计
- 头文件和源文件分离
- 结构体和函数的设计
- 文件操作
- 内存管理
总结
模块化编程是从初学者走向专业开发者的重要一步。通过:
- 头文件和源文件分离 - 实现接口和实现的分离
- 合理的目录结构 - 提高项目的可维护性
- 使用第三方库 - 避免重复造轮子
- 创建自己的库 - 积累可重用的代码资产