# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
# uname -r
3.10.0-1160.el7.x86_64
vim /usr/src/kernels/3.10.0-1160.el7.x86_64/Makefile
KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
-Wno-format-security \
-fno-delete-null-pointer-checks \
-std=gnu89
此选项在编译过程中启用了一组全面的警告信息。它有助于检测潜在问题和可疑的代码结构。
This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros.
GCC 编译器选项 -Wall 是一个常用的编译选项,用于启用大部分警告信息。它表示 “开启所有警告”,让编译器尽可能发现代码中的潜在问题,并生成相应的警告信息。
使用 -Wall 选项可以帮助开发人员改善代码质量、发现潜在的错误和不良实践。编译器会检查代码中的语法错误、类型不匹配、未使用的变量、潜在的隐患等,并生成相应的警告信息。
启用此选项时,编译器会在遇到未定义的宏时生成警告。它有助于识别使用未定义宏可能导致的问题。
Warn if an undefined identifier is evaluated in an #if directive.
GCC 编译器选项 -Wundef 用于启用对未定义宏的警告。当代码中使用了未定义的宏时,编译器会发出警告,以帮助开发人员发现潜在的问题。
使用未定义的宏可能会导致代码中的条件判断、预处理指令等产生意外的行为。编译器的警告可以帮助开发人员及时发现这些问题,避免潜在的错误。
此选项强制执行严格的函数原型检查。它确保函数声明包括参数类型,有助于检测函数声明和定义之间的不匹配。
GCC 编译器选项 -Wstrict-prototypes 是用于启用严格的函数原型检查的警告。当使用该选项编译代码时,编译器会检查函数声明中是否包含参数类型,并警告未声明参数类型的函数声明。
函数原型是函数的声明,其中包含函数的名称、返回类型和参数列表。在早期版本的 C 语言中,可以省略函数声明中的参数类型,这被称为隐式函数声明。然而,根据 ANSI C 和后续的 C 标准,函数原型是强制要求包含参数类型的。
使用 -Wstrict-prototypes 编译选项可以帮助检测出以下情况:
函数声明中缺少参数类型的情况。
函数声明与函数定义之间参数类型不匹配的情况。
通过启用严格的函数原型检查,可以提高代码的可读性和可靠性。它有助于捕获潜在的函数声明错误,避免因函数参数类型不匹配而引发的错误和行为不确定性。
然而,需要注意的是,并非所有的代码都需要启用 -Wstrict-prototypes 选项。在某些情况下,旧代码或特定的编译环境可能仍然使用隐式函数声明。因此,使用该选项时需要权衡考虑,并确保代码与所需的标准和规范相符。
三字符序列是在 C 语言中可以用于替代某些字符的序列。此选项禁用与三字符序列相关的警告。
GCC 编译器选项 -Wno-trigraphs 用于禁用关于三字符序列的警告。在 C 语言中,三字符序列是由两个问号(“??”)后跟一个字符组成的序列,用于代替某些特定字符。
例如,“??=” 会被解释为 “#”,“??/” 会被解释为 “”,“??'” 会被解释为 “^”,等等。这种替换在早期的编译器中用于处理不支持特定字符的硬件和字符集限制。
然而,三字符序列的使用在现代的编程中变得相对较少,因为现代的编译器和编码环境已经具备支持更广泛字符集的能力。因此,GCC 编译器默认会发出关于三字符序列的警告,以提醒开发者可能出现的代码可读性和易于理解的问题。
通过使用 -Wno-trigraphs 选项,可以禁用这些警告信息,使编译器在遇到三字符序列时不会发出警告。这在某些情况下可能是有用的,特别是当代码中故意使用三字符序列进行特定目的的替换时。
然而,需要谨慎使用 -Wno-trigraphs,因为过度使用三字符序列可能会导致代码的可读性下降,并且可能在不同编译环境中产生不一致的结果。在现代的编程实践中,更推荐使用标准字符来代替三字符序列,以提高代码的可读性和可移植性。
(1)
严格别名规则是编译器中的一种优化技术。此选项禁用了严格别名规则,可以避免与别名违规相关的潜在问题。
GCC 编译器选项 -fno-strict-aliasing 用于禁用严格别名规则的优化。严格别名规则是编译器在进行优化时采用的一种策略,用于假设不同类型的指针不会指向同一个内存位置。
严格别名规则的优化假设了不同类型的指针不会引用同一个内存位置,这样编译器可以进行更有效的优化。然而,这种优化可能会导致别名违规(aliasing violation)的问题。别名违规指的是使用一个指针类型的指针来访问被另一个类型的指针所引用的对象,这可能导致未定义的行为。
通过使用 -fno-strict-aliasing 选项,可以禁用严格别名规则的优化。这样,编译器将不再假设不同类型的指针不会指向同一个内存位置,从而避免别名违规问题。禁用严格别名规则的优化可能会导致一些性能上的损失,但可以确保代码的正确性和可靠性。
需要注意的是,在某些情况下,严格别名规则的优化是有益的,可以提高代码的执行效率。因此,在使用 -fno-strict-aliasing 选项时,需要仔细权衡优化性能和代码正确性之间的取舍,并确保禁用严格别名规则的优化不会引入潜在的别名违规问题。
有关严格别名请参考:https://zhuanlan.zhihu.com/p/595286568
(2)
对应的 -fstrict-aliasing 编译选项:
The -fstrict-aliasing option is enabled at levels -O2, -O3, -Os.
GCC 编译器选项 -fstrict-aliasing 用于启用严格别名规则的优化。默认情况下,GCC 编译器会启用严格别名规则的优化,以假设不同类型的指针不会指向同一个内存位置,从而进行更有效的优化。特别是,假设一种类型的对象永远不会与不同类型的对象位于同一地址,除非类型几乎相同。例如,“unsigned int”可以别名为“int”,但不能别名为“void*”或“double”。字符类型可以别名为任何其他类型。
严格别名规则的优化假设了以下内容:
不同类型的指针不会指向同一个内存位置。
对于同一个内存位置,不同类型的指针进行读取或写入操作不会相互影响。
基于这些假设,编译器可以进行一些优化,如寄存器的使用、指令重排、常量传播等,以提高代码的性能。
然而,使用严格别名规则的优化可能导致别名违规(aliasing violation)问题。别名违规指的是使用一个指针类型的指针来访问被另一个类型的指针所引用的对象,这可能导致未定义的行为。
通过使用 -fstrict-aliasing 选项,可以显式启用严格别名规则的优化。这样,编译器将根据严格别名规则进行优化,假设不同类型的指针不会指向同一个内存位置。启用严格别名规则的优化可能会提高代码的执行效率,但需要确保代码中不存在别名违规的问题。
需要注意的是,使用 -fstrict-aliasing 选项时,必须遵守 C 语言的别名规则,以确保代码的正确性。同时,也要注意在某些情况下,严格别名规则的优化可能会导致意外的行为,特别是在涉及类型转换、联合体等复杂语法和编码技巧时。因此,使用该选项时需要仔细考虑,并进行适当的测试和验证,确保代码正确性和可靠性。
默认情况下,未初始化的全局变量可以放置在内存的共享段中,从而可以在多个目标文件之间共享。该选项禁用了这种行为,将未初始化的全局变量视为带有 “extern” 关键字定义的变量。
GCC 编译器选项 -fno-common 用于禁止将未初始化的全局变量和静态变量放置在共享的内存块中。默认情况下,GCC 允许将多个编译单元中的未初始化的全局变量和静态变量合并到一个共享的内存块中,这称为 “common” 块。
使用 -fno-common 选项可以禁用这种行为,每个未初始化的全局变量和静态变量将被分配独立的内存空间。这样做可以避免在链接时可能出现的多个对象具有相同符号的冲突问题。
需要注意的是,使用 -fno-common 选项可能会导致一些副作用:
可能会增加可执行文件的大小,因为每个未初始化的全局变量和静态变量都会分配独立的内存空间。
可能会导致一些与共享内存相关的优化失效,因为每个变量都有自己的内存空间。
启用此选项时,隐式函数声明将被视为错误,要求所有函数都必须显式声明。它有助于检测缺少函数声明的情况。
GCC 编译器选项 -Werror-implicit-function-declaration 用于将隐式函数声明警告视为错误。默认情况下,GCC 编译器会发出警告,提示在使用函数之前未进行显式声明或包含相关的函数头文件。然而,使用 -Werror-implicit-function-declaration 选项可以将这些警告视为编译错误,导致编译过程中止。
隐式函数声明是指在使用函数之前没有显式提供函数原型或包含相关的函数头文件,从而导致编译器无法确定函数的参数和返回类型。这可能会导致潜在的问题,例如函数参数类型不匹配、返回类型不正确等。
通过使用 -Werror-implicit-function-declaration 选项,可以确保在编译过程中任何隐式函数声明都被视为错误,从而强制要求显式声明函数或包含相关的函数头文件。
此选项禁用与格式化字符串安全性相关的警告。格式化字符串安全性漏洞可能在未正确控制函数(如 printf)中的格式说明符时出现。
GCC 编译器选项 -Wno-format-security 用于禁用与格式化字符串安全相关的警告。默认情况下,GCC 编译器会发出与格式化字符串函数(如 printf、sprintf 等)相关的安全性警告,以帮助检测潜在的格式化字符串漏洞。
格式化字符串漏洞是指在使用格式化字符串函数时,传递的格式化字符串与实际参数不匹配,可能导致内存溢出、信息泄露、远程代码执行等安全问题。编译器的警告可以帮助开发人员发现这些潜在的问题,并采取相应的防护措施。
通过使用 -Wno-format-security 选项,可以禁用这些与格式化字符串安全相关的警告。这在某些情况下可能是有意义的,例如在特定的代码库中已经经过充分的安全审查,或者在编写一些特殊目的的代码时,需要使用一些非标准的格式化字符串技巧。
需要注意的是,禁用格式化字符串安全警告可能会增加代码的潜在风险。因此,在使用 -Wno-format-security 选项时,开发人员需要特别小心,并确保代码中没有潜在的格式化字符串漏洞。
此选项禁用删除空指针检查的优化。它确保始终执行空指针检查,这对于调试和提高代码安全性很有用。
GCC 编译器选项 -fno-delete-null-pointer-checks 用于禁用删除空指针检查的优化。默认情况下,GCC 编译器允许对空指针的删除操作进行优化,因为在 C 标准中,对空指针进行删除操作是无效的。
通过使用 -fno-delete-null-pointer-checks 选项,可以禁用这种优化,确保空指针的删除操作被保留在生成的代码中。这样做的目的是为了在程序中明确地标记出对空指针的删除操作,以便在调试和错误排查时能够更容易地定位和识别相关的问题。
需要注意的是,对空指针进行删除操作是一种未定义行为,因此程序中应该避免这样的操作。然而,禁用删除空指针检查的优化可以帮助开发人员在调试和测试阶段更容易地发现和修复这类问题。
此选项将 C 语言标准设置为 GNU C89。它指定了编译器在编译过程中应遵循的 C 语言版本。
GCC 编译器选项 -std=gnu89 用于指定 C 语言的标准版本为 GNU C89 标准(GNU 扩展的 C89 标准)。这个选项告诉编译器按照 GNU C89 标准的语法和语义来解析和编译 C 代码。
GNU C89 是对 ANSI C89 标准的扩展,提供了一些额外的功能和特性。使用 -std=gnu89 选项可以启用这些扩展功能,以便编译使用了 GNU C89 扩展特性的代码。