七、指针
7.1 指针基础
指针是C语言最核心、最强大的特性,也是初学者最困惑的部分。
#include <stdio.h>
int main() {
int num = 42;
// 指针变量:存储地址
int *ptr = # // &取地址符
// 打印地址和值
printf("num的值:%d\n", num);
printf("num的地址:%p\n", &num);
printf("ptr的值(地址):%p\n", ptr);
printf("ptr指向的值:%d\n", *ptr); // *解引用符
// 通过指针修改变量
*ptr = 100;
printf("修改后num的值:%d\n", num);
// 指针的大小
printf("指针大小:%zu 字节\n", sizeof(ptr));
// 空指针
int *nullPtr = NULL;
if (nullPtr == NULL) {
printf("空指针\n");
}
return 0;
}
7.2 指针与数组
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
// 数组名是指向首元素的指针
int *ptr = arr; // 等价于 &arr[0]
// 使用指针访问数组元素
printf("arr[0] = %d, *ptr = %d\n", arr[0], *ptr);
printf("arr[1] = %d, *(ptr+1) = %d\n", arr[1], *(ptr + 1));
// 指针运算
printf("指针加法:ptr+2 指向 %d\n", *(ptr + 2));
// 遍历数组
printf("数组元素:");
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
printf("\n");
// 使用指针遍历
printf("指针遍历:");
int *p = arr;
int *end = arr + 5;
while (p < end) {
printf("%d ", *p);
p++;
}
printf("\n");
// 二维数组与指针
int matrix[2][3] = {
{1, 2, 3}, {4, 5, 6}};
int (*row)[3] = matrix; // 指向一维数组的指针
printf("matrix[1][2] = %d\n", row[1][2]);
printf("*(*(matrix+1)+2) = %d\n", *(*(matrix + 1) + 2));
return 0;
}
7.3 指针与函数
#include <stdio.h>
// 指针作为函数参数(实现多返回值)
void get_min_max(int arr[], int size, int *min, int *max) {
*min = arr[0];
*max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < *min) *min = arr[i];
if (arr[i] > *max) *max = arr[i];
}
}
// 函数指针:指向函数的指针
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 calculate(int (*operation)(int, int), int x, int y) {
return operation(x, y);
}
// 返回指针的函数
int* find_max(int arr[], int size) {
int *max = &arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > *max) {
max = &arr[i];
}
}
return max;
}
int main() {
// 多返回值
int numbers[] = {3, 7, 1, 9, 4, 2, 8, 5, 6};
int min, max;
get_min_max(numbers, 9, &min, &max);
printf("最小值:%d,最大值:%d\n", min, max);
// 函数指针
printf("add: %d\n", calculate(add, 10, 5));
printf("subtract: %d\n", calculate(subtract, 10, 5));
printf("multiply: %d\n", calculate(multiply, 10, 5));
// 函数指针数组
int (*operations[])(int, int) = {add, subtract, multiply};
for (int i = 0; i < 3; i++) {
printf("结果%d: %d\n", i, operations[i](10, 5));
}
// 返回指针的函数
int *maxPtr = find_max(numbers, 9);
printf("最大值:%d,地址:%p\n", *maxPtr, maxPtr);
return 0;
}
八、结构体与联合体
8.1 结构体
#include <stdio.h>
#include <string.h>
// 结构体定义
struct Student {
int id;
char name[50];
int age;
float score;
};
// 结构体别名(typedef)
typedef struct {
int year;
int month;
int day;
} Date;
// 结构体嵌套
struct Teacher {
char name[50];
Date birthDate;
struct Student* students; // 指向学生的指针
};
int main() {
// 结构体变量声明与初始化
struct Student stu1 = {1001, "张三", 20, 85.5};
struct Student stu2;
// 访问成员
stu2.id = 1002;
strcpy(stu2.name, "李四");
stu2.age = 21;
stu2.score = 92.0;
printf("学生信息:\n");
printf("学号:%d\n", stu1.id);
printf("姓名:%s\n", stu1.name);
printf("年龄:%d\n", stu1.age);
printf("成绩:%.1f\n", stu1.score);
// 结构体数组
struct Student class[3] = {
{1001, "张三", 20, 85.5},
{1002, "李四", 21, 92.0},
{1003, "王五", 19, 78.5}
};
printf("\n班级学生:\n");
for (int i = 0; i < 3; i++) {
printf("%d\t%s\t%.1f\n", class[i].id, class[i].name, class[i].score);
}
// 结构体指针
struct Student *ptr = &stu1;
printf("\n通过指针访问:\n");
printf("姓名:%s\n", ptr->name); // 等价于 (*ptr).name
printf("成绩:%.1f\n", ptr->score);
// 计算平均分
float sum = 0;
for (int i = 0; i < 3; i++) {
sum += class[i].score;
}
printf("班级平均分:%.1f\n", sum / 3);
// 结构体大小
printf("Student大小:%zu 字节\n", sizeof(struct Student));
printf("Date大小:%zu 字节\n", sizeof(Date));
return 0;
}
8.2 联合体与枚举
#include <stdio.h>
// 联合体:所有成员共享同一块内存
union Data {
int i;
float f;
char str[20];
};
// 枚举
enum Color {
RED, // 0
GREEN, // 1
BLUE // 2
};
enum Weekday {
MON = 1,
TUE,
WED,
THU,
FRI,
SAT,
SUN
};
// 联合体与结构体结合
struct Variant {
enum { TYPE_INT, TYPE_FLOAT, TYPE_STRING } type;
union {
int intVal;
float floatVal;
char stringVal[50];
} value;
};
void printVariant(struct Variant v) {
switch (v.type) {
case TYPE_INT:
printf("整数:%d\n", v.value.intVal);
break;
case TYPE_FLOAT:
printf("浮点数:%.2f\n", v.value.floatVal);
break;
case TYPE_STRING:
printf("字符串:%s\n", v.value.stringVal);
break;
}
}
int main() {
// 联合体
union Data data;
data.i = 100;
printf("data.i = %d\n", data.i);
printf("data.f = %f\n", data.f); // 垃圾值
data.f = 3.14;
printf("data.f = %f\n", data.f);
printf("data.i = %d\n", data.i); // 被覆盖
printf("联合体大小:%zu 字节\n", sizeof(union Data));
// 枚举
enum Color color = RED;
printf("RED = %d\n", color);
enum Weekday today = WED;
if (today == WED) {
printf("今天是星期三\n");
}
// 联合体与结构体结合(类型安全)
struct Variant v1 = {TYPE_INT, .value.intVal = 42};
struct Variant v2 = {TYPE_FLOAT, .value.floatVal = 3.14159};
struct Variant v3 = {TYPE_STRING, .value.stringVal = "Hello"};
printVariant(v1);
printVariant(v2);
printVariant(v3);
return 0;
}
九、动态内存管理
9.1 malloc、calloc、realloc、free
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// malloc:分配内存,不初始化
int *arr = (int*)malloc(5 * sizeof(int));
if (arr == NULL) {
printf("内存分配失败\n");
return 1;
}
// 使用内存
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
printf("malloc分配:");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// calloc:分配内存并初始化为0
int *arr2 = (int*)calloc(5, sizeof(int));
if (arr2 == NULL) {
printf("内存分配失败\n");
free(arr);
return 1;
}
printf("calloc分配(自动清零):");
for (int i = 0; i < 5; i++) {
printf("%d ", arr2[i]);
}
printf("\n");
// realloc:重新分配内存大小
int *arr3 = (int*)realloc(arr, 10 * sizeof(int));
if (arr3 == NULL) {
printf("内存重新分配失败\n");
free(arr);
free(arr2);
return 1;
}
arr = arr3;
for (int i = 5; i < 10; i++) {
arr[i] = i * 10;
}
printf("realloc扩展后:");
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 动态字符串
char *str = (char*)malloc(100 * sizeof(char));
if (str) {
strcpy(str, "动态分配的字符串");
printf("动态字符串:%s\n", str);
free(str);
}
// 动态结构体
struct Student {
int id;
char name[50];
};
struct Student *student = (struct Student*)malloc(sizeof(struct Student));
if (student) {
student->id = 1001;
strcpy(student->name, "张三");
printf("动态结构体:%d, %s\n", student->id, student->name);
free(student);
}
// 释放内存
free(arr);
free(arr2);
// 避免内存泄漏:释放后设置为NULL
arr = NULL;
return 0;
}
十、文件操作
10.1 文件读写
#include <stdio.h>
#include <stdlib.h>
int main() {
// 写入文件
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) {
printf("文件打开失败\n");
return 1;
}
fprintf(fp, "Hello, C语言!\n");
fprintf(fp, "这是第二行\n");
fclose(fp);
printf("文件写入成功\n");
// 读取文件
fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败\n");
return 1;
}
printf("\n文件内容:\n");
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}
fclose(fp);
// 追加写入
fp = fopen("test.txt", "a");
if (fp == NULL) {
printf("文件打开失败\n");
return 1;
}
fprintf(fp, "这是追加的行\n");
fclose(fp);
printf("\n追加成功\n");
// 二进制文件写入
int numbers[] = {10, 20, 30, 40, 50};
fp = fopen("data.bin", "wb");
fwrite(numbers, sizeof(int), 5, fp);
fclose(fp);
// 二进制文件读取
int readNumbers[5];
fp = fopen("data.bin", "rb");
fread(readNumbers, sizeof(int), 5, fp);
fclose(fp);
printf("\n二进制读取:");
for (int i = 0; i < 5; i++) {
printf("%d ", readNumbers[i]);
}
printf("\n");
return 0;
}
10.2 文件位置操作
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败\n");
return 1;
}
// 获取当前位置
long pos = ftell(fp);
printf("初始位置:%ld\n", pos);
// 读取一行
char buffer[100];
fgets(buffer, sizeof(buffer), fp);
printf("读取:%s", buffer);
pos = ftell(fp);
printf("当前位置:%ld\n", pos);
// 设置位置到文件开头
rewind(fp);
printf("重置后位置:%ld\n", ftell(fp));
// 定位到文件末尾
fseek(fp, 0, SEEK_END);
printf("文件末尾位置:%ld\n", ftell(fp));
// 定位到指定位置
fseek(fp, 10, SEEK_SET);
printf("定位到第10个字节后位置:%ld\n", ftell(fp));
fclose(fp);
return 0;
}
十一、预处理指令
#include <stdio.h>
// 宏定义
#define PI 3.14159
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
// 条件编译
#define DEBUG 1
// 防止头文件重复包含
#ifndef MY_HEADER_H
#define MY_HEADER_H
// 头文件内容
#endif
int main() {
// 使用宏
printf("PI = %.5f\n", PI);
printf("SQUARE(5) = %d\n", SQUARE(5));
printf("MAX(10, 20) = %d\n", MAX(10, 20));
// 条件编译
#if DEBUG
printf("调试模式:变量值...\n");
#else
printf("发布模式\n");
#endif
// #运算符:将参数转换为字符串
#define STR(x) #x
printf("STR(hello) = %s\n", STR(hello));
// ##运算符:连接两个标记
#define CONCAT(a, b) a##b
int xy = 100;
printf("CONCAT(x, y) = %d\n", CONCAT(x, y));
// 预定义宏
printf("文件名:%s\n", __FILE__);
printf("行号:%d\n", __LINE__);
printf("编译日期:%s\n", __DATE__);
printf("编译时间:%s\n", __TIME__);
printf("函数名:%s\n", __func__);
return 0;
}
十二、实战案例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STUDENTS 100
#define NAME_LEN 50
// 学生结构体
typedef struct {
int id;
char name[NAME_LEN];
int age;
float score;
} Student;
// 全局变量
Student students[MAX_STUDENTS];
int studentCount = 0;
// 函数声明
void addStudent();
void displayAll();
void searchStudent();
void deleteStudent();
void updateStudent();
void saveToFile();
void loadFromFile();
void showMenu();
int main() {
loadFromFile(); // 加载已有数据
int choice;
do {
showMenu();
printf("请选择操作:");
scanf("%d", &choice);
getchar(); // 清除缓冲区
switch (choice) {
case 1:
addStudent();
break;
case 2:
displayAll();
break;
case 3:
searchStudent();
break;
case 4:
updateStudent();
break;
case 5:
deleteStudent();
break;
case 6:
saveToFile();
printf("数据已保存,退出系统\n");
break;
default:
printf("无效选择,请重新输入\n");
}
} while (choice != 6);
return 0;
}
void showMenu() {
printf("\n==================\n");
printf("学生信息管理系统\n");
printf("==================\n");
printf("1. 添加学生\n");
printf("2. 显示所有学生\n");
printf("3. 搜索学生\n");
printf("4. 修改学生信息\n");
printf("5. 删除学生\n");
printf("6. 退出系统\n");
printf("==================\n");
}
void addStudent() {
if (studentCount >= MAX_STUDENTS) {
printf("学生数量已达上限\n");
return;
}
Student newStudent;
printf("请输入学号:");
scanf("%d", &newStudent.id);
// 检查学号是否重复
for (int i = 0; i < studentCount; i++) {
if (students[i].id == newStudent.id) {
printf("学号已存在\n");
return;
}
}
printf("请输入姓名:");
scanf("%s", newStudent.name);
printf("请输入年龄:");
scanf("%d", &newStudent.age);
printf("请输入成绩:");
scanf("%f", &newStudent.score);
students[studentCount++] = newStudent;
printf("学生添加成功\n");
}
void displayAll() {
if (studentCount == 0) {
printf("暂无学生数据\n");
return;
}
printf("\n学生列表:\n");
printf("学号\t姓名\t年龄\t成绩\n");
printf("--------------------------------\n");
for (int i = 0; i < studentCount; i++) {
printf("%d\t%s\t%d\t%.1f\n",
students[i].id,
students[i].name,
students[i].age,
students[i].score);
}
// 统计信息
float sum = 0;
int maxScore = students[0].score;
int minScore = students[0].score;
char maxName[NAME_LEN], minName[NAME_LEN];
for (int i = 0; i < studentCount; i++) {
sum += students[i].score;
if (students[i].score > maxScore) {
maxScore = students[i].score;
strcpy(maxName, students[i].name);
}
if (students[i].score < minScore) {
minScore = students[i].score;
strcpy(minName, students[i].name);
}
}
printf("\n统计信息:\n");
printf("总人数:%d\n", studentCount);
printf("平均分:%.2f\n", sum / studentCount);
printf("最高分:%s (%.1f)\n", maxName, maxScore);
printf("最低分:%s (%.1f)\n", minName, minScore);
}
void searchStudent() {
int id;
printf("请输入要搜索的学号:");
scanf("%d", &id);
for (int i = 0; i < studentCount; i++) {
if (students[i].id == id) {
printf("\n找到学生:\n");
printf("学号:%d\n", students[i].id);
printf("姓名:%s\n", students[i].name);
printf("年龄:%d\n", students[i].age);
printf("成绩:%.1f\n", students[i].score);
return;
}
}
printf("未找到学号为%d的学生\n", id);
}
void updateStudent() {
int id;
printf("请输入要修改的学号:");
scanf("%d", &id);
for (int i = 0; i < studentCount; i++) {
if (students[i].id == id) {
printf("当前信息:\n");
printf("姓名:%s\n", students[i].name);
printf("年龄:%d\n", students[i].age);
printf("成绩:%.1f\n", students[i].score);
printf("\n请输入新的姓名:");
scanf("%s", students[i].name);
printf("请输入新的年龄:");
scanf("%d", &students[i].age);
printf("请输入新的成绩:");
scanf("%f", &students[i].score);
printf("信息修改成功\n");
return;
}
}
printf("未找到学号为%d的学生\n", id);
}
void deleteStudent() {
int id;
printf("请输入要删除的学号:");
scanf("%d", &id);
for (int i = 0; i < studentCount; i++) {
if (students[i].id == id) {
// 将后面的元素前移
for (int j = i; j < studentCount - 1; j++) {
students[j] = students[j + 1];
}
studentCount--;
printf("学生删除成功\n");
return;
}
}
printf("未找到学号为%d的学生\n", id);
}
void saveToFile() {
FILE *fp = fopen("students.dat", "wb");
if (fp == NULL) {
printf("文件打开失败\n");
return;
}
fwrite(&studentCount, sizeof(int), 1, fp);
fwrite(students, sizeof(Student), studentCount, fp);
fclose(fp);
printf("数据保存成功\n");
}
void loadFromFile() {
FILE *fp = fopen("students.dat", "rb");
if (fp == NULL) {
printf("首次运行,无历史数据\n");
return;
}
fread(&studentCount, sizeof(int), 1, fp);
fread(students, sizeof(Student), studentCount, fp);
fclose(fp);
printf("加载了%d个学生数据\n", studentCount);
}