CentOS Stream 9入门学习教程,从入门到精通,CentOS Stream 9 中 Linux C 编程 —语法详解与实战案例(13)

CentOS Stream 9 中 Linux C 编程 ---语法详解与实战案例


一、概述

在 Linux 系统中进行 C 语言开发,主要依赖三大核心工具:

工具 功能
GCC GNU 编译器集合,用于编译 C/C++ 代码
GDB GNU 调试器,用于调试程序运行时错误
Make / Makefile 自动化编译工具,管理多文件项目依赖
Autotools 项目自动化构建系统(configure + Makefile)

✅ 开发环境准备(CentOS Stream 9):

bash 复制代码
# 安装开发工具包
sudo dnf groupinstall "Development Tools" -y

# 安装调试工具
sudo dnf install gdb -y

# 安装 Autotools
sudo dnf install autoconf automake libtool -y

# 验证安装
gcc --version
gdb --version
make --version

二、GCC 编译


2.1 GCC 工具链

GCC(GNU Compiler Collection)包含多个工具:

工具 作用
gcc C 语言编译器前端
cpp C 预处理器
as 汇编器
ld 链接器
ar 静态库打包工具
nmobjdumpreadelf 目标文件分析工具

✅ 编译流程四阶段:

复制代码
源代码(.c) 
  → 预处理(.i) 
  → 编译(.s) 
  → 汇编(.o) 
  → 链接(可执行文件)

2.2 gcc 命令基本用法

基本语法:

bash 复制代码
gcc [选项] [输入文件] -o [输出文件]

常用选项:

选项 说明
-E 只进行预处理
-S 只进行编译到汇编
-c 只编译到目标文件(.o)
-o 指定输出文件名
-g 生成调试信息(供 GDB 使用)
-Wall 启用所有警告
-O0/-O1/-O2/-O3 优化级别(0=无优化,3=最高优化)
-I<目录> 指定头文件搜索路径
-L<目录> 指定库文件搜索路径
-l<库名> 链接指定库(如 -lm 链接 math 库)

2.3 gcc 使用实例

✅ 案例1:编译单文件程序

c 复制代码
// hello.c
#include <stdio.h>

int main() {
    printf("Hello, Linux C Programming!\n");
    return 0;
}
bash 复制代码
# 编译并运行
gcc hello.c -o hello
./hello
# 输出:Hello, Linux C Programming!

✅ 案例2:分步编译(预处理 → 编译 → 汇编 → 链接)

bash 复制代码
# 1. 预处理(展开宏、包含头文件)
gcc -E hello.c -o hello.i

# 2. 编译为汇编代码
gcc -S hello.i -o hello.s

# 3. 汇编为目标文件
gcc -c hello.s -o hello.o

# 4. 链接生成可执行文件
gcc hello.o -o hello

# 或者一步到位
gcc hello.c -o hello

✅ 案例3:启用调试和警告

bash 复制代码
gcc -g -Wall -O0 hello.c -o hello_debug
# -g:生成调试信息
# -Wall:显示所有警告
# -O0:不优化,便于调试

✅ 案例4:链接数学库

c 复制代码
// math_test.c
#include <stdio.h>
#include <math.h>

int main() {
    double x = 2.0;
    double result = sqrt(x);
    printf("sqrt(%.1f) = %.6f\n", x, result);
    return 0;
}
bash 复制代码
# 必须链接数学库 -lm
gcc math_test.c -o math_test -lm
./math_test
# 输出:sqrt(2.0) = 1.414214

三、综合案例:使用 GCC 编译包含多个源文件的项目


3.1 案例概述

项目结构:

复制代码
project/
├── main.c          // 主函数
├── calc.h          // 函数声明
├── calc.c          // 函数实现
└── Makefile        // 后续章节使用

功能:实现加减乘除计算器


3.2 案例详解

✅ 文件1:calc.h(头文件)

c 复制代码
// calc.h - 函数声明
#ifndef CALC_H
#define CALC_H

// 加法
int add(int a, int b);

// 减法
int subtract(int a, int b);

// 乘法
int multiply(int a, int b);

// 除法(注意除零检查)
double divide(int a, int b);

