C K&;R atoi通用内存泄漏
我按照K&R第二版的例子学习C和编码,因为我认为这是正确的方法。无论如何,当我在编译后运行这个程序时,程序被卡住了。我使用valgrind执行编译后的脚本C K&;R atoi通用内存泄漏,c,atoi,kernighan-and-ritchie,C,Atoi,Kernighan And Ritchie,我按照K&R第二版的例子学习C和编码,因为我认为这是正确的方法。无论如何,当我在编译后运行这个程序时,程序被卡住了。我使用valgrind执行编译后的脚本 #include <ctype.h> int atoi(char s[]) { int i, n, sign; for(i = 0; isspace(s[i]); i++) ; sign = (s[i] == '-')? -1 : 1; if (s[i] == '+'|| s[i]
#include <ctype.h>
int atoi(char s[])
{
int i, n, sign;
for(i = 0; isspace(s[i]); i++)
;
sign = (s[i] == '-')? -1 : 1;
if (s[i] == '+'|| s[i] == '-')
i++;
for (int n = 0; isdigit(s[i]); n++)
n = 10 * n + (s[i]-'0');
return sign * n;
}
int main()
{
char input[] = " -12345";
atoi(input);
}
# vaibhavchauhan at startup001 in ~/Documents/Projects/K-R on git:master x [0:43:36]
$ valgrind ./atoi-general
==8075== Memcheck, a memory error detector
==8075== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8075== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==8075== Command: ./atoi-general
==8075==
^C==8075==
#包括
int atoi(字符s[]
{
int i,n,符号;
对于(i=0;isspace(s[i]);i++)
;
符号=(s[i]='-')?-1:1;
如果(s[i]='+'| | s[i]=='-')
i++;
对于(int n=0;isdigit(s[i]);n++)
n=10*n+(s[i]-'0');
返回符号*n;
}
int main()
{
字符输入[]=“-12345”;
atoi(输入);
}
#vaibhavchauhan在git上的~/Documents/Projects/K-R中的startup001上:master x[0:43:36]
$valgrind./atoi general
==8075==Memcheck,内存错误检测器
==8075==2002-2015年版权(C)和GNU GPL'd,朱利安·苏厄德等人。
==8075==使用Valgrind-3.11.0和LibVEX;使用-h重新运行以获取版权信息
==8075==命令:./atoi常规
==8075==
^C==8075==
在第二个循环中,您正在迭代n
,但您使用i
进行计算和计算。这将导致你观察到的无限循环。要解决此问题,请始终使用i
作为索引:
int atoi(char s[])
{
int i, sign, n;
for(i = 0; isspace(s[i]); i++)
;
sign = (s[i] == '-')? -1 : 1;
if (s[i] == '+'|| s[i] == '-')
i++;
for (n = 0; isdigit(s[i]); i++)
n = 10 * n + (s[i]-'0');
return sign * n;
}
请注意,索引的类型应该是
size\u t
,而不是int
,因为后者可能不够大,无法索引每个数组。为此,类型为int
的索引是可以的。注意到,第二个循环中的索引是不正确的
for(int n = 0; isdigit(s[i]); n++)
n = 10 * n + (s[i]-'0');
这应该是-注意,您不应该重新初始化第二个循环中的索引i
,因为它应该是第一个循环的剩余值(因为您已经跳过了空格和符号)
K&R示例的可读性和性能可以得到改进。存在以下问题:
- 不允许将自己的函数命名为与标准库函数相同的函数<代码>原子存在于stdlib.h中
- 对传递的参数使用const correction
- 多个循环和迭代器很难读取和维护,这导致了问题中的错误。理想情况下,应该只有一个迭代器。可以说,这个bug是由原始程序员造成的,因为他们编写了无法维护的、草率的代码李>
- 迭代器应使用类型
,而不是size\u t
int
- 如果迭代器有一个有意义的名称,也不会有什么坏处
对于迭代器来说是一个非常糟糕的名字!在C编程中,n
有一个特殊的含义,即描述容器中项目的数量n
- 为了可读性,也可能为了性能,指针参数可以用作迭代器。那么我们就不需要首先发明所有这些局部迭代器变量
- 为符号使用bool变量以增加可读性
- 无需检查当前字符是否为
两次-
- 每次声明后都要使用大括号,以防止致命的错误,如Apple goto fail
#包括
#包括
int my_atoi(常量字符*str)
{
while(isspace(*str))
{
str++;
}
布尔符号=假;
如果(*str=='-')
{
符号=真;
str++;
}
如果(*str=='+'),则为else
{
str++;
}
int结果=0;
while(isdigit(*str))
{
结果=10*result+*str-'0';
str++;
}
如果(签名)
{
结果=-结果;
}
返回结果;
}
除非我大错特错,否则这既不是“K&R”(或者这是书中的复制粘贴?),也不是“内存泄漏”(valgrind没有这样的消息)。这是您试图实现陷入无休止循环的atoi()
函数的尝试。您可能想编辑问题的标题。@DevSolar您弄错了,这段代码几乎是p54.Valgrind一书的复制/粘贴?我很惊讶,在使用调试器时,您没有检测到“n/I”无限循环。我也同意,你可能会比K&R差很多,但有很多好的选择。我的主要目标是让自己学习与今天相关的基本编程技术。我喜欢K&R,因为它解释并教您可以应用其他编程语言的编程结构。索引I
不应该在第二个循环中重新初始化。for语句是无限循环的,因为检查条件始终为真?@VaibhavChauhan是的。删除n=0
会导致未定义的行为因为n
未初始化。循环应以n=0
和增量i
开始(注意n
属于功能范围)。这很让人困惑,但这是故意的。声明时,应该初始化“n”,而不是在循环内部,这会导致更多的混乱。我必须说,我从未在C中使用bool类型,我仍然依赖于0,1。也许是时候升级了。你的例子非常干净,我喜欢它,我看到你把声明放在函数的中间。您不认为最好在开始时移动它们吗?@terencehill声明变量的位置其实并不重要,这是原始代码中最次要的问题。我将声明放在使用变量的位置附近,以清楚地显示变量已初始化以及使用了什么值。这类代码intx=something;/*一百行无关代码*/x+=y代码>很难阅读。但是对于result
变量的特定情况,最好将其放在函数定义的最上面,因为它是函数返回的值。
for( ; isdigit(s[i]); i++ )
n = 10 * n + (s[i]-'0');
#include <ctype.h>
#include <stdbool.h>
int my_atoi (const char* str)
{
while(isspace(*str))
{
str++;
}
bool sign = false;
if(*str == '-')
{
sign = true;
str++;
}
else if(*str == '+')
{
str++;
}
int result = 0;
while(isdigit(*str))
{
result = 10*result + *str - '0';
str++;
}
if(sign)
{
result = -result;
}
return result;
}