2

I am following K&R second edition examples to learn C and coding as I think this is correct way of doing things. Anyhow when I run this program post compilation the program get stuck. I used valgrind for execution of compiled script.

#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==
Lundin
  • 195,001
  • 40
  • 254
  • 396
vector8188
  • 1,293
  • 4
  • 22
  • 48
  • Unless I am very much mistaken, this is neither "K&R" (or is this copy & paste from the book?), nor is it a "memory leak" (no such message from valgrind). This is your attempt at implementing the `atoi()` function that gets stuck in an endless loop. You might want to edit the title of the question. – DevSolar Feb 05 '16 at 08:59
  • @DevSolar You are mistaken, this code is almost a copy/paste from the book p54. – Lundin Feb 05 '16 at 09:24
  • Valgrind? I'm surprised that you did not detect the 'n/i' infinite loop when stepping through with your debugger. – Martin James Feb 05 '16 at 09:29
  • @Lundin: Including the bugs? I've lent my copy to my friend's son. I'd be rather surprised if such an old and well-studied book had such a howler in it. – DevSolar Feb 05 '16 at 09:30
  • @DevSolar The bug was introduced by the OP. But K&R is otherwise infamous for its considerable amount of typos, bugs and reliance on poorly-specified behavior. It also seems that the K&R errata is no longer available on the net. My advise is to not read this book, it is harmful reading for C programmers in the year 2016. – Lundin Feb 05 '16 at 09:56
  • @lundin so if KR not worth reading, whats your recommendation then :) – artm Feb 05 '16 at 10:07
  • @artm I haven't read any C books in ages so I only have anti-recommendations. The [C FAQ](http://c-faq.com/) is good reading though. – Lundin Feb 05 '16 at 10:12
  • In fact there are not many C books after KR. My take is that it's way better than the recent few books on the subject.. – artm Feb 05 '16 at 10:19
  • 1
    [The Definite C Book Guide](http://stackoverflow.com/questions/562303) and myself agree that you can get a *lot* worse than K&R, but there *are* plenty good alternatives. – DevSolar Feb 05 '16 at 10:43
  • I wouldn't recommend that list, it is completely subjective, anyone can dump any crap book there and get away with it for months or years. – Lundin Feb 05 '16 at 10:54
  • 1
    my primary objective is to set myself for learning basic programming technique which are relevant today. I have liked K&R because it explains and teaches you programming constructs which you can apply other programming language. – vector8188 Feb 05 '16 at 17:10

3 Answers3

3

In your second loop, you are iterating n but you use i for your computations and computations. This leads to the infinite loop you observe. To fix this, use either i as an index consistently:

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;
}

Note that indices should have type size_t, not int as the latter might be not large enough to index every array. For this purpose, an index of type int is fine though.

fuz
  • 88,405
  • 25
  • 200
  • 352
3

As noticed, the index in your second loop is incorrect.

for(int n = 0; isdigit(s[i]); n++)
    n = 10 * n + (s[i]-'0');

This should be - notice that you should NOT reinitialize the index i in the second loop as it should be the left over value from the first loop (as you already skipped the white spaces and the sign).

for( ; isdigit(s[i]); i++ )
    n = 10 * n + (s[i]-'0');
artm
  • 17,291
  • 6
  • 38
  • 54
  • Your removing `n = 0` leads to undefined behaviour since `n` isn't initialised. The loop is supposed to start with `n = 0` and increment `i` (note that `n` belongs to the function scope). It's very confusing, but it's deliberate. – molbdnilo Feb 05 '16 at 09:52
  • 'n' should be initialized when it's declared, not inside the loop which causes more confusions. – artm Feb 05 '16 at 09:56
3

The K&R example could be improved for readability and performance. The following issues exist:

  • You are not allowed to name your own functions the same as standard lib functions. atoi exists in stdlib.h.
  • Use const correctness for the passed parameter.
  • The multiple loops and iterators are hard to read and maintain, which caused the bug in the question. Ideally there should only be one iterator. Arguably, this bug was caused by the original programmer(s), since they wrote unmaintainable, sloppy code.
  • The type size_t should be used for the iterators, not int.
  • It doesn't hurt if that iterator has a meaningful name either. n is a very bad name for an iterator! In C programming, n has a special meaning and that is to describe the number of items in a container.
  • For the sake of readability and possibly also for performance, the pointer parameter can be used as iterator. Then we don't need to invent all these local iterator variables in the first place.
  • Use a bool variable for the sign to increase readability.
  • There is no need to check if the current character is - twice.
  • Always use braces after every statement, to prevent fatal bugs, like the Apple goto fail.

#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;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I must say I never use bool type in C, I still rely on the 0,1. Maybe it's time to upgrade. Your example is very clean and I like it, I see that you put the declaration in the middle of the function. Don't you think it would be better to move them at the beginning? – terence hill Feb 05 '16 at 10:37
  • @terencehill It is not really all that important where the variable is declared, it is the most minor concern in the original code. I put the declarations near where the variable is used to clearly show that the variable has been initialized and what value that was used. Code of the kind `int x=something; /* hundred lines of unrelated code */ x += y;` is hard to read. For the specific case of the `result` variable though, it would perhaps be good practice to put it at the very top under the function definition, as it is the value returned from the function. – Lundin Feb 05 '16 at 10:46