2

I try to convert a string to an integer by using this function:

#include <stdio.h>
#include <assert.h>
#include <limits.h>

int StringToInteger(const char* str) {
    int sign = 1, result = 0;
    
    assert(str != NULL);
    
    while (*str == ' ' || *str == '\t' || *str == '\n' ||
           *str == '\r' || *str == '\v' || *str == '\f') {
        str++;
    }

    if (*str == '+') {
        str++;
    } else if (*str == '-') {
        sign = -1;
        str++;
    }

    while (*str >= '0' && *str <= '9') {
        if (result > (INT_MAX / 10) || (result == INT_MAX / 10 && *str - '0' > (INT_MAX % 10))) {
            return (sign == 1) ? INT_MAX : INT_MIN;
        }

        result = result * 10 + (*str - '0');
        str++;
    }

    
    return result * sign;
}

For some reason, when it gets the following string 5240157683pykjw it fails. How can I fix this?

For some reason it keeps iterating when encountering a non digit.

binaryescape
  • 105
  • 6
Bar Gonen
  • 21
  • 2
  • 5
    I recommend you start using the `isspace` and `isdigit` functions. – Some programmer dude Aug 05 '23 at 14:34
  • 4
    [Seems fine to me](https://godbolt.org/z/csjEnvjrq). `5240157683` is too large to fit in an int so it returns `INT_MAX`. What do you mean by "it fails"? – Kevin Aug 05 '23 at 14:34
  • Have you stepped through it in a debugger to see what's going on with the loop and your assorted variables? – Shawn Aug 05 '23 at 14:34
  • 2
    As for your problem, this seems like a perfect time to learn how to [*debug*](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) your programs. For example by using a [*debugger*](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) to step through the code line by line while monitoring variables and their values. – Some programmer dude Aug 05 '23 at 14:35
  • 4
    Oh, and I assume this is a school or book assignment/exercise? Because otherwise you should just use the `strtol` function. – Some programmer dude Aug 05 '23 at 14:35
  • @Someprogrammerdude I don't think an explicit check for a null terminator is necessary, as any increment of `str` only happens when it's pointing to a specific character (e.g. a space, +, -, or digit). For example `StringToInteger(" ")` would return 0. – Kevin Aug 05 '23 at 14:37
  • Side note, test if your program handles for cases like this: `565a67b7`. – binaryescape Aug 05 '23 at 14:37
  • @Kevin True, edited my comment. – Some programmer dude Aug 05 '23 at 14:39
  • @Someprogrammerdude The `*str - '0' > (INT_MAX % 10)` check is to specifically handle a string like `2147483648`, where adding the last digit would made the result greater than `INT_MAX`, but if the last digit was `7` it would be fine – Kevin Aug 05 '23 at 14:42
  • 1
    Another by the way: [The `assert` macro](https://en.cppreference.com/w/c/error/assert) is usually disabled in release code. Please don't use it for run-time checking. Also note that if the assertion fails, the program will *crash*, not exit gracefully or return with an error from the function. Crashing is not very user-friendly. – Some programmer dude Aug 05 '23 at 14:45
  • Thank you all, it is an exercise from school and I used a special compiler of that school, and it doesn't have the INT MAX and INT MIN functions – Bar Gonen Aug 05 '23 at 14:51
  • 2
    `INT_MIN` and `INT_MAX` are macros (not functions) defined in ``. If your compiler does not define them, it is irretrievably broken. – Jonathan Leffler Aug 05 '23 at 14:52
  • 1
    @BarGonen If your environment for some reason didn't have the standard `INT_MAX` and `INT_MIN` macros, then the code you show would not build, as you use them. – Some programmer dude Aug 05 '23 at 14:54
  • Note: it may be useful to have your function take a pointer to a `char *` and then set this to the value of `str` _after_ reading all of the digits. You could use this to efficiently read multiple ints from a string. – Chris Aug 05 '23 at 16:42
  • Why not `strtod`? – i486 Aug 05 '23 at 16:42
  • @BarGonen "For some reason, when it gets this string: 5240157683pykjw it fails. How to fix?" not reproduceable, as in general, code is fine. Post a [mcve]. – chux - Reinstate Monica Aug 05 '23 at 16:42
  • @BarGonen "when it gets this string: 5240157683pykjw it fails." --> I suspect you have a buffer overflow on input in the calling code. – chux - Reinstate Monica Aug 05 '23 at 16:52
  • @BarGonen Minor side issue: Consider `if (result > (INT_MAX / 10) || (result == INT_MAX / 10 && *str - '0' > (INT_MAX % 10))) {` vs. `if (result >= (INT_MAX / 10) && (result >INT_MAX / 10 || *str - '0' > (INT_MAX % 10))) {`. The 2nd form may emit faster code. – chux - Reinstate Monica Aug 05 '23 at 16:56
  • @i486 `strtod()` would be a poor substitute for string to integer conversions. Perhaps you were thinking of `strtol()`? – chux - Reinstate Monica Aug 05 '23 at 16:57
  • @Someprogrammerdude Re "start using the `isspace`". Maybe. Typical use of `isspace()` suffers from _signed_ `char` issues and pedantically invokes _locale_ issues. OP's code is OK. – chux - Reinstate Monica Aug 05 '23 at 17:04
  • If you want access to the `INT_MIN` and `INT_MAX` macro constants, you must `#include ` at the top of the file, because, as already pointed out by someone else, that is the header file in which these macro constants are defined. – Andreas Wenzel Aug 05 '23 at 19:14
  • If you want to get really fancy remember there is no requirement that -1*INT_MAX is equal to INT_MIN. Indeed on all modern platforms it is not. – Persixty Aug 07 '23 at 09:22

1 Answers1

1

There is nothing wrong with your function. It is not failing on your test string, and it is not continuing to iterate after the last digit. It is correctly returning INT_MAX for your input, which is an integer greater than INT_MAX.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158