C 为什么itoa被宣布为不安全?

C 为什么itoa被宣布为不安全?,c,security,C,Security,为什么 char* itoa(int value, char* str, int base); 而不是 char (*itoa(int value, char (*str)[sizeof(int) * CHAR_BIT + 1], int base))[sizeof(int) * CHAR_BIT + 1] 哪一个可以保护程序员免受缓冲区溢出 为什么itoa被宣布为不安全 请注意,itoa()不是标准的C库函数,实现/签名各不相同 itoa(int-value,char*str,int-ba

为什么

char* itoa(int value, char* str, int base);
而不是

char (*itoa(int value, char (*str)[sizeof(int) * CHAR_BIT + 1], int base))[sizeof(int) * CHAR_BIT + 1]
哪一个可以保护程序员免受缓冲区溢出

为什么itoa被宣布为不安全

请注意,
itoa()
不是标准的C库函数,实现/签名各不相同

itoa(int-value,char*str,int-base)
是一些库提供的构建块函数。这意味着当一个知识渊博的程序员使用它时,它会占用足够的缓冲空间

然而,
itoa(int-value,char*str,int-base)缺少缓冲区大小安全保护。最坏的情况很容易计算错误,就像OP所做的那样

建议
itoa(char*dest,size\u t size,int a,int base)
并放弃缓冲区大小检查的较小效率。注意:调用方应检查返回值


必须注意预测最大字符串需求

根据OP的建议,考虑以下内容:

#define INT_N (sizeof(int) * CHAR_BIT + 1)
char (*itoa(int value, char (*str)[INT_N], int base))[INT_N]
需要将有符号的32位
INT_MIN
转换为基数2

"-10000000000000000000000000000000"
INT\N
为33,但所需的缓冲区大小为34


itoa不是标准的,所以任何库都可以随意声明它。对于一开始就没有标准化的功能,询问其基本原理是没有什么意义的

而那些标准化的功能大多是偶然进入标准的。他们基本上接受了Unix中可用的每一个函数,在纸上写下它的名字,把它们扔进帽子里,然后异想天开地画了大约一百个。所以我们得到了一系列不同的函数,它们的质量和用途都不同。我要说的是,它们大多数要么不安全,要么风格不好。不存在任何理由


关于具体情况:

不使用固定数组指针的原因显然是C语言中的大多数库函数,不管是标准函数还是非标准函数,都处理以null结尾的可变长度字符串。如果某个函数的行为会有所不同,它就会脱颖而出。在C启动时,Unix显然正试图从固定长度字符串转移到以null结尾的字符串

此外,关于指针转换的规则一开始几乎不存在,因此在20世纪70年代,当所有这些函数都被修改时,使用数组指针可能不会增加任何安全性。甚至没有空指针类型


关于缓冲区溢出和其他错误控制:在编写C时,可以在函数或调用方中放置错误控制。C语言中最常见的做法是将错误处理留给调用方,只要有文档记录就可以了。为此,有一个理论基础,即“C的精神”,即始终把性能放在首位。错误处理会影响性能。在许多用例中,调用者提前知道数据的性质,这使得这种错误控制是多余的

因为您的工作是防止CAnd中的缓冲区溢出,如果传递给函数的缓冲区不是大小
sizeof(int)*字符位+1
?如果它是由
malloc
返回的动态分配指针,该怎么办?如果要将返回的指针传递给另一个只需要指向以null结尾的字符串的指针的函数,该怎么办?它与过时的
get
函数有很大不同,后者程序员无法采取任何措施防止缓冲区溢出。@Artikash这些大多是Microsoft对C的扩展,这是一个意见问题它们是否使代码更安全,部分原因是用户经常错误地使用它们。而且它们是不可携带的。@Artikash。在这一点上,你的问题是基于观点的。有些低级语言的理念与您希望看到的一致。更深层的原因:在我看来,“不安全的”
itoa(int-value,char*str,int-base)
的一个原因是缓冲区管理在留给调用方的同时,很难推进到函数中。我的备用
iota()
可防止缓冲区溢出和UB,但调用方仍必须检查返回值。为了要求始终提供最坏情况下的缓冲区(例如34),当调用代码知道较小的缓冲区就足够时,会使
itoa()
的使用变得麻烦。C既快又瘦。精益是有代价的。安全变量是
asprintf(&dest,“%d”,value)。或者类似的
fprintf()
,带有一个
文件*
,该文件来自
open\u memstream()
调用。除非您被迫支持非POSIX.1-2008兼容的系统,否则没有理由依赖不安全的字符串操作函数,即使这样,依靠
vsnprintf()提供自己的
asprintf()
实现通常也更容易
而不是试图正确分配每个缓冲区。@cmaster不会在此处用备选答案发表评论,发布您的
asprintf()
将允许其他人对其进行投票。我的评论不是答案,因为它不包含任何关于问题“为什么”部分的信息。当我写一个答案的时候,我试着实际回答这个问题。有时成功地。。。