我正在编写一个函数库,可以安全地在各种数字类型之间进行转换或者尝试.我的意图大致相当于创建有用库和学习C边缘案例.
我int
-到- size_t
功能引发了GCC -Wtype-limits
,声称我不应该测试,如果一个警告int
是大于SIZE_MAX
,因为它永远不会是真实的.(另一个转换int
为ssize_t
生成相同警告的函数SSIZE_MAX
.)
我的MCVE,有额外的评论和婴儿步骤,是:
#include /* SIZE_MAX */
#include /* exit EXIT_FAILURE size_t */
extern size_t i2st(int value) {
if (value <0) {
exit(EXIT_FAILURE);
}
// Safe to cast --- not too small.
unsigned int const u_value = (unsigned int) value;
if (u_value > SIZE_MAX) { /* Line 10 */
exit(EXIT_FAILURE);
}
// Safe to cast --- not too big.
return (size_t) u_value;
}
编译器警告
我在Linux 2.6.34上得到了GCC 4.4.5的类似警告:
$ gcc -std=c99 -pedantic -Wall -Wextra -c -o math_utils.o math_utils.c
math_utils.c: In function ‘i2st’:
math_utils.c:10: warning: comparison is always false due to limited range of data type
...以及Linux 3.10.0上的GCC 4.8.5:
math_utils.c: In function ‘i2st’:
math_utils.c:10:5: warning: comparison is always false due to limited range of data type [-Wtype-limits]
if (u_value > SIZE_MAX) { /* Line 10 */
^
这些警告对我来说似乎不合理,至少在一般情况下是这样.(我不否认在硬件和编译器的某些特定组合上比较可能"总是假的".)
C标准
C 1999标准似乎并不排除int
大于SIZE_MAX
.
"6.5.3.4 sizeof
运营商"一节根本没有涉及size_t
,除非将其描述为"在
(和其他标题)中定义".
"7.17通用定义
"一节定义size_t
为" sizeof
运算符结果的无符号整数类型".(多谢你们!)
"7.18.3其他整数类型的限制"一节更有用---它将"限制size_t
" 定义为:
SIZE_MAX
65535
...含义SIZE_MAX
可能小到65535. int
(有符号或无符号)可能比这大得多,具体取决于硬件和编译器.
堆栈溢出
接受的" unsigned int
vs.size_t
" 答案似乎支持我的解释(重点补充):
该size_t
类型可以是比我的大,等于,小于或更小的unsigned int
,和你的编译器可能会做出假设它进行优化.
这个答案引用了我已经引用的C标准的"7.17节".
其他文件
我的搜索结果显示了Open Group的论文" 数据大小中立性和64位支持 ",该文件声称"64位数据模型"(重点补充):
ISO/IEC 9899:1990,编程语言 - C(ISO C)留下了short int
,the int
,the long int
和pointer
故意模糊的定义[...]唯一的限制是int
s必须不小于short
s,long
s必须不小于int
s,并且size_t
必须代表实现支持的最大无符号类型.[...]基本数据类型之间的关系可表示为:
sizeof(char)
<= sizeof(short)
<= sizeof(int)
<= sizeof(long)
=sizeof(size_t)
如果这是真的,那么测试int
反对SIZE_MAX
确实是徒劳的......但是这篇论文并没有引用章节和经文,所以我不知道它的作者是如何得出结论的.他们自己的"基本规范版本7"
sys/types.h
文档
没有解决这个问题.
我的问题
据我所知,size_t
是不太可能比窄int
,但的确C标准保证在比较some_unsigned_int > SIZE_MAX
将永远是假的?如果是的话,在哪里?
未重复
这个问题有两个半重复,但他们都在询问关于size_t
应该代表什么以及什么时候应该/不应该使用的更一般的问题.
" C中的内容是什么size_t
? "没有解决size_t
其他整数类型之间的关系.它接受的答案只是维基百科的一句话,它没有提供我已经发现的任何信息.
" 这个问题的正确定义是size_t
什么? "几乎与我的问题重复,但随后转向偏离,询问何时size_t
应该使用以及为何引入它.它作为上一个问题的副本而被关闭.
rici..
8
当前的C标准并不需要size_t
至少与a一样宽int
,而且我对任何版本的标准都持怀疑态度.size_t
需要能够表示任何可能是对象大小的数字; 如果实现将对象大小限制为24位宽,则size_t
可以是24位无符号类型,而不管它是什么int
.
GCC警告并未涉及理论上的可能性.它正在检查特定的硬件平台和特定的编译器和运行时.这意味着它有时会触发试图移植的代码.(在其他情况下,可移植代码将触发可选的GCC警告.)这可能不是您希望警告可以执行的操作,但可能有用户的期望与实现的行为完全匹配,并且标准不提供任何指导用于编译器警告.
正如OP在评论中提到的那样,这个警告有很长的历史.警告是在版本3.3.2左右(2003年)中引入的,显然不受任何-W
标志的控制.这被用户报告为错误12963,他们显然觉得警告不鼓励便携式编程.从错误报告中可以看出,各种GCC维护者(以及社区中其他知名成员)都强烈感受到了相互冲突的意见.(这是开源错误报告中的常见动态.)几年后,决定使用标志来控制警告,并且默认情况下不启用该标志或作为其一部分-Wall
.在此期间,该-W
选项已重命名-Wextra
,并且新创建的flag(-Wtype-limits
)已添加到-Wextra
集合中.对我来说,这似乎是正确的解决方案.
这个答复的其余部分包含了我的个人意见.
-Wall
,如GCC手册中所述,实际上并未启用所有警告.它允许那些警告"关于一些用户认为有问题的构造,并且容易避免(或修改以防止警告),甚至与宏一起使用." GCC可以检测到许多其他条件:
请注意,并不暗示某些警告标志-Wall
.他们中的一些人警告用户通常不认为有问题的结构,但有时您可能希望检查; 其他人警告在某些情况下必要或难以避免的构造,并且没有简单的方法来修改代码以抑制警告.其中一些是启用的,-Wextra
但其中许多必须单独启用.
这些区别有些武断.例如,每次GCC决定"在'|''内'和''周围建议括号时,我都要咬紧牙关." (似乎没有必要在'+'内围绕'*'建议括号,这对我来说并没有什么不同.)但我认识到我们所有人都有不同的舒适度与运算符优先级,而不是全部海湾合作委员会关于括号的建议对我来说似乎过分了.
但总的来说,区别似乎是合理的.有些警告通常是适用的,并且启用了这些警告-Wall
,应始终指定这些警告,因为这些警告几乎总是要求采取措施纠正缺陷.还有其他警告可能在特定情况下有用,但也有很多误报; 这些警告需要单独调查,因为它们并不总是(或者甚至经常)与代码中的问题相对应.
我知道有些人认为海湾合作委员会知道如何警告某些情况这一事实足以要求采取行动以避免这种警告.每个人都有权获得他们的风格和审美判断,这是正确的,只是这样的程序员添加-Wextra
到他们的构建标志.然而,我并不在那群人中.在项目的给定点,我将尝试使用大量可选警告启用的构建,并考虑是否根据报告修改我的代码,但我真的不想花费我的开发时间每次重建文件时都要考虑非问题.这面-Wtypes-limit
旗帜对我来说属于这一类.
1> rici..:
当前的C标准并不需要size_t
至少与a一样宽int
,而且我对任何版本的标准都持怀疑态度.size_t
需要能够表示任何可能是对象大小的数字; 如果实现将对象大小限制为24位宽,则size_t
可以是24位无符号类型,而不管它是什么int
.
GCC警告并未涉及理论上的可能性.它正在检查特定的硬件平台和特定的编译器和运行时.这意味着它有时会触发试图移植的代码.(在其他情况下,可移植代码将触发可选的GCC警告.)这可能不是您希望警告可以执行的操作,但可能有用户的期望与实现的行为完全匹配,并且标准不提供任何指导用于编译器警告.
正如OP在评论中提到的那样,这个警告有很长的历史.警告是在版本3.3.2左右(2003年)中引入的,显然不受任何-W
标志的控制.这被用户报告为错误12963,他们显然觉得警告不鼓励便携式编程.从错误报告中可以看出,各种GCC维护者(以及社区中其他知名成员)都强烈感受到了相互冲突的意见.(这是开源错误报告中的常见动态.)几年后,决定使用标志来控制警告,并且默认情况下不启用该标志或作为其一部分-Wall
.在此期间,该-W
选项已重命名-Wextra
,并且新创建的flag(-Wtype-limits
)已添加到-Wextra
集合中.对我来说,这似乎是正确的解决方案.
这个答复的其余部分包含了我的个人意见.
-Wall
,如GCC手册中所述,实际上并未启用所有警告.它允许那些警告"关于一些用户认为有问题的构造,并且容易避免(或修改以防止警告),甚至与宏一起使用." GCC可以检测到许多其他条件:
请注意,并不暗示某些警告标志-Wall
.他们中的一些人警告用户通常不认为有问题的结构,但有时您可能希望检查; 其他人警告在某些情况下必要或难以避免的构造,并且没有简单的方法来修改代码以抑制警告.其中一些是启用的,-Wextra
但其中许多必须单独启用.
这些区别有些武断.例如,每次GCC决定"在'|''内'和''周围建议括号时,我都要咬紧牙关." (似乎没有必要在'+'内围绕'*'建议括号,这对我来说并没有什么不同.)但我认识到我们所有人都有不同的舒适度与运算符优先级,而不是全部海湾合作委员会关于括号的建议对我来说似乎过分了.
但总的来说,区别似乎是合理的.有些警告通常是适用的,并且启用了这些警告-Wall
,应始终指定这些警告,因为这些警告几乎总是要求采取措施纠正缺陷.还有其他警告可能在特定情况下有用,但也有很多误报; 这些警告需要单独调查,因为它们并不总是(或者甚至经常)与代码中的问题相对应.
我知道有些人认为海湾合作委员会知道如何警告某些情况这一事实足以要求采取行动以避免这种警告.每个人都有权获得他们的风格和审美判断,这是正确的,只是这样的程序员添加-Wextra
到他们的构建标志.然而,我并不在那群人中.在项目的给定点,我将尝试使用大量可选警告启用的构建,并考虑是否根据报告修改我的代码,但我真的不想花费我的开发时间每次重建文件时都要考虑非问题.这面-Wtypes-limit
旗帜对我来说属于这一类.
@ machine_1:"宽于"和"不窄于"*表示不同的东西*.你不能在不改变程序语义的情况下改变`> =`到`>`.无论如何,我把它改为"至少和我一样宽",这似乎更适合我,但也许更符合答案读者的美学.