#endif

✅ 文件2:calc.c(函数实现)

c 复制代码
// calc.c - 函数定义
#include "calc.h"
#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;
}

double divide(int a, int b) {
    if (b == 0) {
        fprintf(stderr, "Error: Division by zero!\n");
        return 0.0;
    }
    return (double)a / (double)b;
}

✅ 文件3:main.c(主程序)

c 复制代码
// main.c - 主函数
#include <stdio.h>
#include "calc.h"

int main() {
    int x = 10, y = 5;

    printf("x = %d, y = %d\n", x, y);
    printf("%d + %d = %d\n", x, y, add(x, y));
    printf("%d - %d = %d\n", x, y, subtract(x, y));
    printf("%d * %d = %d\n", x, y, multiply(x, y));
    printf("%d / %d = %.2f\n", x, y, divide(x, y));

    // 测试除零
    printf("10 / 0 = %.2f\n", divide(10, 0));

    return 0;
}

✅ 手动编译命令:

bash 复制代码
# 方法1:一步编译所有源文件
gcc main.c calc.c -o calculator -g -Wall

# 方法2:分步编译(推荐用于大型项目)
gcc -c main.c -o main.o          # 编译 main.c
gcc -c calc.c -o calc.o          # 编译 calc.c
gcc main.o calc.o -o calculator  # 链接

# 运行
./calculator

✅ 输出:

复制代码
x = 10, y = 5
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2.00
Error: Division by zero!
10 / 0 = 0.00

四、GDB 调试


4.1 GDB 基本命令

编译时必须加 -g 选项:

bash 复制代码
gcc -g -Wall main.c calc.c -o calculator

常用 GDB 命令:

命令 说明
gdb ./program 启动 GDB
runr 运行程序
breakb 设置断点(b main, b 10, b calc.c:5
continuec 继续执行
nextn 单步执行(不进入函数)
steps 单步执行(进入函数)
printp 打印变量值(p x, p result
listl 显示源代码
backtracebt 显示调用栈
quitq 退出 GDB

4.2 综合案例:使用 GDB 调试 C 语言项目


4.2.1 案例概述

修改 calc.c 引入一个 bug,使用 GDB 定位并修复。


4.2.2 案例详解

✅ 修改 calc.c(故意制造 bug):

c 复制代码
// calc.c - 有 bug 的版本
#include "calc.h"
#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 + 1;  // BUG: 多加了1!
}

double divide(int a, int b) {
    if (b == 0) {
        fprintf(stderr, "Error: Division by zero!\n");
        return 0.0;
    }
    return (double)a / (double)b;
}

✅ 重新编译:

bash 复制代码
gcc -g -Wall main.c calc.c -o calculator

✅ 启动 GDB:

bash 复制代码
gdb ./calculator

✅ GDB 调试过程:

gdb 复制代码
(gdb) break multiply        # 在 multiply 函数设断点
Breakpoint 1 at 0x40114e: file calc.c, line 12.

(gdb) run                   # 运行程序
Starting program: /home/user/project/calculator
x = 10, y = 5
10 + 5 = 15
10 - 5 = 5

Breakpoint 1, multiply (a=10, b=5) at calc.c:12
12	    return a * b + 1;   # 停在这里

(gdb) print a               # 查看变量 a
$1 = 10

(gdb) print b               # 查看变量 b
$2 = 5

(gdb) next                  # 执行下一行
13	}

(gdb) print a * b + 1       # 手动计算
$3 = 51                     # 应该是50,多加了1!

(gdb) quit                  # 退出

✅ 修复 bug:

c 复制代码
// 修复:去掉 +1
int multiply(int a, int b) {
    return a * b;  // 修复后
}

✅ 重新编译并测试:

bash 复制代码
gcc -g -Wall main.c calc.c -o calculator
./calculator
# 输出:10 * 5 = 50 (正确!)

五、Make 编译


5.1 make 和 Makefile 概述

Makefile 是一个文本文件,定义了:

  • 目标(target)
  • 依赖(dependencies)
  • 命令(commands)

make 工具根据 Makefile 自动判断哪些文件需要重新编译。


