Linux ——— sudo权限管理和GCC编译工具链的核心操作

目录

[让普通用户拥有 sudo 权限指令](#让普通用户拥有 sudo 权限指令)

[一、sudo 权限的核心逻辑](#一、sudo 权限的核心逻辑)

[二、让普通用户拥有 sudo 权限的完整配置流程(结合示例)](#二、让普通用户拥有 sudo 权限的完整配置流程(结合示例))

四、总结
[gcc 相关指令的基本使用](#gcc 相关指令的基本使用)

[生成可执行程序 a.out 并且执行](#生成可执行程序 a.out 并且执行)

[一、gcc test_1.c 命令的核心解析](#一、gcc test_1.c 命令的核心解析)

[二、a.out 文件的核心解析](#二、a.out 文件的核心解析)

三、总结

[gcc -E(仅执行预处理步骤)](#gcc -E(仅执行预处理步骤))

[一、gcc -E test_1.c -o test_1.i 命令的核心解析](#一、gcc -E test_1.c -o test_1.i 命令的核心解析)

二、该命令的核心使用场景

三、总结

[gcc -S(仅执行编译生成汇编)](#gcc -S(仅执行编译生成汇编))

[一、gcc -S test_1.c -o test_1.s 指令的核心解析](#一、gcc -S test_1.c -o test_1.s 指令的核心解析)

二、该指令的核心使用场景

三、总结

[gcc -c(仅执行汇编生成机器可识别的二进制)](#gcc -c(仅执行汇编生成机器可识别的二进制))

[一、gcc -c test_1.c -o test_1.o 命令的核心解析](#一、gcc -c test_1.c -o test_1.o 命令的核心解析)

二、该指令的核心使用场景

三、总结
安装C语言静态库的指令
安装C++静态库的指令
[make 指令 和 makefile 文件](#make 指令 和 makefile 文件)

[简单使用 make 和 makefile](#简单使用 make 和 makefile)

[一、makefile 文件与 make 指令的核心关联](#一、makefile 文件与 make 指令的核心关联)

[二、makefile 文件内部的指令解析](#二、makefile 文件内部的指令解析)

[三、make test_1.out 指令解析](#三、make test_1.out 指令解析)

[四、make clean 指令解析](#四、make clean 指令解析)

五、核心补充细节

总结

[重复执行 make 指令和 touch 更新文件时间](#重复执行 make 指令和 touch 更新文件时间)

[一、make 指令的核心解析](#一、make 指令的核心解析)

[二、touch test_1.c 指令的核心解析](#二、touch test_1.c 指令的核心解析)

[三、stat test_1.c 指令的核心解析](#三、stat test_1.c 指令的核心解析)

四、三者的关联逻辑

伪目标:.PHONY

[一、.PHONY:test_1.out 的核心解析](#一、.PHONY:test_1.out 的核心解析)

二、伪目标的核心特性(结合示例)

三、伪目标的核心使用场景

四、关键补充细节

总结


让普通用户拥有 sudo 权限指令

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ ~]$ pwd
/home/ranjiaju

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ ~]$ sudo touch test.c
[sudo] password for ranjiaju: 
ranjiaju is not in the sudoers file.  This incident will be reported.

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ ~]$ su -
Password: 
Last login: Thu Dec  4 14:52:30 CST 2025 on pts/0
[root@iZ2vc15k23y9vpuyi3tiqzZ ~]# vim /etc/sudoers

// 进入 sudoers 文件后,找到 【root    ALL=(ALL)       ALL】 这行指令
// 并且在这行指令下方加上 【ranjiaju        ALL=(ALL)       ALL】 就可以了

// 注意:因为 sudoers 这个文件的拥有者和所属组都是 root ,而且权限是 -r--r-----
//      所以加上自己账号的 sudo 权限后,要通过 :wq! 的方式强制保存并退出

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ ~]$ ll
total 4
drwxrwxr-x 2 ranjiaju ranjiaju 4096 Dec  3 23:13 test

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ ~]$ sudo touch test.c

[sudo] password for ranjiaju: 

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ ~]$ ll
total 4
drwxrwxr-x 2 ranjiaju ranjiaju 4096 Dec  3 23:13 test
-rw-r--r-- 1 root     root        0 Dec  4 15:04 test.c

一、sudo 权限的核心逻辑

sudo(superuser do)是 Linux 中让 普通用户临时以 root(或指定用户)权限执行命令 的工具,其权限控制核心依赖 /etc/sudoers 配置文件 ------ 只有该文件中明确授权的普通用户,才能使用 sudo 执行高权限操作;未授权用户执行 sudo 时,会提示 is not in the sudoers file(示例中 ranjiaju 初始状态即为此类)。

二、让普通用户拥有 sudo 权限的完整配置流程(结合示例)

示例中的操作覆盖了从 "权限缺失" 到 "授权生效" 的全流程,核心是修改 /etc/sudoers 文件并添加用户授权规则,步骤拆解如下:

1. 权限缺失的触发场景

普通用户 ranjiaju 执行 sudo touch test.c 时,系统提示:

复制代码
ranjiaju is not in the sudoers file.  This incident will be reported.

原因:/etc/sudoers 文件中无 ranjiaju 的授权记录,sudo 拒绝其使用高权限操作。

2. 切换 root 编辑 sudoers 文件(唯一可修改该文件的身份)

执行 su - 切换到 root 用户(登录式切换,获取完整 root 权限),然后执行 vim /etc/sudoers 编辑配置文件 ------该文件默认权限为 -r--r-----(仅 root 可读,无写权限),且拥有者 / 所属组均为 root,普通用户无法修改,必须以 root 身份操作。

3. 添加核心授权规则

/etc/sudoers 中找到 root 的授权行:

复制代码
root    ALL=(ALL)       ALL

在其下方添加普通用户 ranjiaju 的授权行:

复制代码
ranjiaju        ALL=(ALL)       ALL

这是让普通用户获得完整 sudo 权限的核心指令,各字段含义如下:

字段位置 内容 含义
第一个 ranjiaju 被授权的普通用户名(需与系统中实际用户名一致)
第二个 ALL 允许执行 sudo 的主机(ALL 表示所有主机,单服务器场景固定为 ALL)
第三个 (ALL) 允许切换到的用户身份(ALL 表示可切换到任意用户,通常为 root)
第四个 ALL 允许执行的命令(ALL 表示可执行任意高权限命令,也可限制仅执行特定命令)

4. 强制保存退出 sudoers 文件

/etc/sudoers 权限为 -r--r-----(只读),普通 :wq 保存会失败,需执行 :wq! 强制保存并退出 ------ 这是修改该文件的关键操作,否则配置无法生效。

5. 验证 sudo 权限生效

切换回 ranjiaju 用户,执行 sudo touch test.c,输入 ranjiaju 自身的密码(非 root 密码),即可成功创建文件:

  • 执行结果:test.c 文件的拥有者 / 所属组为 root(因 sudo 以 root 权限执行命令);
  • 核心变化:ranjiaju 从 "无 sudo 权限" 变为 "可通过 sudo 执行任意 root 权限命令"。

四、总结

让普通用户拥有 sudo 权限的核心是 /etc/sudoers 中添加用户授权规则,核心步骤为:

  1. su - 切换到 root;
  2. vim /etc/sudoers(或 visudo)编辑配置;
  3. 添加 用户名 ALL=(ALL) ALL 授权行;
  4. :wq! 强制保存退出;
  5. 切换普通用户验证 sudo 命令。

该配置的核心价值是:普通用户无需长期登录 root(降低误操作风险),仅在需要时通过 sudo 临时获取高权限,兼顾权限管控与操作便捷性。


gcc 相关指令的基本使用

生成可执行程序 a.out 并且执行

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ pwd
/home/ranjiaju/test

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 4
-rw-rw-r-- 1 ranjiaju ranjiaju 156 Dec  4 15:36 test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ cat test_1.c
#include<stdio.h>

#define M 100

int main()
{
    printf("helloc Linux\n");

    // printf("helloc Linux\n");
    
    printf("%d\n", M);

    return 0;
}

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ gcc test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 16
-rwxrwxr-x 1 ranjiaju ranjiaju 8496 Dec  4 15:36 a.out
-rw-rw-r-- 1 ranjiaju ranjiaju  156 Dec  4 15:36 test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ./a.out
helloc Linux
100

一、gcc test_1.c 命令的核心解析

gcc(GNU Compiler Collection)是 Linux 系统中 编译 C 语言源文件的核心工具gcc test_1.c 是编译单个 C 源文件的基础命令,会自动完成「预处理 → 编译 → 汇编 → 链接」四个核心步骤,最终生成可执行文件,是将 C 代码转换为可运行程序的关键操作。

1. 编译的四个核心步骤(后台自动完成)

步骤 核心操作 处理对象 / 生成文件(默认不保留中间文件)
预处理(Preprocessing) 处理源文件中的预处理指令:- #include<stdio.h>:引入标准输入输出头文件;- #define M 100:将宏 M 替换为 100;- 注释(如 // printf(...))被删除 源文件 test_1.c → 生成 .i 预处理文件
编译(Compilation) 将预处理后的文件转换为汇编语言代码(人类可读的机器指令) .i 文件 → 生成 .s 汇编文件
汇编(Assembly) 将汇编语言代码转换为二进制机器码(目标文件) .s 文件 → 生成 .o 目标文件
链接(Linking) 将目标文件与系统库(如 stdio.h 对应的 libc 标准库)链接,补全程序运行所需的底层函数(如 printf .o 文件 + 系统库 → 生成可执行文件

示例中执行 gcc test_1.c 后,无任何报错输出,说明四个步骤均成功完成,未出现语法错误、库缺失等问题。

2. 命令的关键特性

  • 无输出文件名指定:gcc test_1.c 未通过 -o 选项指定输出文件名,因此 gcc 会使用 默认命名规则 生成可执行文件 a.out
  • 权限自动赋予:编译生成的可执行文件默认继承当前用户的 umask 配置(示例中 umask 为 0002),因此 a.out 权限为 -rwxrwxr-x(775),自动赋予执行权限(x),无需手动 chmod +x
  • 依赖检查:若源文件中使用了未引入的库函数(如未写 #include<stdio.h> 却用 printf),链接阶段会报错,无法生成 a.out

二、a.out 文件的核心解析

a.out 是 gcc 编译 C 源文件后 默认生成的可执行文件 (全称 assembler output,意为 "汇编输出"),是 Unix/Linux 系统的历史默认命名,也是示例中编译后新增的核心文件。

1. 文件属性与生成逻辑(结合示例)

  • 权限与归属:示例中 a.out 权限为 -rwxrwxr-x,拥有者 / 所属组为 ranjiaju------rwx 表示拥有者可读写执行,rwx 表示所属组可读写执行,r-x 表示其他用户可读执行;文件大小 8496 字节(远大于源文件的 156 字节),包含编译后的机器码 + 链接的系统库函数;
  • 生成条件:仅当 gcc test_1.c 编译无错误时才会生成,若源文件有语法错误(如少分号、括号不匹配),gcc 会输出错误信息,不会生成 a.out
  • 执行方式:./a.out 表示执行当前目录下的 a.out 文件 ------Linux 中执行可执行文件需指定路径(./ 代表当前目录),若直接输入 a.out,系统会在 PATH 环境变量指定的目录中查找,而当前目录默认不在 PATH 中,因此会提示 "命令未找到"。

2. 执行结果解析

示例中执行 ./a.out 输出:

复制代码
helloc Linux
100
  • 第一行:对应源文件中 printf("helloc Linux\n");printf 函数通过链接系统库正常执行;
  • 第二行:对应 printf("%d\n", M);,宏 M 已被预处理阶段替换为 100,因此输出 100;
  • 注释行 // printf("helloc Linux\n"); 已被预处理阶段删除,不会执行。

3. 自定义可执行文件名(补充实用用法)

若需替换默认的 a.out 命名,可通过 -o 选项指定,例如:

复制代码
gcc test_1.c -o test  # 编译生成名为 test 的可执行文件
./test                # 执行自定义命名的可执行文件,输出结果与 a.out 一致

三、总结

  • gcc test_1.c:核心是将 C 源文件编译为可执行文件,默认生成 a.out,需确保源文件无语法错误、依赖库完整;
  • a.out:是 gcc 默认生成的可执行文件,具备执行权限,执行后运行编译后的 C 程序,输出对应结果;
  • 核心逻辑:C 代码需经 gcc 编译链接才能从 "文本文件" 转为 "可运行程序",a.out 是这一过程的最终产物,默认命名可通过 -o 自定义。

示例中的操作(编译 → 生成 a.out → 执行)是 Linux 下运行简单 C 程序的标准流程,覆盖了从代码到可执行程序的完整链路。

gcc -E(仅执行预处理步骤)

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ pwd
/home/ranjiaju/test

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 16
-rwxrwxr-x 1 ranjiaju ranjiaju 8496 Dec  4 15:36 a.out
-rw-rw-r-- 1 ranjiaju ranjiaju  156 Dec  4 15:36 test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ cat test_1.c
#include<stdio.h>

#define M 100

int main()
{
    printf("helloc Linux\n");

    // printf("helloc Linux\n");
    
    printf("%d\n", M);

    return 0;
}

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ gcc -E test_1.c -o test_1.i

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 36
-rwxrwxr-x 1 ranjiaju ranjiaju  8496 Dec  4 15:36 a.out
-rw-rw-r-- 1 ranjiaju ranjiaju   156 Dec  4 15:36 test_1.c
-rw-rw-r-- 1 ranjiaju ranjiaju 16914 Dec  4 15:44 test_1.i

一、gcc -E test_1.c -o test_1.i 命令的核心解析

该命令是 gcc 编译流程中 仅执行 "预处理阶段" 的专用指令,-E 选项强制 gcc 只完成预处理步骤后立即停止(不执行编译、汇编、链接),并通过 -o 选项指定预处理后的输出文件名为 test_1.i,是调试预处理指令(如宏定义、头文件引入)的核心命令。

1. 命令各参数拆解

参数 核心含义
gcc GNU C 编译器核心程序,负责驱动编译流程
-E 关键选项:指定 "仅执行预处理步骤,停止后续所有编译流程"(编译、汇编、链接均不执行)
test_1.c 输入文件:待预处理的原始 C 源文件
-o test_1.i 输出控制:-o(output)指定预处理结果的输出文件名为 test_1.i.i 是预处理文件的标准后缀

2. 预处理阶段的核心操作(结合示例源文件)

预处理是 gcc 编译的第一步,-E 选项会对 test_1.c 执行以下关键处理,最终生成 test_1.i

  • 头文件嵌入 :将 #include<stdio.h> 指令替换为 <stdio.h> 头文件的全部内容(包括标准输入输出函数的声明、宏定义、结构体定义等)------ 这是 test_1.i 大小(16914 字节)远大于源文件 test_1.c(156 字节)的核心原因(示例中 ll 输出可验证此大小差异);
  • 宏替换 :将源文件中所有 #define M 100 定义的宏 M,全部替换为字面量 100(源文件中 printf("%d\n", M); 会被替换为 printf("%d\n", 100););
  • 注释删除 :彻底删除源文件中的注释内容(如 // printf("helloc Linux\n"); 这行注释会被完全清除,不会出现在 test_1.i 中);
  • 预处理指令清理 :执行并移除所有预处理指令(如 #include/#define),仅保留处理后的纯代码内容;
  • 空行 / 格式整理:清理预处理过程中产生的冗余空行,保证代码结构可读。

3. 输出文件 test_1.i 的特性

  • 文件类型:纯文本文件(可通过 cat test_1.i 直接查看内容),而非二进制文件;
  • 内容特征:包含预处理后的完整代码(嵌入的头文件内容 + 替换宏后的业务代码),无汇编 / 机器码,保留 C 语言语法结构;
  • 权限属性:示例中 test_1.i 权限为 -rw-rw-r--(无执行权限),因仅为预处理文本,并非可执行程序或目标文件。

4. 与完整编译命令的对比

命令 执行阶段 输出文件 核心差异
gcc test_1.c 预处理 + 编译 + 汇编 + 链接 a.out 生成可执行文件,无中间文件
gcc -E test_1.c -o test_1.i 仅预处理 test_1.i 仅生成预处理文本,停止后续流程

二、该命令的核心使用场景

  1. 调试预处理指令问题 :若源文件中宏替换、头文件引入出错(如找不到头文件、宏定义冲突),执行 -E 可查看预处理后的代码,定位问题(如确认 M 是否被正确替换、stdio.h 是否完整引入);
  2. 分析头文件依赖 :通过 test_1.i 的大小和内容,可清晰看到 <stdio.h> 等头文件包含的具体内容,理解代码的依赖关系;
  3. 验证注释清理效果:确认注释是否被完全删除,避免注释中隐藏的语法问题(虽注释不影响编译,但预处理阶段的清理可辅助排查)。

三、总结

gcc -E test_1.c -o test_1.i 的核心是 "仅执行预处理,输出预处理文本文件",是拆分 gcc 编译流程、调试预处理相关问题的关键指令。示例中生成的 test_1.i 因嵌入了 <stdio.h> 头文件内容,大小远大于源文件,这是预处理阶段最直观的特征。

gcc -S(仅执行编译生成汇编)

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ gcc -S test_1.c -o test_1.s

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 40
-rwxrwxr-x 1 ranjiaju ranjiaju  8496 Dec  4 15:36 a.out
-rw-rw-r-- 1 ranjiaju ranjiaju   156 Dec  4 15:36 test_1.c
-rw-rw-r-- 1 ranjiaju ranjiaju 16914 Dec  4 15:44 test_1.i
-rw-rw-r-- 1 ranjiaju ranjiaju   535 Dec  4 16:23 test_1.s

一、gcc -S test_1.c -o test_1.s 指令的核心解析

该命令是 gcc 编译流程中 执行 "预处理 + 编译阶段" 后停止 的专用指令,-S 选项强制 gcc 完成预处理(与 -E 阶段一致)和编译步骤,生成对应 CPU 架构的汇编语言代码文件,且不执行后续的汇编、链接阶段;-o 选项指定汇编文件的输出名为 test_1.s.s 是汇编语言文件的标准后缀),是分析 C 代码对应汇编实现、调试编译阶段问题的核心指令。

1. 命令各参数拆解

参数 核心含义
gcc GNU C 编译器核心程序,驱动编译流程执行
-S 关键选项:指定 "执行预处理 + 编译阶段,停止在编译完成后"(不执行汇编、链接)
test_1.c 输入文件:待处理的原始 C 源文件
-o test_1.s 输出控制:-o(output)指定编译结果的输出文件名为 test_1.s.s 是汇编语言文件的标准后缀

2. 指令执行的完整流程(预处理 + 编译)

-S 选项会先对 test_1.c 执行与 -E 完全一致的预处理操作(头文件嵌入、宏替换、注释删除等),再执行编译阶段 ,最终生成 test_1.s,两步核心操作如下:

步骤 1:预处理(与 gcc -E 完全一致)

test_1.c 完成:

  • 嵌入 <stdio.h> 头文件核心内容(仅保留编译所需的函数声明,而非全部头文件内容);
  • M 替换为 100,删除 // printf(...) 注释;
  • 清理预处理指令,得到纯 C 中间代码。

步骤 2:编译(核心转换步骤)

将预处理后的 C 中间代码,转换为对应 CPU 架构(示例中服务器为 x86_64 架构)的 汇编语言代码------ 这是编译阶段的核心价值:

  • 把 C 语言的高级语法(如 printf 函数调用、return 0)转换为汇编指令助记符(如 movcallpushret 等);
  • 对代码进行基础优化(如常量折叠、指令重排),但不改变代码逻辑;
  • 生成的汇编代码与硬件架构强相关(x86 架构和 ARM 架构的汇编指令完全不同)。

3. 输出文件 test_1.s 的核心特性(结合示例)

示例中 ll 输出显示 test_1.s 权限为 -rw-rw-r--、大小 535 字节,其关键特性如下:

  • 文件类型:纯文本文件(可通过 cat test_1.s 直接查看内容),非二进制文件;
  • 内容特征:包含对应 CPU 架构的汇编语言指令(人类可读的机器指令助记符),无 C 语言语法、无头文件冗余内容,仅保留程序执行的核心汇编逻辑;
  • 大小特征:535 字节远小于预处理文件 test_1.i(16914 字节)------ 因编译阶段剔除了头文件中未被使用的冗余内容,仅保留与 test_1.c 业务逻辑相关的汇编指令;略大于源文件 test_1.c(156 字节)------ 因 C 代码的每一行逻辑需对应多条汇编指令;
  • 权限属性:无执行权限(-rw-rw-r--),仅为汇编代码文本,需经 gcc -c 汇编为 .o 目标文件后,才能参与链接生成可执行程序。

4. 与 -E 选项的核心对比

指令 执行阶段 输出文件 核心内容 文件大小特征
gcc -E test_1.c -o test_1.i 仅预处理 test_1.i 预处理后的 C 代码 最大(含头文件全量)
gcc -S test_1.c -o test_1.s 预处理 + 编译 test_1.s 汇编语言代码 中等(仅核心汇编指令)

二、该指令的核心使用场景

  1. 调试编译阶段问题 :若 C 代码在编译阶段报错(如语法错误、类型不匹配),可通过查看 test_1.s 确认预处理后的代码是否符合预期,定位编译失败的根源;
  2. 分析代码的汇编实现 :学习 C 语言语句(如 printf、宏替换)对应的汇编指令,理解代码的底层执行逻辑(如函数调用的栈操作、常量的存储方式);
  3. 优化代码性能:对比不同写法的 C 代码生成的汇编指令,选择指令更少、执行效率更高的写法(如减少冗余的内存读写指令)。

三、总结

gcc -S test_1.c -o test_1.s 的核心是 "执行预处理 + 编译,生成汇编语言文件",是拆分 gcc 编译流程、分析 C 代码底层汇编实现的关键指令。示例中生成的 test_1.s 大小为 535 字节,体现了编译阶段 "剔除冗余、保留核心汇编指令" 的特征,与预处理文件 test_1.i 形成明显的大小差异。

gcc -c(仅执行汇编生成机器可识别的二进制)

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ gcc -c test_1.c -o test_1.o

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 44
-rwxrwxr-x 1 ranjiaju ranjiaju  8496 Dec  4 15:36 a.out
-rw-rw-r-- 1 ranjiaju ranjiaju   156 Dec  4 15:36 test_1.c
-rw-rw-r-- 1 ranjiaju ranjiaju 16914 Dec  4 15:44 test_1.i
-rw-rw-r-- 1 ranjiaju ranjiaju  1600 Dec  4 16:33 test_1.o
-rw-rw-r-- 1 ranjiaju ranjiaju   535 Dec  4 16:23 test_1.s

一、gcc -c test_1.c -o test_1.o 命令的核心解析

该命令是 gcc 编译流程中 执行 "预处理 + 编译 + 汇编阶段" 后停止 的专用指令,-c 选项强制 gcc 完成前三步编译流程(不执行最后的链接阶段),并通过 -o 选项指定汇编后的二进制目标文件名为 test_1.o.o 是目标文件的标准后缀),是多文件编译、制作库文件的核心基础指令。

1. 命令各参数拆解

参数 核心含义
gcc GNU C 编译器核心程序,驱动编译流程执行
-c 关键选项:指定 "执行预处理 + 编译 + 汇编阶段,停止在汇编完成后"(不执行链接)
test_1.c 输入文件:待处理的原始 C 源文件
-o test_1.o 输出控制:-o(output)指定汇编结果的输出文件名为 test_1.o.o 是目标文件(Object File)的标准后缀

2. 指令执行的完整流程(预处理 + 编译 + 汇编)

-c 选项会对 test_1.c 依次执行前三步编译操作,最终生成二进制目标文件 test_1.o,三步核心操作如下:

步骤 1:预处理(与 -E/-S 完全一致)

test_1.c 完成头文件嵌入(<stdio.h>)、宏替换(M→100)、注释删除,得到纯 C 中间代码。

步骤 2:编译(与 -S 完全一致)

将预处理后的 C 代码转换为对应 CPU 架构(x86_64)的汇编语言代码(如 movcall 等指令)。

步骤 3:汇编(新增核心步骤)

将编译阶段生成的汇编语言代码(.s 级内容)转换为 二进制机器码------ 这是汇编阶段的核心价值:

  • 把人类可读的汇编助记符(如 push %rbp)转换为 CPU 可识别的二进制指令(如 0x55);
  • 生成的二进制代码与硬件架构强绑定(x86 架构的机器码无法在 ARM 架构上运行);
  • 为代码添加符号表(记录函数 / 变量的内存地址),便于后续链接阶段查找。

3. 输出文件 test_1.o 的核心特性(结合示例)

示例中 ll 输出显示 test_1.o 权限为 -rw-rw-r--、大小 1600 字节,其关键特性如下:

  • 文件类型:二进制文件 (非文本文件,无法通过 cat test_1.o 直接查看可读内容,需用 objdump 等工具反汇编);
  • 权限属性:无执行权限(-rw-rw-r--)------ 因未执行链接阶段,缺少系统库(如 printf 对应的 libc 标准库实现),无法独立运行;
  • 大小特征:1600 字节大于汇编文件 test_1.s(535 字节)------ 二进制机器码比文本汇编指令占用更多字节;远小于预处理文件 test_1.i(16914 字节)------ 汇编阶段剔除了头文件中未被使用的冗余内容,仅保留程序核心逻辑的二进制码;
  • 内容特征:包含 test_1.c 对应的全部二进制机器码,但缺少程序运行所需的 "外部依赖"(如 printf 函数的具体实现),需通过链接阶段补充后才能成为可执行程序。

4. 与 -E/-S 选项的核心对比

指令 执行阶段 输出文件 文件类型 核心内容
gcc -E test_1.c -o test_1.i 仅预处理 test_1.i 文本 预处理后的 C 代码
gcc -S test_1.c -o test_1.s 预处理 + 编译 test_1.s 文本 汇编语言代码
gcc -c test_1.c -o test_1.o 预处理 + 编译 + 汇编 test_1.o 二进制 二进制机器码(目标文件)

二、该指令的核心使用场景

  1. 多文件编译(最核心场景) :当程序由多个 C 源文件(如 test_1.ctest_2.c)组成时,可先通过 gcc -c 分别将每个文件编译为 .o 目标文件,再通过 gcc test_1.o test_2.o -o test 链接为可执行文件 ------ 此方式可大幅提高编译效率(修改其中一个文件时,仅需重新编译该文件的 .o,无需重新编译所有文件);
  2. 制作库文件 :将多个 .o 目标文件打包为静态库(.a)或动态库(.so),供其他程序调用(如封装通用函数为库,避免重复编译);
  3. 调试汇编阶段问题 :若汇编阶段报错(如汇编指令不兼容、符号表冲突),可通过检查 test_1.o 是否生成,定位汇编阶段的错误根源;
  4. 分步验证编译流程:拆分编译步骤,逐一确认预处理、编译、汇编阶段均无错误,再执行链接,便于精准定位编译失败原因。

三、总结

gcc -c test_1.c -o test_1.o 的核心是 "执行前三步编译流程,生成二进制目标文件",是 gcc 编译中 "拆分编译 - 链接" 的关键步骤。示例中生成的 test_1.o 是二进制文件,具备程序核心逻辑的机器码,但因缺少库依赖无法直接执行,需后续链接阶段补充系统库后,才能成为可运行的 a.out 类文件。


安装C语言静态库的指令

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ sudo yum install -y glibc-static

安装C++静态库的指令

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ sudo yum install -y libstdc++-static

make 指令 和 makefile 文件

简单使用 make 和 makefile

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ pwd
/home/ranjiaju/test

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 8
drwxrwxr-x 2 ranjiaju ranjiaju 4096 Dec  5 16:51 makefile
-rw-rw-r-- 1 ranjiaju ranjiaju  194 Dec  5 16:54 test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ cat test_1.c
#include<stdio.h>

int main()
{
    printf("hello Linux\n");
    printf("hello Linux\n");
    printf("hello Linux\n");
    printf("hello Linux\n");
    printf("hello Linux\n");

    return 0;
}

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ cat makefile
test_1.out:test_1.o
	gcc test_1.o -o test_1.out
test_1.o:test_1.s
	gcc -c test_1.s -o test_1.o
test_1.s:test_1.i
	gcc -S test_1.i -o test_1.s
test_1.i:test_1.c
	gcc -E test_1.c -o test_1.i
clean:
	rm -rf test_1.i test_1.s test_1.o test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make test_1.out
gcc -E test_1.c -o test_1.i
gcc -S test_1.i -o test_1.s
gcc -c test_1.s -o test_1.o
gcc test_1.o -o test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 48
-rw-rw-r-- 1 ranjiaju ranjiaju   243 Dec  5 17:12 makefile
-rw-rw-r-- 1 ranjiaju ranjiaju   194 Dec  5 16:54 test_1.c
-rw-rw-r-- 1 ranjiaju ranjiaju 16999 Dec  5 17:13 test_1.i
-rw-rw-r-- 1 ranjiaju ranjiaju  1728 Dec  5 17:13 test_1.o
-rwxrwxr-x 1 ranjiaju ranjiaju  8480 Dec  5 17:13 test_1.out
-rw-rw-r-- 1 ranjiaju ranjiaju   565 Dec  5 17:13 test_1.s

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ./test_1.out
hello Linux
hello Linux
hello Linux
hello Linux
hello Linux

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make clean
rm -rf test_1.i test_1.s test_1.o test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 8
-rw-rw-r-- 1 ranjiaju ranjiaju 243 Dec  5 17:12 makefile
-rw-rw-r-- 1 ranjiaju ranjiaju 194 Dec  5 16:54 test_1.c

一、makefile 文件与 make 指令的核心关联

make 是 Linux 下的 自动化构建工具 ,核心作用是根据 makefile(或 Makefile)文件中定义的 "目标 - 依赖 - 命令" 规则,自动执行编译、清理等命令序列;makefile 是构建规则的配置文件,相当于 make 工具的 "执行手册"------make 工具的所有操作都依赖 makefile 中的规则定义,两者配合可替代手动逐行执行 gcc 等编译命令,实现一键构建 / 清理,是多文件、复杂项目编译的核心工具。

二、makefile 文件内部的指令解析

makefile 的核心语法是 "规则",每个规则对应一个构建目标,格式为:

复制代码
目标(target):依赖(dependencies)
    命令(command)  # 关键:命令行必须以 Tab 缩进(不能用空格)

示例中的 makefile 定义了 5 个规则(4 个编译规则 + 1 个清理规则),逐行解析如下:

规则行 解析说明
test_1.out:test_1.o 「最终目标规则」- 目标:test_1.out(要生成的可执行文件)- 依赖:test_1.o(生成该目标需先有 test_1.o 目标文件)
gcc test_1.o -o test_1.out 生成 test_1.out 的命令:将 test_1.o 链接为可执行文件 test_1.out
test_1.o:test_1.s 「目标文件规则」- 目标:test_1.o(二进制目标文件)- 依赖:test_1.s(生成该目标需先有 test_1.s 汇编文件)
gcc -c test_1.s -o test_1.o 生成 test_1.o 的命令:将 test_1.s 汇编为二进制目标文件
test_1.s:test_1.i 「汇编文件规则」- 目标:test_1.s(汇编语言文件)- 依赖:test_1.i(生成该目标需先有 test_1.i 预处理文件)
gcc -S test_1.i -o test_1.s 生成 test_1.s 的命令:将 test_1.i 编译为汇编文件
test_1.i:test_1.c 「预处理文件规则」- 目标:test_1.i(预处理文件)- 依赖:test_1.c(最底层依赖,原始 C 源文件)
gcc -E test_1.c -o test_1.i 生成 test_1.i 的命令:对 test_1.c 仅做预处理生成 test_1.i
clean: 「清理伪目标规则」- 目标:clean(伪目标,无依赖)- 依赖:无(无需前置文件)
rm -rf test_1.i test_1.s test_1.o test_1.out 清理命令:删除所有编译生成的中间文件(test_1.i/test_1.s/test_1.o)和可执行文件(test_1.out

三、make test_1.out 指令解析

make test_1.out 是告诉 make 工具:test_1.out 为最终目标,执行 makefile 中对应的规则链,核心执行逻辑如下:

  1. 依赖检查make 先检查 test_1.out 的依赖 test_1.o 是否存在 → 不存在,检查 test_1.o 的依赖 test_1.s → 不存在,检查 test_1.s 的依赖 test_1.i → 不存在,检查 test_1.i 的依赖 test_1.c → 存在(核心源文件);

  2. 规则链执行 :从最底层依赖(test_1.c)开始,按 "预处理→编译→汇编→链接" 的顺序执行规则命令:

    复制代码
    gcc -E test_1.c -o test_1.i (生成预处理文件)
    gcc -S test_1.i -o test_1.s (生成汇编文件)
    gcc -c test_1.s -o test_1.o (生成目标文件)
    gcc test_1.o -o test_1.out (生成可执行文件)
  3. 效率特性 :若 test_1.c 未修改,再次执行 make test_1.outmake 会提示 test_1.out is up to date,不会重复编译(仅重新构建 "比依赖文件新" 的目标)。

四、make clean 指令解析

make clean 是告诉 make 工具:执行 makefileclean 目标对应的命令,核心逻辑如下:

  1. 伪目标特性clean 是 "伪目标"(无依赖),因此 make 不会检查任何文件,直接强制执行其下的 rm 命令;
  2. 执行效果 :删除所有编译生成的中间文件(test_1.itest_1.stest_1.o)和可执行文件(test_1.out),仅保留原始源文件(test_1.c)和 makefile,恢复到编译前的状态;
  3. 注意事项clean 无依赖,因此每次执行 make clean 都会强制执行 rm 命令,即使目标文件已不存在,也不会报错。

五、核心补充细节

  1. Tab 缩进要求makefile 中命令行必须以 Tab 开头(不能用空格),否则 make 会报错 "missing separator";

  2. 依赖链自动解析make 会自动反向解析目标的所有依赖,无需手动指定执行顺序,这是其 "自动化" 的核心;

  3. 伪目标优化 :示例中 clean 未声明为 "伪目标",若目录中存在名为 clean 的文件,make clean 会提示 "clean is up to date" 而不执行删除。规范写法需在 makefile 开头添加:

    复制代码
    .PHONY: clean  # 标记 clean 为伪目标,强制执行命令

总结

makefile 定义了编译 / 清理的规则(目标、依赖、命令),make 工具根据指定的目标(如 test_1.outclean),自动解析规则链并执行对应命令:

  • make test_1.out:一键完成从 test_1.ctest_1.out 的全流程编译;
  • make clean:一键清理所有编译产物;两者配合替代了手动执行多条 gcc/rm 命令,大幅提升项目构建效率,是 Linux 下 C/C++ 项目编译的标准方式。

重复执行 make 指令和 touch 更新文件时间

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ pwd
/home/ranjiaju/test

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 8
-rw-rw-r-- 1 ranjiaju ranjiaju  75 Dec  5 19:47 makefile
-rw-rw-r-- 1 ranjiaju ranjiaju 194 Dec  5 20:31 test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ cat test_1.c
#include<stdio.h>

int main()
{
    printf("hello Linux\n");
    printf("hello Linux\n");
    printf("hello Linux\n");
    printf("hello Linux\n");
    printf("hello Linux\n");

    return 0;
}

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ cat makefile
test_1.out:test_1.c
	gcc test_1.c -o test_1.out
clean:
	rm -rf test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
gcc test_1.c -o test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 20
-rw-rw-r-- 1 ranjiaju ranjiaju   75 Dec  5 19:47 makefile
-rw-rw-r-- 1 ranjiaju ranjiaju  194 Dec  5 20:31 test_1.c
-rwxrwxr-x 1 ranjiaju ranjiaju 8480 Dec  5 20:34 test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ./test_1.out
hello Linux
hello Linux
hello Linux
hello Linux
hello Linux

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make clean
rm -rf test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 8
-rw-rw-r-- 1 ranjiaju ranjiaju  75 Dec  5 19:47 makefile
-rw-rw-r-- 1 ranjiaju ranjiaju 194 Dec  5 20:31 test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
gcc test_1.c -o test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
make: `test_1.out' is up to date.

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ touch test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
gcc test_1.c -o test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ touch test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
gcc test_1.c -o test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ stat test_1.c
  File: 'test_1.c'
  Size: 194       	Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d	Inode: 1055023     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1001/ranjiaju)   Gid: ( 1001/ranjiaju)
Access: 2025-12-05 20:39:46.661053395 +0800
Modify: 2025-12-05 20:39:44.746977002 +0800
Change: 2025-12-05 20:39:44.746977002 +0800
 Birth: -

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ touch test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ stat test_1.c
  File: 'test_1.c'
  Size: 194       	Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d	Inode: 1055023     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1001/ranjiaju)   Gid: ( 1001/ranjiaju)
Access: 2025-12-05 20:42:19.227142514 +0800
Modify: 2025-12-05 20:42:19.227142514 +0800
Change: 2025-12-05 20:42:19.227142514 +0800
 Birth: -

一、make 指令的核心解析

make 是 Linux 下的 自动化构建工具 ,核心逻辑是读取当前目录的 makefile(或 Makefile)文件,按 "目标 - 依赖 - 命令" 规则实现增量构建 ------ 仅当 "依赖文件的修改时间晚于目标文件" 时,才重新执行编译命令,避免重复构建,提升效率。结合示例的执行逻辑拆解如下:

1. 核心执行规则

  • 无指定目标时 :make 默认执行 makefile 中第一个规则的目标(示例中第一个规则是 test_1.out:test_1.c,因此直接执行 make 等效于 make test_1.out);
  • 增量构建特性 :make 会对比 "目标文件(test_1.out)" 和 "依赖文件(test_1.c)" 的修改时间(Modify 时间):
    • 若目标文件不存在 → 执行规则命令(示例中首次 make 时无 test_1.out,执行 gcc test_1.c -o test_1.out);
    • 若目标文件存在且修改时间晚于依赖文件 → 提示 make: 'test_1.out' is up to date.(无需重新编译);
    • 若依赖文件修改时间更新(如执行 touch test_1.c)→ 重新执行编译命令。

2. 示例中 make 指令的执行表现

操作场景 make 执行结果 核心原因
首次执行 make 执行 gcc test_1.c -o test_1.out test_1.out 不存在,触发规则执行
再次执行 make 提示 test_1.out is up to date. test_1.out 修改时间晚于 test_1.c,无需重新编译
touch test_1.c 后执行 make 重新执行 gcc test_1.c -o test_1.out touch 更新了 test_1.c 的修改时间,使其晚于 test_1.out,触发重新编译

二、touch test_1.c 指令的核心解析

touch 是 Linux 中 修改文件时间属性(或创建空文件) 的基础命令,核心作用分两种场景:

  1. 文件已存在时 :更新文件的三类时间属性(示例中 test_1.c 已存在,执行 touch 后触发此逻辑):
    • Access 时间(访问时间):文件内容被读取的最后时间;
    • Modify 时间(修改时间):文件内容被修改的最后时间(make 判定是否重新编译的核心依据);
    • Change 时间(状态改变时间):文件元数据(权限、属主、inode 等)被修改的最后时间;
  2. 文件不存在时 :创建一个空文件(如示例中最初 touch test_1.c 用于创建空的 C 源文件)。

示例中 touch test_1.c 的执行效果

执行 touch test_1.c 后,通过 stat test_1.c 可验证:

  • Modify 时间从 2025-12-05 20:39:44 更新为 2025-12-05 20:42:19
  • AccessChange 时间同步更新为当前执行 touch 的时间;
  • 文件大小、权限、属主等元数据无变化(仅时间属性修改)。此操作的核心目的是 "欺骗" make 工具 ------ 让 test_1.c 的修改时间晚于 test_1.out,触发 make 重新编译(即使 test_1.c 内容未实际修改)。

三、stat test_1.c 指令的核心解析

stat 是 Linux 中 查看文件完整属性 的命令,可输出文件的元数据、时间属性、存储属性等所有核心信息,是比 llls -l)更详细的文件信息查询工具。示例中 stat test_1.c 的输出字段解析如下:

输出字段 核心含义
File: 'test_1.c' 文件名称
Size: 194 文件大小(字节),示例中 test_1.c 内容为 194 字节
Blocks: 8 文件占用的磁盘块数(每块默认 512 字节,8 块对应 4096 字节,即 1 个扇区)
IO Block: 4096 文件系统的 IO 块大小(4KB)
Device: fd01h/64769d 文件所在的设备号(十六进制 / 十进制)
Inode: 1055023 文件的 inode 编号(文件系统中唯一标识文件的 ID)
Links: 1 文件的硬链接数(示例中仅 1 个硬链接)
Access: (0664/-rw-rw-r--) 文件权限(数字 0664 对应符号 -rw-rw-r--,属主 / 属组可读可写,其他用户只读)
Uid: ( 1001/ranjiaju) 文件属主的 UID(1001)和用户名(ranjiaju)
Gid: ( 1001/ranjiaju) 文件属组的 GID(1001)和组名(ranjiaju)
Access: 2025-12-05 20:42:19 文件最后访问时间(由 touch 更新)
Modify: 2025-12-05 20:42:19 文件最后内容修改时间(make 判定编译的核心依据)
Change: 2025-12-05 20:42:19 文件最后元数据修改时间(与 touch 操作同步)
Birth: - 文件创建时间(部分文件系统不支持,显示为 -

核心作用

示例中执行 stat test_1.c 是为了验证 touch 指令对文件时间属性的修改效果,同时理解 make 工具 "增量构建" 的底层依据 ------仅对比 Modify 时间,而非文件内容是否真的修改

四、三者的关联逻辑

示例中 touch test_1.cstat test_1.cmake 的操作链路,清晰体现了 Linux 构建工具的核心逻辑:

  1. touch 修改 test_1.c 的 Modify 时间;
  2. stat 验证时间已更新;
  3. make 检测到 test_1.c 的 Modify 时间晚于 test_1.out,触发重新编译。

这一链路也解释了 make 工具 "增量构建" 的本质:基于文件时间戳的对比,而非内容对比,既提升构建效率,也允许通过 touch 手动触发重新编译(即使内容未改)。

伪目标:.PHONY

复制代码
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ ll
total 8
-rw-rw-r-- 1 ranjiaju ranjiaju  94 Dec  5 20:58 makefile
-rw-rw-r-- 1 ranjiaju ranjiaju 194 Dec  5 20:42 test_1.c

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ cat makefile
.PHONY:test_1.out
test_1.out:test_1.c
	gcc test_1.c -o test_1.out
clean:
	rm -rf test_1.out

[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
gcc test_1.c -o test_1.out
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
gcc test_1.c -o test_1.out
[ranjiaju@iZ2vc15k23y9vpuyi3tiqzZ test]$ make
gcc test_1.c -o test_1.out

一、.PHONY:test_1.out 的核心解析

.PHONY 是 makefile 中的 伪目标声明指令 ,用于标记指定的目标(如 test_1.out)为 "伪目标"(Phony Target)------ 伪目标的核心特征是 "不对应实际文件",make 工具执行该目标时,会忽略目录中是否存在同名文件、忽略文件时间戳对比,强制执行目标对应的命令,而非遵循默认的 "增量构建" 逻辑。

二、伪目标的核心特性(结合示例)

1. 无 .PHONY 时的默认行为(回顾前文场景)

此前未声明 .PHONY:test_1.out 时,make 会将 test_1.out 视为 "文件目标":

  • 检查目录中是否存在 test_1.out 文件;
  • 对比 test_1.out 和依赖文件 test_1.c 的修改时间;
  • test_1.out 存在且修改时间更新,会提示 test_1.out is up to date.,不执行编译命令。

2. 声明 .PHONY:test_1.out 后的行为(示例场景)

示例中添加 .PHONY:test_1.out 后,多次执行 make 均输出 gcc test_1.c -o test_1.out,核心原因是:

  • test_1.out 被标记为伪目标,make 不再检查是否存在 test_1.out 文件,也不对比时间戳;
  • 无论 test_1.c 是否修改、test_1.out 是否存在,每次执行 make(默认执行第一个目标 test_1.out)都会强制执行 gcc test_1.c -o test_1.out 命令,这也是示例中三次执行 make 都重复编译的关键。

三、伪目标的核心使用场景

.PHONY 声明伪目标的核心价值是 "强制执行命令",常见场景包括:

  1. 需要强制重新编译的场景 :即使目标文件已存在且未过期,也需每次重新编译(如示例中 test_1.out 的场景);

  2. 避免与同名文件冲突 :若目录中意外创建了名为 clean/test_1.out 的文件,未声明伪目标时 make clean/make test_1.out 会提示 "up to date" 而不执行命令,声明 .PHONY 可规避此问题;

  3. 批量声明伪目标 :可同时声明多个伪目标,用空格分隔,例如:

    makefile

    复制代码
    .PHONY:test_1.out clean  # 同时标记 test_1.out 和 clean 为伪目标

四、关键补充细节

  1. 语法要求:.PHONY 必须以点开头,冒号后紧跟目标名(无空格),多个目标名用空格分隔;
  2. 执行优先级:伪目标声明需写在对应目标规则之前(示例中 .PHONY:test_1.out 写在 test_1.out:test_1.c 之前,符合规范);
  3. 对比 clean 目标:示例中 clean 未声明为伪目标,若目录中创建了名为 clean 的文件,执行 make clean 会提示 "up to date" 而不删除 test_1.out;规范写法应补充 .PHONY:clean,确保 make clean 始终强制执行删除命令。

总结

.PHONY:test_1.out 的核心作用是将 test_1.out 标记为伪目标,让 make 工具忽略文件存在性和时间戳对比,每次执行该目标时都强制编译 test_1.c 生成 test_1.out------ 这与 make 默认的 "增量构建" 逻辑相反,适用于需要强制重新编译的场景。

相关推荐
爱跑马的程序员2 小时前
Kernel i2c 设备驱动详细讲解
linux·安卓·内核驱动
love混世_魔王3 小时前
VIM经典命令系列之数字递增、递减
linux·编辑器·vim·verilog vim插件·vim使用技巧·vim高效编程
PAQQ3 小时前
ubuntu 22.04 更新cmake版本
linux·运维·ubuntu
陈苏同学3 小时前
[ 已解决 ] Cursor ssh 打开文件夹目录后,聊天框不能 chat 卡住
linux·服务器·ssh
代码游侠3 小时前
应用——文件I/O操作代码
linux·运维·c语言·笔记·学习·算法
Hui Baby3 小时前
Python Flask 多文件项目打包部署(Linux+Docker+Windows 全环境)
linux·python·flask
物随心转3 小时前
Arm的协处理器
linux·arm开发
小婷要努力3 小时前
Linux常用命令速查表
linux·运维·服务器
微风◝3 小时前
AlmaLinux9-网络管理篇-更改网络接口名称
linux·运维·网络