我只是阅读" C接口和实现"。书中描述了一些非常有趣的概念。有时(在我看来)代码非常难看,但现在我有一个关于将整数/ long转换为字符串(char数组)的问题。书中描述的是:
const char *Atom_int(long n) {
char str[43];
char *s = str + sizeof str;
unsigned long m;
if (n == LONG_MIN)
m = LONG_MAX + 1UL;
else if (n < 0)
m = -n;
else
m = n;
do
*--s = m%10 + '0';
while ((m /= 10) > 0);
if (n < 0)
*--s = '-';
return Atom_new(s, str + sizeof str - s);
}
由于没有描述为什么使用这个功能的方式......我想知道为什么它不仅仅是简单的东西:
const char *Atom_int(long n)
{
char str[43];
char *s = str;
sprintf(str, "%ld", n);
return Atom_new(s, str + sizeof str - s);
}
有什么区别吗?我错过了关于我的&#34;简单&#34;使用sprintf的方法可能会导致与书中的函数不同的结果?我的意思是,如果它只是为了展示如何将长片转换为不使用ltoa / sprintf / ...的字符串,那很好。但如果这是唯一的原因,这是不必要的复杂,......
答案 0 :(得分:6)
您为两个功能发布的原始代码存在两个主要问题:
str
数组未'\0'
终止,在传递给printf
时调用未定义的行为。s
返回到具有自动存储str
的数组也是不正确的。取消引用此返回值也将调用未定义的行为。关于你的问题,第一个函数的目的是显示整数到字符串转换器的实现。使用sprintf
会失败。请注意作者如何处理INT_MIN
的子区域情况:计算-n
会因为大多数系统上的整数溢出而调用未定义的行为,尽管结果在所有现代系统上都是正确的。但完全符合标准是一项困难的艺术:他的解决方案假设2s补充,否则将失败。
这是一个使用相同原型的改进解决方案。它更具可移植性,不需要特殊情况LONG_MIN
,可以减少分割和模运算。
const char *Atom_int(long n) {
char str[43];
char *s = str + sizeof str;
unsigned long m;
if (n < 0)
m = (unsigned long)-(n + 1) + 1;
else
m = n;
while (m >= 10) {
*--s = m % 10 + '0';
m /= 10;
}
*--s = m + '0';
if (n < 0)
*--s = '-';
return Atom_new(s, str + sizeof str - s);
}
另请注意,您建议的替代方案不正确:您将错误的长度传递给Atom_new()
。您应该传递sprintf
或snprintf
返回的字节数。这是一个改进版本:
const char *Atom_int(long n) {
char str[43];
return Atom_new(str, snprintf(str, sizeof str, "%ld", n));
}