5.2 Makefile 语法基础

基本语法:

makefile 复制代码
target: dependencies
<TAB>command

✅ 示例:

makefile 复制代码
# 最简单的 Makefile
hello: hello.c
	gcc hello.c -o hello

特殊变量:

变量 说明
$@ 目标文件名
$< 第一个依赖文件
$^ 所有依赖文件

伪目标(.PHONY):

makefile 复制代码
.PHONY: clean
clean:
	rm -f *.o calculator

5.3 Makefile 实例

✅ 基础版 Makefile(对应前面的计算器项目)

makefile 复制代码
# Makefile for calculator project

# 编译器
CC = gcc

# 编译选项
CFLAGS = -g -Wall

# 目标文件
TARGET = calculator

# 源文件
SRCS = main.c calc.c

# 目标文件
OBJS = $(SRCS:.c=.o)  # main.o calc.o

# 默认目标
all: $(TARGET)

# 链接目标文件
$(TARGET): $(OBJS)
	$(CC) $(OBJS) -o $(TARGET)

# 编译 .c 到 .o
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

# 清理
.PHONY: clean
clean:
	rm -f $(OBJS) $(TARGET)

# 重新编译
.PHONY: rebuild
rebuild: clean all

✅ 使用:

bash 复制代码
# 编译
make

# 清理
make clean

# 重新编译
make rebuild

# 查看帮助(可选添加)
.PHONY: help
help:
	@echo "Available targets:"
	@echo "  make     - 编译项目"
	@echo "  make clean - 清理目标文件"
	@echo "  make rebuild - 重新编译"

5.4 Make 编译的基本步骤

  1. 编写 Makefile:定义目标、依赖、命令
  2. 运行 make:自动检查依赖,只编译修改过的文件
  3. 增量编译:节省时间,提高效率
  4. 清理项目make clean 删除中间文件

六、综合案例:使用 Makefile 管理 C 语言项目


6.1 案例概述

项目结构升级:

复制代码
advanced_project/
├── src/
│   ├── main.c
│   ├── calc.c
│   └── utils.c
├── include/
│   ├── calc.h
│   └── utils.h
├── lib/
├── bin/
└── Makefile

新增功能:字符串工具函数


6.2 案例详解1(基础版)

✅ 创建目录结构:

bash 复制代码
mkdir -p advanced_project/{src,include,lib,bin}
cd advanced_project

✅ 文件1:include/utils.h

c 复制代码
// utils.h
#ifndef UTILS_H
#define UTILS_H

// 字符串反转
void reverse_string(char *str);

// 字符串长度
int string_length(const char *str);

#endif

✅ 文件2:src/utils.c

c 复制代码
// utils.c
#include "utils.h"

int string_length(const char *str) {
    int len = 0;
    while (str[len] != '\0') {
        len++;
    }
    return len;
}

void reverse_string(char *str) {
    int len = string_length(str);
    int i, j;
    char temp;
    for (i = 0, j = len - 1; i < j; i++, j--) {
        temp = str[i];
        str[i] = str[j];
        str[j] = temp;
    }
}

✅ 修改 src/main.c

c 复制代码
// main.c
#include <stdio.h>
#include "calc.h"
#include "utils.h"

int main() {
    int x = 10, y = 5;
    char test_str[] = "Hello World";

    printf("=== Calculator Test ===\n");
    printf("%d + %d = %d\n", x, y, add(x, y));
    printf("%d * %d = %d\n", x, y, multiply(x, y));

    printf("\n=== String Utils Test ===\n");
    printf("Original: %s\n", test_str);
    reverse_string(test_str);
    printf("Reversed: %s\n", test_str);

    return 0;
}

✅ 基础版 Makefile:

makefile 复制代码
# Makefile - 基础版

CC = gcc
CFLAGS = -g -Wall -I./include
TARGET = bin/calculator
SRCDIR = src
INCDIR = include
BINDIR = bin

