概述
C语言消除编译警告
特此说明: 内容主要参考魏永明老师老师的课程 C 语言最佳实践之清除所有编译警告,也可以看下面我整理的笔记。建议初级C程序员多看,干货多水分少,质量很高。
1 说明背景
1、实验环境
遵循C99规范,使用GCC编译器,源码路径:https://github.com/VincentWei/best-practices-of-c
$tree best-practices-of-c/src/foobar/warnings
.
├── bin2c.c
├── bugs-not-warned.c # 代码缺陷,不会被编译器警告
├── format.c # 格式化相关警告
├── lexical.c # 词法警告
├── others.c # 其他警告
├── preprocessor.c # 预处理警告
├── typesafe.c # 类型安全警告
├── uninitialized.c # 未初始化警告
└── unused.c # 未使用警告
2、为什么不能忽视编译警告
- 大部分编译警告意味着代码存在缺陷或者问题
- 充斥大量编译警告的代码,通常意味着代码质量不高
- 破窗心理会拉低团队的平均水平
3、什么是编译警告
消除编译警告是写’好代码’的第一步,对代码初步静态分析
4、如何使能编译警告
# file: best-practices-of-c/src/cmake/GlobalCompilerFlags.cmake
# Don't give -Wall to clang-cl because clang-cl treats /Wall and -Wall as -Weverything.
# -Wall and -Wextra should be specified before -Wno-* for Clang.
FOOBAR_PREPEND_GLOBAL_COMPILER_FLAGS(-Wall -Wextra -Wconversion -Wfloat-equal -Wformat -Wpacked -fmax-errors=10)
set(CMAKE_C_FLAGS "-Werror ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "-Werror ${CMAKE_CXX_FLAGS}")
2 编译警告分类
2.1 预处理警告
-Wunused-macros # 未使用的宏
-Wundef # 使用未定义的宏
-Wtringraphs # 无法识别的三联符
2.2 未使用警告
-Wunused-label # 未使用的标签
-Wunused-variable # 未使用的变量
-Wunsed-but-set-parameter # 未使用但被设置的参数
-Wunsed-but-set-variable # 未使用但被设置的变量
-Wunused-local-typedefs # 未使用局部类型定义
-Wunused-parameter # 未使用的参数
-Wunused-value # 未使用的值
-Wunused-function # 未使用的函数
-Wunused-result # 未使用的结果
2.3 未初始化警告
-Wuninitialized # 未初始化的
-Wmaybe-uninitialized # 可能未初始化的
-Winit-self # 初始化自己
2.4 类型安全警告
## 强制转换相关
-Wcast-qual # 强制转换丢失限定符
-Wcast-align # 强制转换导致对齐单位增加
-Wcast-function-type # 强制转换函数指针到不兼容的(参数类型及返回值)函数指针
-Wdiscarded-qualifiers # 丢失限定符
-Wint-to-pointer-cast # 整数强制转换为指针
-Wpointer-to-int-cast # 指针强制转换为整数
-Wbad-function-cast # 强制转换函数返回类型为不匹配的类型
## 符号相关
-Wsign-compare # 对比有符号和无符号值时,由于隐含的符号转换会导致不正确的结果
-Wpointer-sign # 使用带符号的值赋值给指针或者传递给指针参数
## 隐含类型转换相关
-Wconversion # 当隐式转换可能会改变值时
-Wsign-conversion # 隐含的转换会改变整数的符号
-Wfloat-conversion # 隐含的转换会丢失实数的精度
-Wenum-conversion # 一个枚举类型的值被隐含的转换为另一个枚举类型值
-Warith-conversion #混合不同类型运算时产生警告
-Wincompatible-pointer-types # 不兼容指针类型
## 逻辑运算相关
-Wbool-compare # 当布尔表达式与不同于true/false的整数值相比较时
-Wbool-oeration # 在布尔类型表达式上存在可疑运算时
-Wlogical-op # 奇怪的逻辑运算
-Wlogical-not-parentheses # 在比较的左侧操作数上使用了逻辑'非'操作
### 其他
-Wfloat-equal # 如果在相等比较中使用浮点值,则发出警告
-Wpointer-arith # 警告任何依赖于函数类型或void之'大小'的运算
-Wtype-limits # 由于数据类型有限范围,当比较始终为真或者始终为假时,则发出警告,但不对常量表达式发出警告
2.5 格式化相关警告
-Wformat # 如下描述
# case0: 当提供给printf、scanf等格式化函数的参数和格式化字符串不匹配时
# case1: 当格式化字符串长度为零时 => 使用 -Wno-format-zero-length 关闭
# case2: 当格式化字符串包含NUL字节时 => 使用 -Wno-format-contains-nul 关闭
# case3: 当提供给pringf或scanf函数的参数超过格式化字符串需要的参数个数时 => 使用 -Wno-format-extra-args 关闭
-Wformat-overflow # 当提供给sprintf和vsprintf等格式化函数的缓冲区大小有溢出风险时
-Wformat-security # 存在可能的格式化安全问题,尤其是格式化字符串由外部传入时
-Wformat-signedness # 格式化字符串要求的符号不匹配
-Wformat-truncation # 对snprintf和vsnprintf的调用可能导致输出被截断
2.6 词法警告
## 函数相关
-Wstrict-prototypes # 严格的函数原型
-Wmissing-prototypes # 在定义全局函数之前,未见其原型声明
-Wmissing-parameter-type # 未指定参数类型
-Wredundant-decls # 冗余声明
-Wrestrict # 当一个对象被另一个带有约束限制的参数引用时,或者这些对象之间的副本重叠时发出警告
-Winline # 当被声明为inline的函数无法内嵌时,发生警告
## 分支相关
-Wempty-body # 如果在if else或do while语句中出现空体,则发出警告
-Wduplicated-branches # 当if-else分支相同时
-Wduplicated-cond # 当if-else-if中使用重复的条件时
-Wdangling-else # 当出现可能混淆else分支所属的if语句时发出警告
-Wimplicit-fallthrough # case语句落空
## 结构相关
-Wmissing-field-initializers # 结构的初始化器当中缺少某些字段
-Wpacked # 如果结构被赋予了压实(packed)属性,但压实属性对结构的布局或大小没有影响
-Wpadded # 当为了对齐结构中的成员或者对齐整个结构而在结构中产生空白(padding)时,发出警告
-Wpacked-not-aligned # 如果在压实的(packed)结构或联合中包含有一个未对齐的显式指定对齐的结构时,发出警告
## 数组相关
-Wvla # 当使用可变长度的数组时发出警告
-Wvla-larger-than # 当可变长度数组的尺寸大于指定值时产生警告
-Wwizeof-array-argument # 当sizeof运算符应用于在函数定义中声明为数组的参数时
-Warray-bounds # 数组下表始终越界时产生警告(和优化选项一并使用)
2.7 其他警告
-Wabosolute-value # 当有更合适的标准函数可用时,对计算参数绝对值的不正确的标准函数调用发出警告
## 函数属性相关
-Wfree-nonheap-object # 尝试释放未在堆上分配的对象或使用未从先前调用相应分配函数返回的指针时发出警告
-Wnonnull # 接受被修饰函数非空的指针参数
-Wnonnull-compare
-Wunused-result # 不使用返回值时产生警告
-Wno-unused-result
-Wformat # 检查格式化字符串和参数。
3 常见案例
CASE01: 警告当作错误严肃对待
-Wall # 使能通用的编译警告
-Wextra # 使能补充编译警告
-Werror # 将编译警告当成错误处理
-Fmax-error=10 #错误数量为10,及时停止编译
CASE02: 函数属性(编译警告相关)
函数属性用于辅助修饰一些函数的行为,以帮助编译器完成一些基本的代码分析,提示可能出现的缺陷
/* `malloc` 属性
* -Wfree-nonheap-object # 尝试释放未在堆上分配的对象或使用未从先前调用相应分配函数返回的指针时发出警告
*/
/* `nonnull` 属性
* -Wnonnull # 接受被修饰函数非空的指针参数
* -Wnonnull-compare
*/
void *memcpy(void *dst, const void *src, size_t n)
__attribute__ ((nonnull (1, 2)));
/* `__warn_unused_result__` 属性
* -Wunused-result # 不使用返回值时产生警告
* -Wno-unused-result
*/
int chdir(const char *path) __attribute__ ((__warn_unused_result__));
/* `format 属性`
* -Wformat # 检查格式化字符串和参数。
*/
format (archetype, string-index, first-to-check)
CASE03: 常见函数属性(非编译警告)
/* `const` 属性, 函数对相同的参数返回相同的结果;用于优化 */
int square (int) __attribute__ ((const));
/* `deprecated` 属性, 标记函数被废弃,将在未来移除;使用时产生 `-Wdeprecated`警告 */
int old_fn () __attribute__ ((deprecated));
/* `unavailable` 属性, 标记函数不可用(已被移除);使用时产生错误,无需等到链接时 */
int removed_fn () __attribute__ ((unavailable));
/* `noreturn` 属性, 标记该函数不会返回,如标准 C 库函数 `exit` 和 `abort` */
void fatal () __attribute__ ((noreturn));
void fatal (/* … */)
{
/* … */ /* Print error message. */ /* … */
exit (1);
}
/* `returns_nonnull` 属性, 标记该函数的返回值不会为 NULL */
extern void *
mymalloc (size_t len) __attribute__((returns_nonnull));
/* `visibility` 属性, 标记外部/全局函数和变量的可见性;可取 `default`、`hidden`、`internal`、`protected` 四个值之一 */
void __attribute__ ((visibility ("internal"))) fn (void)
{
/* Do something. */;
}
int i __attribute__ ((visibility ("hidden")));
CASE04: 不会被警告的缺陷
#include <stdio.h>
static unsigned int number_of_calls(const unsigned int *nr_calls)
{
static unsigned int _nr_calls;
_nr_calls++;
if (nr_calls == NULL)
return 0;
return _nr_calls;
}
static void foo(void)
{
unsigned int nr_calls;
number_of_calls(&nr_calls); // 由于函数形参为const修饰, 实际上nr_calls不会被初始化
printf ("The number of calls: %un", nr_calls);
char hello[100];
printf ("%sn", hello); // hello没有被初始化, 这里字符串输出不能确定打印内容
}
void bugs_not_warned(void)
{
foo();
}
4 参考资料
- best-practices-of-c
- C 语言最佳实践之清除所有编译警告
最后
以上就是缓慢花生为你收集整理的C语言消除编译警告C语言消除编译警告的全部内容,希望文章能够帮你解决C语言消除编译警告C语言消除编译警告所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复