2

The main problem I am facing here is that strtoll() is flagged as an error in VC 2010 (error C3861: 'strtoll': identifier not found). Will it do the same thing if I replace it with strtol()?

unsigned int get_uintval_from_arg(int argc, int index, char **argv,
                                  unsigned int lower_bound, unsigned int upper_bound) 
{  
    unsigned int return_val=0;

    if (index + 1 <= argc - 1)
    {
        return_val=(unsigned int)strtoll(argv[index+1],NULL,10);
        if (errno == EINVAL || errno== ERANGE) 
        {
            fprintf(stderr, "Could not parse argument %s for switch %s!\n",
                    argv[index], argv[index+1]);
            return 0;
        }
    }
    // ....... I will post the remaining part of the code if necessary 
    .......
}
M.M
  • 138,810
  • 21
  • 208
  • 365
John
  • 794
  • 2
  • 18
  • 34
  • 1
    Did you `#include ` in your program? Since a `long long` is so much longer than an `unsigned long`, why are you trying to use the `long long` variant? – sarnold Nov 23 '11 at 08:21
  • 2
    `long long` is part of the latest ISO 9899:99 C standard, "C99", which is not supported by Microsoft. Visual Studio only supports the twenty-one years old C standard from 1990. – Lundin Nov 23 '11 at 09:45

4 Answers4

5

Since your return_val is an unsigned int, you should probably be using strtoul() which has been standard since C89 and is therefore supported by MSVC (whereas strtoll() has only been standard since C99 and is not supported by MSVC).

Your testing of the error conditions is not adequate. You need to set errno to zero before calling the conversion function; you also need to detect whether an error was reported, which is trickier than it seems.

Section §7.20.1.4 'The strtol, strtoll, strtoul, and strtoull functions' of the C99 standard says:

Returns

The strtol, strtoll, strtoul, and strtoull functions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno.

You also have to read the look at the value stored in the endptr parameter to the conversion functions to tell that no conversion was performed (as opposed to a valid zero was converted).

If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a null pointer.

So, you must write code more like this (omitting the test against EINVAL because the standard does not mention these functions setting errno to EINVAL):

unsigned int return_val=0;

if (index + 1 <= argc - 1)
{
    char *end;
    unsigned long ul;
    errno = 0;
    ul = strtoul(argv[index+1], &end, 10);
    if ((ul == 0 && end == argv[index+1]) ||
        (ul == ULONG_MAX && errno == ERANGE) ||
        (ul > UINT_MAX))
    {
        fprintf(stderr, "Could not parse argument %s for switch %s!\n",
                argv[index], argv[index+1]);
        return 0;
    }
    retval = (unsigned int)ul;
}

Note that this is simpler than the test for a signed integer conversion which must take into account the negative <type>_MIN limit as well as the <type>_MAX limit.

Also note that you really should record the result in an unsigned long and then check whether it fits within your designated range, which may be limited to UINT_MAX (which can be less than ULONG_MAX in a Unix-like 64-bit environment).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • also, you should check for `retval > UINT_MAX` as well (before downcasting to `unsigned int`), in case it's on a 64-bit long platform. – bdonlan Nov 23 '11 at 08:39
  • @bdonlan: yes - thanks (to both comments). I fixed the downcast with the last paragraph without seeing your comment (but the first edition of the answer didn't include that, and your comment is spot on). I had missed my '=' instead of '==' typo (but the compiler would have warned me about it had I compiled the code). – Jonathan Leffler Nov 23 '11 at 08:41
  • You've fixed the downcast but left in the ul > UINT_MAX test which will now always be false. – David Conrad Dec 18 '13 at 19:21
  • @DavidConrad: why do you think that test will always be false? On a 64-bit (Unix) machine, it is trivial to have an `unsigned long` value that is much larger than will fit in `unsigned int`. On Windows 64 or a 32-bit system, you're correct; however, I believe the code will work correctly on those too — though a compiler might decide to warn about the test. If that was a problem, I'd arrange for conditional compilation, probably look for `ULONG_MAX != UINT_MAX` and only making the comparision if the two are different. – Jonathan Leffler Dec 18 '13 at 20:30
  • Whoops! Sorry, I was thinking ULONG_MAX, not UINT_MAX. My mistake. – David Conrad Dec 18 '13 at 20:35
4

In Visual studio use _strtoi64() method instead. It has the same parameters as strtoll.

For compatibility you can simply use a define to wrap it as strtoll (if you need portability) such as

#if defined(_MSC_VER)
#define strtoll _strtoi64
#endif
Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88
  • @PaulR Yeah he said specifically VS though. If he wants it to be portable he should use defines to redefine _strtoi64 as strtoll on microsoft c compiler. – Jesus Ramos Nov 23 '11 at 08:24
  • @bdonlan Yeah I noticed afterwards since i had issues porting some code to VS C compiler before I kind of ignored what he was doing and just told him why strtoll wasn't working. – Jesus Ramos Nov 23 '11 at 08:25
  • @JesusRamos, see my comment on wallyk's post - there is a reason for it actually – bdonlan Nov 23 '11 at 08:27
0

strtol() is the proper library function for a long. For portability, declare return_val as a long or unsigned long. If the latter, see if MSVC offers strtoul().

wallyk
  • 56,922
  • 16
  • 83
  • 148
  • Yeah he's using stroll for long long int, but casts down to unsigned int... I kind of skipped over that heh. – Jesus Ramos Nov 23 '11 at 08:23
  • If `long` and `int` are the same size, this is a reasonable way to parse values between `INT_MAX+1..UINT_MAX`. – bdonlan Nov 23 '11 at 08:25
  • @bdonlan: assuming `long` and `int` are the same size is short-sighted. It is quite simple and easy to write it portably: that should be emphasized, not taking an environment-sensitive lazy shortcut, which will easily cost significant time when it eventually breaks. – wallyk Nov 23 '11 at 08:28
  • i have tried it with strol() and it works, but I am not sure if it can do the same thing as stroll() – John Nov 23 '11 at 08:30
  • 1
    @wallyk, it's not quite that easy, actually - using it directly gives wrong answers for values greater than `LONG_MAX` (clamped to `LONG_MAX`; see http://codepad.org/nu9L2AED); and all this code assumes is that `long long` is larger than `int`; it DOES NOT make the assumption that `long == int`. – bdonlan Nov 23 '11 at 08:32
  • @bdonlan: I amended my answer a few minutes before your comment to mention `strtoul` which addresses the `LONG_MAX` issue. Downcasting a `long long` to a `long` in `C` will toss the most significant bits. To be completely proper, a check should be made instead of truncating. – wallyk Nov 23 '11 at 08:36
0

For your purposes, you should use strtoul, as you are converting to an unsigned long type, not a signed long long type (which is what strtoll would do). If you need to read in long longs, use the Microsoft-specific _strtoi64; you can use #defines to rename it to strtoll on Microsoft platforms, or write a wrapper function in a Windows-specific portability shims file (with your own prototype for it in a private header).

bdonlan
  • 224,562
  • 31
  • 268
  • 324