SRCS = $(wildcard $(SRCDIR)/*.c)
OBJS = $(SRCS:$(SRCDIR)/%.c=$(BINDIR)/%.o)

$(BINDIR)/%.o: $(SRCDIR)/%.c
	@mkdir -p $(BINDIR)
	$(CC) $(CFLAGS) -c $< -o $@

$(TARGET): $(OBJS)
	@mkdir -p $(dir $@)
	$(CC) $(OBJS) -o $@

.PHONY: clean
clean:
	rm -rf $(BINDIR)

.PHONY: run
run: $(TARGET)
	./$(TARGET)

.PHONY: all
all: $(TARGET)

.PHONY: help
help:
	@echo "Targets:"
	@echo "  make     - 编译"
	@echo "  make run - 运行"
	@echo "  make clean - 清理"

✅ 编译运行:

bash 复制代码
make
make run

输出:

复制代码
=== Calculator Test ===
10 + 5 = 15
10 * 5 = 50

=== String Utils Test ===
Original: Hello World
Reversed: dlroW olleH

6.3 案例详解2(进阶版)

✅ 进阶版 Makefile(支持调试/发布模式、自动依赖)

makefile 复制代码
# Makefile - 进阶版

# ============ 配置 ============
CC = gcc
BINDIR = bin
SRCDIR = src
INCDIR = include
LIBDIR = lib

# 调试模式(默认)或发布模式
DEBUG ?= 1

ifeq ($(DEBUG), 1)
    CFLAGS = -g -Wall -O0 -DDEBUG
    TARGET = $(BINDIR)/calculator_debug
else
    CFLAGS = -O2 -DNDEBUG
    TARGET = $(BINDIR)/calculator_release
endif

CFLAGS += -I$(INCDIR)

# 自动发现源文件
SRCS = $(wildcard $(SRCDIR)/*.c)
OBJS = $(SRCS:$(SRCDIR)/%.c=$(BINDIR)/%.o)
DEPS = $(OBJS:.o=.d)

# 自动生成依赖
$(BINDIR)/%.d: $(SRCDIR)/%.c
	@mkdir -p $(dir $@)
	@$(CC) -MM -MT "$(BINDIR)/$*.o" -MF $@ $(CFLAGS) $<

# 包含依赖文件(如果存在)
-include $(DEPS)

# 编译规则
$(BINDIR)/%.o: $(SRCDIR)/%.c
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -c $< -o $@

# 链接
$(TARGET): $(OBJS)
	@mkdir -p $(dir $@)
	$(CC) $(OBJS) -o $@

# 清理
.PHONY: clean
clean:
	rm -rf $(BINDIR)/*

# 重新编译
.PHONY: rebuild
rebuild: clean $(TARGET)

# 运行
.PHONY: run
run: $(TARGET)
	./$(TARGET)

# 调试(使用 GDB)
.PHONY: debug
debug: $(TARGET)
	gdb ./$(TARGET)

# 发布版本
.PHONY: release
release:
	$(MAKE) DEBUG=0

# 默认目标
.PHONY: all
all: $(TARGET)

# 帮助
.PHONY: help
help:
	@echo "Advanced Makefile Usage:"
	@echo "  make           - 编译调试版本"
	@echo "  make DEBUG=0   - 编译发布版本"
	@echo "  make run       - 运行程序"
	@echo "  make debug     - 使用 GDB 调试"
	@echo "  make release   - 编译发布版本"
	@echo "  make clean     - 清理"
	@echo "  make rebuild   - 重新编译"

✅ 使用示例:

bash 复制代码
# 调试版本
make
make run

# 发布版本
make release
./bin/calculator_release

# 调试程序
make debug

# 清理
make clean

七、Makefile 自动生成技术


7.1 Autotools 简介

Autotools 是一套自动化构建工具,包含:

工具 作用
autoconf 生成 configure 脚本
automake 生成 Makefile.in
libtool 管理库文件

工作流程:

复制代码
Makefile.am + configure.ac 
  → automake → Makefile.in
  → autoconf → configure
  → ./configure → Makefile
  → make → 可执行文件

八、综合案例:使用 Autotools 管理 C 语言项目


8.1 案例概述

项目结构:

复制代码
autotools_project/
├── configure.ac        # autoconf 配置
├── Makefile.am         # automake 配置
├── src/
│   ├── Makefile.am     # 子目录 Makefile.am
│   ├── main.c
│   ├── calc.c
│   └── utils.c
└── include/
    ├── calc.h
    └── utils.h

8.2 案例详解

✅ 步骤1:创建 configure.ac

bash 复制代码
# 在项目根目录创建 configure.ac
touch configure.ac
autoconf 复制代码
# configure.ac
AC_INIT([calculator], [1.0], [your-email@example.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
    Makefile
    src/Makefile
])
AC_OUTPUT

✅ 步骤2:创建根目录 Makefile.am

makefile 复制代码
# Makefile.am
SUBDIRS = src

✅ 步骤3:创建 src/Makefile.am

makefile 复制代码
# src/Makefile.am
bin_PROGRAMS = calculator
calculator_SOURCES = main.c calc.c utils.c
calculator_CFLAGS = -I$(top_srcdir)/include

✅ 步骤4:运行 Autotools 生成构建系统

bash 复制代码
# 1. 生成 aclocal.m4
aclocal

# 2. 生成 configure 脚本
autoconf

# 3. 生成 Makefile.in
automake --add-missing

# 4. 运行 configure 生成 Makefile
./configure

# 5. 编译
make

# 6. 安装(可选)
sudo make install

# 7. 清理
make clean

✅ 完整构建脚本 build.sh

bash 复制代码
#!/bin/bash
# build.sh - 一键构建脚本

set -e  # 遇错停止

echo "🔄 正在生成构建系统..."

# 生成 aclocal.m4
echo "1/5: 运行 aclocal..."
aclocal

# 生成 configure
echo "2/5: 运行 autoconf..."
autoconf

# 生成 Makefile.in
echo "3/5: 运行 automake..."
automake --add-missing

# 配置项目
echo "4/5: 运行 configure..."
./configure

# 编译
echo "5/5: 运行 make..."
make

echo "✅ 构建完成!可执行文件:src/calculator"
echo "💡 运行:./src/calculator"

✅ 使用:

bash 复制代码
chmod +x build.sh
./build.sh
./src/calculator

✅ 支持标准操作:

bash 复制代码
./configure --prefix=/usr/local  # 指定安装路径
make
make check                      # 运行测试(需配置)
sudo make install               # 安装
make uninstall                  # 卸载(需配置)

✅ Linux C 编程最佳实践

  1. 始终使用 -Wall -Wextra:开启所有警告
  2. 调试版本加 -g:便于 GDB 调试
  3. 使用 Makefile:管理多文件项目
  4. 分离头文件和源文件include/src/
  5. 使用 Autotools:便于跨平台分发
  6. 版本控制:使用 Git 管理代码
  7. 代码格式化 :使用 indentclang-format
  8. 静态分析 :使用 cppchecksplint

📚 附录:常用命令速查表

功能 命令
编译单文件 gcc -g -Wall file.c -o program
调试程序 gdb ./program
设置断点 break function_namebreak line_number
运行 Makefile make
清理 make clean
生成 Autotools aclocal && autoconf && automake --add-missing
配置项目 ./configure
安装 sudo make install

这份文档覆盖了 CentOS Stream 9 上 Linux C 编程的全部核心知识点 + 语法细节 + 实用案例 + 综合项目,所有代码均含详细注释,可直接用于教学、自学或生产环境参考。

相关推荐
piaoroumi3 小时前
UVC调试
linux·运维·前端
刘洋浪子3 小时前
Git命令学习
git·学习·elasticsearch
VekiSon3 小时前
Linux系统编程——标准IO
linux·运维·服务器
大白的编程日记.4 小时前
【计算网络学习笔记】Socket编程UDP实现简单聊天室
网络·笔记·学习
爱宇阳4 小时前
Linux 安全加固:禁用 IPv4 ICMP 重定向发送
linux·运维·安全
阿豪学编程4 小时前
动静态库制作与原理
linux·个人开发
2401_861786184 小时前
linux修改ip地址(有详细步骤)kali
linux·运维·服务器
颜子鱼4 小时前
Linux platform总线驱动框架
linux·驱动开发
徐子元竟然被占了!!4 小时前
Linux-top
linux·运维·windows