Issue
I have been reading through the documentation for strtoul()/strtoull() from here, and under the "Conforming To" section towards to bottom, it makes these two points:
strtoul(): POSIX.1-2001, POSIX.1-2008, C89, C99 SVr4.
strtoull(): POSIX.1-2001, POSIX.1-2008, C99.
These two lines, in addition to other references throughout the document indicate to me that the function strtoull should not be available when compiling a program using the c89/c90 standard. However, when I run a quick test with gcc, it allows me to call this function, regardless of the standard that I specify.
First, the code I am using to test:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
unsigned long long x;
const char *str = "1234";
x = strtoull(str, NULL, 10);
printf("%llu\n", x);
return 0;
}
And here is my compilation command:
gcc test.c -std=c89 -pedantic -Wall -Wextra
Now, in fairness it does warn me of the compatibility issue:
test.c: In function ‘main’:
test.c:6:16: warning: ISO C90 does not support ‘long long’ [-Wlong-long]
unsigned long long x;
^~~~
test.c:9:6: warning: implicit declaration of function ‘strtoull’; did you mean ‘strtoul’? [-Wimplicit-function-declaration]
x = strtoull(str, NULL, 10);
^~~~~~~~
strtoul
test.c:11:9: warning: ISO C90 does not support the ‘ll’ gnu_printf length modifier [-Wformat=]
printf("%llu\n", x);
^~~~~~~~
These warning messages are exactly what I would expect given the documentation. It notifies me that the function I have specified cannot be found, and even that the C90 standard doesn't support unsigned long long
. However, when I attempt to run this code, it works just fine, with no crashing or other types of errors. It prints the value 1234
, as desired. So, based on this experiment, I have a few questions that I was hoping someone more seasoned than I could answer.
- Is this a matter of me not providing the necessary compilation flags to enforce the 'strict' c98 standard?
- Is this a case of me misunderstanding the documentation, or is there some documentation for gcc itself that I should refer to? And, if so, where could I find it?
- Is there something fundamental about the compiling/linking process that I am not understanding, which explains this issue?
- Why would I be warned of an incompatibility, even warned that the function I am calling does not exist, but the code still works with no issue?
- Does this experiment imply that the
-std=c89 -pedantic
flags do not actually enforce the C89/C90 standard?
As a final note, I am not trying to say I want to use this function in C89, I was just curious about the compatibility restriction, and then confused about the answer.
Thanks in advance for any responses!
Solution
From a C89/C90 compiler's point of view, the only thing wrong with your code is the use of unsigned long long
which looks like a syntax error. The standard requires only that the compiler produce a "diagnostic" in this case, and GCC has done so with its "ISO C90 does not support long long
" warning. There is no requirement that this error should be fatal, and the compiler can decide to handle the code some other way if it wants. GCC obviously chooses to understand it as the long long
type which it supports as an extension to C89.
The use of strtoull
then just looks like some function that you made up, as C89 had no way of knowing that this name would be special in some future version of the standard. (Well, they did specify that more functions starting with str
could be added to <string.h>
in the future, but that doesn't make your code illegal for C89.) You haven't declared it, but C89 allowed implicit declarations, so it's understood to be declared as int strtoull();
, i.e. returning int
and with unspecified arguments. AFAIK no diagnostic was required for implicit declarations, but GCC chooses to issue one anyway. So it's treated like any other call to a function not defined in this source file, and the compiler presumes that some other part of your program (including the libraries you use) will define it.
And in fact some other part of your program does define it, namely libc, since your libc conforms to C99 and later. (You know, hopefully, that libc is not part of GCC.) C library authors generally don't provide a version of the library that only includes functions from a particular standard version, since having so many different libraries around would be awkward and inefficient. So linking succeeds.
Note, though, that because of the implicit declaration, the program may not actually work correctly. The compiler will generate code incorrectly assuming that strtoull
returns int
, which depending on your system's calling conventions, may cause all sorts of problems. On x86-64, it means that your program will only look at the low 32 bits of the result and will sign-extend them to 64 bits. So if you try to convert a number that fits in 32 bits but would not fit in long long
, you'll get the wrong result. Example.
If you want a program that would work on a system that only supports C89 and nothing else, it's your responsibility to look at the diagnostics issued by the compiler and fix the corresponding problems. The -pedantic-errors
option mentioned in comments can help with this, as it causes compilation to fail when such diagnostics are issued.
It would also help if you could find a C89-only libc, but that's not GCC's problem. But its implicit declaration warnings do give you some assistance in noticing that you have called a function which you may not have intended for your program to define.
As a final point, it's historically been part of GCC's design philosophy that they don't think "enforcing the standard" is really part of what they want to do. They saw their goal as writing a compiler that helps people write and compile programs that are useful, not a linter that checks for conformance with coding standards; they figured the latter should be a separate project, and not one that they were interested in. As such, they were liberal in providing extensions to the standard language, and not particularly diligent in providing ways for programs to avoid using them. They did provide the -pedantic
option but apparently with some reluctance, as you can tell from the derogatory name.
Answered By - Nate Eldredge Answer Checked By - Senaida (WPSolving Volunteer)