C 编程速查表
通过实践实验室和真实场景学习 C 编程。LabEx 提供全面的 C 课程,涵盖基本语法、内存管理、指针、数据结构和高级技术。掌握 C 的强大功能,以构建高效的系统级应用程序并理解底层编程概念。
基本语法与结构
Hello World 程序
C 程序的结构基础。
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
头文件与预处理器
包含库和使用预处理器指令。
#include <stdio.h> // 标准输入/输出
#include <stdlib.h> // 标准库
#include <string.h> // 字符串函数
#include <math.h> // 数学函数
#define PI 3.14159
#define MAX_SIZE 100
注释
单行和多行注释。
// 单行注释
/*
多行注释
跨越多行
*/
// TODO: 实现功能
/* FIXME: 此处有错误 */
Main 函数
程序的入口点及返回值。
int main() {
// 程序的代码放在这里
return 0; // 成功
}
int main(int argc, char *argv[]) {
// argc: 参数数量
// argv: 参数值 (命令行)
return 0;
}
测验
登录后即可答题并追踪学习进度
在 main 函数中
return 0 表示什么?程序失败
程序仍在运行
程序成功执行
程序未返回值
基本输出
向控制台显示文本和变量。
printf("Hello\n");
printf("Value: %d\n", 42);
// 一行中的多个值
printf("Name: %s, Age: %d\n", name, age);
基本输入
从控制台读取用户输入。
int age;
char name[50];
scanf("%d", &age);
scanf("%s", name);
// 使用 fgets 读取包含空格的整行
fgets(name, sizeof(name), stdin);
数据类型与变量
基本类型
用于存储不同类型值的基本数据类型。
// 整数类型
int age = 25;
short small_num = 100;
long large_num = 1000000L;
long long huge_num = 9223372036854775807LL;
// 浮点类型
float price = 19.99f;
double precise = 3.14159265359;
// 字符和布尔值 (使用 int)
char grade = 'A';
int is_valid = 1; // 1 表示 true, 0 表示 false
数组与字符串
C 中的数组和字符串处理。
// 数组
int numbers[5] = {1, 2, 3, 4, 5};
int matrix[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
// 字符串 (字符数组)
char name[50] = "John Doe";
char greeting[] = "Hello";
char buffer[100]; // 未初始化
// 字符串长度和大小
int len = strlen(name);
int size = sizeof(buffer);
测验
登录后即可答题并追踪学习进度
C 语言中字符串是如何表示的?
作为特殊的字符串类型
作为整数
作为字符数组
仅作为指针
常量与修饰符
不可变值和存储修饰符。
// 常量
const int MAX_SIZE = 100;
const double PI = 3.14159;
// 预处理器常量
#define BUFFER_SIZE 512
#define TRUE 1
#define FALSE 0
// 存储修饰符
static int count = 0; // 静态变量
extern int global_var; // 外部变量
register int fast_var; // 寄存器提示
控制流结构
条件语句
基于条件做出决策。
// If-else 语句
if (age >= 18) {
printf("Adult\n");
} else if (age >= 13) {
printf("Teenager\n");
} else {
printf("Child\n");
}
// 三元运算符
char* status = (age >= 18) ? "Adult" : "Minor";
// Switch 语句
switch (grade) {
case 'A':
printf("Excellent!\n");
break;
case 'B':
printf("Good job!\n");
break;
default:
printf("Keep trying!\n");
}
For 循环
基于计数器的循环迭代。
// 传统的 for 循环
for (int i = 0; i < 10; i++) {
printf("%d ", i);
}
// 数组迭代
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
// 嵌套循环
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d,%d ", i, j);
}
}
测验
登录后即可答题并追踪学习进度
sizeof(numbers) / sizeof(numbers[0]) 计算的是什么?数组中的元素数量
数组的总内存大小
最后一个元素的索引
一个元素的大小
While 循环
基于条件的迭代。
// While 循环
int count = 0;
while (count < 5) {
printf("%d\n", count);
count++;
}
// Do-while 循环 (至少执行一次)
int input;
do {
printf("Enter a number (0 to quit): ");
scanf("%d", &input);
} while (input != 0);
循环控制
Break 和 continue 语句。
for (int i = 0; i < 10; i++) {
if (i == 3) {
continue; // 跳过本次迭代
}
if (i == 7) {
break; // 退出循环
}
printf("%d ", i);
}
// 嵌套循环与 break
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) break; // 只跳出内层循环
printf("%d,%d ", i, j);
}
}
函数
函数声明与定义
创建可重用的代码块。
// 函数声明 (原型)
int add(int a, int b);
void printMessage(char* msg);
// 函数定义
int add(int a, int b) {
return a + b;
}
void printMessage(char* msg) {
printf("%s\n", msg);
}
// 函数调用
int result = add(5, 3);
printMessage("Hello, functions!");
向函数传递数组
处理数组的函数。
// 数组作为参数 (指针)
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 修改数组元素
void doubleValues(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2;
}
}
递归函数
调用自身的函数。
// 阶乘计算
int factorial(int n) {
if (n <= 1) {
return 1; // 基准情况
}
return n * factorial(n - 1);
}
// 斐波那契数列
int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n-1) + fibonacci(n-2);
}
函数指针
用于动态行为的函数指针。
// 函数指针声明
int (*operation)(int, int);
// 将函数赋值给指针
operation = add;
int result = operation(5, 3);
// 函数指针数组
int (*operations[])(int, int) = {add, subtract, multiply};
result = operations[0](10, 5);
指针与内存管理
指针基础
声明和使用指针来访问内存地址。
int x = 10;
int *ptr = &x; // x 的指针
printf("Value of x: %d\n", x);
printf("Address of x: %p\n", &x);
printf("Value of ptr: %p\n", ptr);
printf("Value pointed by ptr: %d\n", *ptr);
// 通过指针修改值
*ptr = 20;
printf("New value of x: %d\n", x);
// 空指针
int *null_ptr = NULL;
数组与指针
数组和指针之间的关系。
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 指向第一个元素
// 数组表示法 vs 指针算术
printf("%d\n", arr[2]); // 数组表示法
printf("%d\n", *(p + 2)); // 指针算术
printf("%d\n", p[2]); // 将指针当作数组使用
// 使用指针迭代
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i));
}
动态内存分配
在运行时分配和释放内存。
#include <stdlib.h>
// 为单个整数分配内存
int *ptr = (int*)malloc(sizeof(int));
if (ptr != NULL) {
*ptr = 42;
printf("Value: %d\n", *ptr);
free(ptr); // 始终释放分配的内存
}
// 动态分配数组
int *arr = (int*)malloc(10 * sizeof(int));
if (arr != NULL) {
for (int i = 0; i < 10; i++) {
arr[i] = i * i;
}
free(arr);
}
字符串指针
处理字符串和字符指针。
// 字符串字面量和指针
char *str1 = "Hello"; // 字符串字面量
char str2[] = "World"; // 字符数组
char *str3 = (char*)malloc(20); // 动态字符串
// 字符串函数
strcpy(str3, "Dynamic");
printf("Length: %lu\n", strlen(str1));
printf("Compare: %d\n", strcmp(str1, str2));
strcat(str2, "!");
// 始终释放动态字符串
free(str3);
结构体与用户定义类型
结构体定义
定义具有多个字段的自定义数据类型。
// 结构体定义
struct Rectangle {
double width;
double height;
};
// 带 typedef 的结构体
typedef struct {
char name[50];
int age;
double gpa;
} Student;
// 创建和初始化结构体
struct Rectangle rect1 = {5.0, 3.0};
Student student1 = {"Alice", 20, 3.75};
// 访问结构体成员
printf("Area: %.2f\n", rect1.width * rect1.height);
printf("Student: %s, Age: %d\n", student1.name, student1.age);
嵌套结构体
包含其他结构的结构体。
typedef struct {
int day, month, year;
} Date;
typedef struct {
char name[50];
Date birthdate;
double salary;
} Employee;
Employee emp = {
"John Smith",
{15, 6, 1985},
50000.0
};
printf("Born: %d/%d/%d\n",
emp.birthdate.day,
emp.birthdate.month,
emp.birthdate.year);
结构体指针
使用指针访问和修改结构体。
Student *student_ptr = &student1;
// 通过指针访问 (两种方法)
printf("Name: %s\n", (*student_ptr).name);
printf("Age: %d\n", student_ptr->age);
// 通过指针修改
student_ptr->age = 21;
strcpy(student_ptr->name, "Alice Johnson");
// 动态结构体分配
Student *new_student = (Student*)malloc(sizeof(Student));
if (new_student != NULL) {
strcpy(new_student->name, "Bob");
new_student->age = 19;
new_student->gpa = 3.2;
free(new_student);
}
联合体和枚举
替代的数据组织方法。
// 联合体 - 共享内存空间
union Data {
int integer;
float floating;
char character;
};
union Data data;
data.integer = 42;
printf("Integer: %d\n", data.integer);
// 枚举
enum Weekday {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
};
enum Weekday today = FRIDAY;
printf("Today is day %d\n", today);
文件输入/输出操作
文件读取
从文本文件读取数据。
#include <stdio.h>
// 按字符读取整个文件
FILE *file = fopen("data.txt", "r");
if (file != NULL) {
int ch;
while ((ch = fgetc(file)) != EOF) {
putchar(ch);
}
fclose(file);
}
// 按行读取
FILE *file2 = fopen("lines.txt", "r");
char buffer[256];
while (fgets(buffer, sizeof(buffer), file2) != NULL) {
printf("Line: %s", buffer);
}
fclose(file2);
// 读取格式化数据
FILE *numbers = fopen("numbers.txt", "r");
int num;
while (fscanf(numbers, "%d", &num) == 1) {
printf("Number: %d\n", num);
}
fclose(numbers);
错误检查
安全处理文件操作。
FILE *file = fopen("data.txt", "r");
if (file == NULL) {
printf("Error opening file!\n");
perror("fopen"); // 打印系统错误信息
return 1;
}
// 检查读取错误
if (ferror(file)) {
printf("Error reading file!\n");
}
// 检查文件结束
if (feof(file)) {
printf("Reached end of file\n");
}
fclose(file);
文件写入
向文本文件写入数据。
// 写入文件
FILE *outfile = fopen("output.txt", "w");
if (outfile != NULL) {
fprintf(outfile, "Hello, file!\n");
fprintf(outfile, "Number: %d\n", 42);
fclose(outfile);
}
// 追加到现有文件
FILE *appendfile = fopen("log.txt", "a");
if (appendfile != NULL) {
fprintf(appendfile, "New log entry\n");
fclose(appendfile);
}
// 写入数组到文件
int numbers[] = {1, 2, 3, 4, 5};
FILE *numfile = fopen("numbers.txt", "w");
for (int i = 0; i < 5; i++) {
fprintf(numfile, "%d ", numbers[i]);
}
fclose(numfile);
二进制文件操作
高效地读写二进制数据。
// 写入二进制数据
Student students[3] = {
{"Alice", 20, 3.75},
{"Bob", 21, 3.2},
{"Charlie", 19, 3.9}
};
FILE *binfile = fopen("students.bin", "wb");
fwrite(students, sizeof(Student), 3, binfile);
fclose(binfile);
// 读取二进制数据
Student loaded_students[3];
FILE *readbin = fopen("students.bin", "rb");
fread(loaded_students, sizeof(Student), 3, readbin);
fclose(readbin);
字符串操作
字符串函数
来自 string.h 库的常见字符串操作。
#include <string.h>
char str1[50] = "Hello";
char str2[] = "World";
char dest[100];
// 字符串长度
int len = strlen(str1);
printf("Length: %d\n", len);
// 字符串复制
strcpy(dest, str1);
strncpy(dest, str1, 10); // 复制前 10 个字符
// 字符串连接
strcat(dest, " ");
strcat(dest, str2);
strncat(dest, "!", 1); // 追加 1 个字符
// 字符串比较
int result = strcmp(str1, str2);
if (result == 0) {
printf("Strings are equal\n");
}
字符串搜索
在字符串中查找子串和字符。
char text[] = "The quick brown fox";
char *ptr;
// 查找字符的第一次出现
ptr = strchr(text, 'q');
if (ptr != NULL) {
printf("Found 'q' at position: %ld\n", ptr - text);
}
// 查找最后一次出现
ptr = strrchr(text, 'o');
printf("Last 'o' at position: %ld\n", ptr - text);
// 查找子串
ptr = strstr(text, "brown");
if (ptr != NULL) {
printf("Found 'brown' at: %s\n", ptr);
}
字符串转换
将字符串转换为数字,反之亦然。
#include <stdlib.h>
// 字符串转数字转换
char num_str[] = "12345";
char float_str[] = "3.14159";
int num = atoi(num_str);
long long_num = atol(num_str);
double float_num = atof(float_str);
printf("Integer: %d\n", num);
printf("Long: %ld\n", long_num);
printf("Double: %.2f\n", float_num);
// 数字转字符串 (使用 sprintf)
char buffer[50];
sprintf(buffer, "%d", 42);
sprintf(buffer, "%.2f", 3.14159);
printf("String: %s\n", buffer);
自定义字符串处理
手动字符串操作技术。
// 计算字符串中特定字符的个数
int countChar(char *str, char target) {
int count = 0;
while (*str) {
if (*str == target) count++;
str++;
}
return count;
}
// 就地反转字符串
void reverseString(char *str) {
int len = strlen(str);
for (int i = 0; i < len/2; i++) {
char temp = str[i];
str[i] = str[len-1-i];
str[len-1-i] = temp;
}
}
编译与构建过程
GCC 编译
用于 C 语言的 GNU 编译器集合。
# 基本编译
gcc -o program main.c
# 包含调试信息
gcc -g -o program main.c
# 优化级别
gcc -O2 -o program main.c
# 多个源文件
gcc -o program main.c utils.c math.c
# 包含附加目录
gcc -I/usr/local/include -o program main.c
# 链接库
gcc -o program main.c -lm -lpthread
C 标准
使用特定 C 标准版本进行编译。
# C90/C89 标准 (ANSI C)
gcc -std=c89 -o program main.c
# C99 标准
gcc -std=c99 -o program main.c
# C11 标准 (推荐)
gcc -std=c11 -o program main.c
# C18 标准 (最新)
gcc -std=c18 -o program main.c
# 启用所有警告
gcc -Wall -Wextra -std=c11 -o program main.c
Makefile 基础
使用 make 工具自动化编译过程。
# 简单的 Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -g
TARGET = program
SOURCES = main.c utils.c
$(TARGET): $(SOURCES)
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)
clean:
rm -f $(TARGET)
.PHONY: clean
最佳实践与技巧
命名约定
一致的命名使代码更具可读性。
// 变量和函数:snake_case (蛇形命名法)
int student_count;
double calculate_average(int scores[], int size);
// 常量:UPPER_CASE (大写)
#define MAX_BUFFER_SIZE 1024
#define PI 3.14159
// 结构体:PascalCase 或 snake_case
typedef struct {
char name[50];
int age;
} Student;
// 全局变量:以 g_ 为前缀
int g_total_count = 0;
// 函数参数:清晰的名称
void process_data(int *input_array, int array_size);
内存安全
防止常见的内存相关错误。
// 始终初始化变量
int count = 0; // 好
int count; // 危险 - 未初始化
// 检查 malloc 的返回值
int *ptr = malloc(sizeof(int) * 10);
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return -1;
}
// 始终释放分配的内存
free(ptr);
ptr = NULL; // 防止意外重用
// 数组边界检查
for (int i = 0; i < array_size; i++) {
// 安全的数组访问
array[i] = i;
}
性能提示
编写高效的 C 代码。
// 使用适当的数据类型
char small_num = 10; // 用于小数值
int normal_num = 1000; // 用于典型整数
// 在循环中最小化函数调用
int len = strlen(str); // 只计算一次
for (int i = 0; i < len; i++) {
// 处理字符串
}
// 对频繁访问的变量使用 register
register int counter;
// 尺寸已知时,优先使用数组而不是动态分配
int fixed_array[100]; // 栈分配
// vs
int *dynamic_array = malloc(100 * sizeof(int));
代码组织
为可维护性组织代码。
// 头文件 (utils.h)
#ifndef UTILS_H
#define UTILS_H
// 函数原型
double calculate_area(double radius);
int fibonacci(int n);
// 结构体定义
typedef struct {
int x, y;
} Point;
#endif // UTILS_H
// 实现文件 (utils.c)
#include "utils.h"
#include <math.h>
double calculate_area(double radius) {
return M_PI * radius * radius;
}