Is there a way instruct the the compiler that:
The language is C90
The declarations of stdio.h are those of C99 (including snprintf)
With cc -std=c90 -Wall (on a source file using snprintf), an annoying warning is issued (sometimes, depending on the compiler/environment, with a confusing hint that stdio.h should be included, even if it already is), but the linker finds snprintf anyway. I understand what is happening, that is not the question.
Using strict c90 (language) and snprintf (library function) is technically well possible, but I do not know how to instruct the compiler accordingly. In other words, I would like to distinguish between language compliance and library compliance.
Remark: I assume that the language used in the C99 stdio.h header file is actually C90, and that it could thus be included by a C90 source file. Does anything prevent it?
I would prefer a solution with a generic include <stdio.h>, for the sake of portability.
Yes, though it's a bit of a weird thing to do.
You can define _ISOC99_SOURCE to signal that you want C99 functions to be defined.
Example:
#define _ISOC99_SOURCE
#include <features.h>
#include <stdio.h>
int main() {
char buf[10];
snprintf(buf, 4, "foo");
puts(buf);
return 0;
}
Compile it like so:
$ gcc -std=c90 -Wall test.c
$ ./a.out
foo
This will work in gcc 10.3.0 and clang 11.0.0. In terms of library compatibility, it has only been tested with glibc, and not musl or other versions of the standard library.
No.
A generic #include <stdio.h> for a C90 compiler needs not declare snprintf.
Before you argue that it's not a C90 compiler, keep in mind that you are requesting that it acts as one usng -std=c90. A compiler acting as a C90 compiler needs not do anything that a C90 compiler wouldn't do.
There is therefore no generic solution.
There may be compiler-specific solutions, including adding the following:
int snprintf( char * buffer, size_t bufsz, const char * format, ... );
Related
THIS QUESTION IS NOT HOW TO REMOVE THE WARNING
I am writing a shell. I referred this source. I used the same headers (in the same order), as he did, in my code.
When compiling his code, I do not get any warnings for implicit declaration of getline. But when I compile mine, it does get thrown.
The man page suggests to use #define _GNU_SOURCE, and adding that removed the warning from my code.
So why was no warning thrown for the code in the blog, as he did not use #define _GNU_SOURCE?
Here is the minimal code (I copied all the headers as I mentioned above)
// #define _GNU_SOURCE
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
ssize_t bytes_read;
size_t input_buffer_size = 1024;
char *user_input = (char *)malloc(input_buffer_size * sizeof(char));
while (1)
{
printf("> ");
bytes_read = getline(&user_input, &input_buffer_size, stdin);
printf("%s\n", user_input);
}
return 0;
}
And here is the compilation process I used...
gcc -std=c11 -o bin/shell src/shell.c
Here is the error that I get if I leave the first line commented.
src/shell.c: In function ‘main’:
src/shell.c:18:18: warning: implicit declaration of function ‘getline’ [-Wimplicit-function-declaration]
18 | bytes_read = getline(&user_input, &input_buffer_size, stdin);
| ^~~~~~~
It appears that the person who wrote the tutorial you're referring to, did not supply any special compilation options when they were testing their code. I see only one compilation command anywhere on that page, and it is gcc -o main main.c. Thus, they got GCC's defaults, which, typically, make getline available on computers that have it.
You, however, used the compiler flag -std=c11 when you compiled your code. One of the effects of this flag is that GCC directs the C library's headers to declare only the functions, constants, variables, etc. that are specified by ISO C2011. (Depending on which C library you're using, this directive may or may not have any effect — but Ubuntu uses the GNU C library, which implements it thoroughly.) getline is not part of ISO C2011, so it is not declared and you get an "implicit declaration" diagnostic when you try to use it.
Use of the hyperconformant -std=cXX modes is almost always a mistake. There are exactly three differences between -std=cXX and -std=gnuXX and none of them is desirable in practice:
As discussed above, it directs the headers not to declare anything that's not part of the specified revision of ISO C. As you saw for yourself, this is almost never what you want when writing a nontrivial C program. It also has a nasty tendency to break library headers — both third-party headers and the C library's own headers — because they are rarely, if ever, tested in this mode.
It disables "system-specific predefined macros" that pollute the user namespace (e.g. linux, unix, arm). This is abstractly desirable but, like #1, has a nasty tendency to break library headers that are rarely, if ever, tested in this mode.
It enables trigraphs, which are a kludge to make C work with "national variants" of ASCII that are missing some punctuation. These are so rarely used and cause so much practical confusion that they were actually stripped out of C++ 2017 (not C 2017, though).
To compile your own code with a reasonably picky level of conformance diagnostics, but not risk breaking library headers, there is a better combination of options:
cc -std=gnuXX -g -Og -Wall -Wextra -Wpedantic -Wstrict-prototypes -Wwrite-strings
(Pick a suitable XX; if you have no reason to choose anything else, I'd go with 11.) You may or may not want to add a -D switch for one of the _xxx_SOURCE feature selection macros; explaining how those work and how to choose one is a whole question in itself.
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!
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.
I am trying to use the open_memstream function in my C code. However I cannot seem to compile it. Minimal working example as follows:
#include <stdio.h>
int main(void) {
char *buf;
size_t sz;
FILE *stream = open_memstream(&buf, &sz);
putc('A', stream);
fclose(stream);
}
And I also use the following command to compile it:
gcc -std=c99 -o test test.c
After some research, I found that I need to define a macro before I include stdio.h. However the following example code was to no avail.
#define __USE_POSIX
#define __USE_XOPEN
#include <stdio.h>
The following compiler warnings are thrown; I assume the second warning is because of the first one.
test.c:7:17: warning: implicit declaration of function ‘open_memstream’ [-Wimplicit-function-declaration]
FILE *stream = open_memstream(&buf, &sz);
^
test.c:7:17: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
The __USE_* macros are internal to glibc's headers, and defining them yourself does not work. You should instead do one of the following:
Compile your program with -std=gnu11 instead of -std=c99 and don't define any special macros. This is the easiest change. Conveniently, -std=gnu11 is the default with newer versions of GCC.
If you have some concrete reason to want to select an old, strict conformance mode, but also you want POSIX extensions to C, then you can use the documented POSIX feature selection macros:
#define _XOPEN_SOURCE 700
or
#define _POSIX_C_SOURCE 200809L
These must be defined before including any standard headers. The difference is that _XOPEN_SOURCE requests an additional set of features (the "XSI" functions). See the Feature Test macros section of the glibc manual for more detail.
Note that if you need to request strict conformance mode from the library, using a -std=cXX option, then you almost certainly also want to use the -Wall and -Wpedantic options to enable strict conformance checking for the language. (You should use at least -Wall even if you don't need strict conformance checking.)
My compiler (gcc) throws warnings (not errors!) on the line which declares fp:
int fd = open("filename.dat", O_RDONLY);
FILE* fp = fdopen(fd, "r"); // get a file pointer fp from the file descriptor fd
These are the warnings:
main.c: In function ‘main’:
main.c:606: warning: implicit declaration of function ‘fdopen’
main.c:606: warning: initialization makes pointer from integer without a cast
I do not understand these warnings since the return value of fopen is a FILE*. What is the mistake I am making here?
EDIT: I am including stdio.h (and I am also on Linux).
Short answer: use -std=gnu99 when compiling, the usual standard is non-POSIX and does not have fdopen.
warning: implicit declaration of function ‘fdopen’
Means you have forgot to include the header file which the declaration of fdopen() resides in. Then an implicit declaration by the compiler occurs - and that means the return value of the unknown function will be assumed to be int - thus the second warning. You have to write
#include <stdio.h>
Edit: if you properly include stdio.h, then fdopen() might not be available on the system you're targeting. Are you on Windows? This function is POSIX-only.
Edit 2: Sorry, I really should have perceived this. C99 means the ANSI C99 standard - and standard C doesn't force the concept of file descriptors in order to support non-POSIX systems, so it provides fopen() only. fdopen() is related to file descriptors, so it's POSIX-only, so it's not part of standard C99. If you use the -std=gnu99 switch for GCC, it gets rid of the standard's restrictions and lets in the POSIX and GNU-only extensions, essentially fixing your problem.
#define _XOPEN_SOURCE 600
#include <stdio.h>
This conforms perfectly with strict c99
gcc -std=c99 -pedantic -Wall -Wextra -Werror
You are not including #include <stdio.h> in C the compiler therefore "guesses" the declaration of the function you're trying to call. (Taking the parameters you've based and using int as return value). Usually you don't want such guesses therefore the compiler warns you.
Solution: Add proper #includes.
The fdopen function is not part of the C standard and is not available as part of the standard headers if you compile in standard C mode. So you either need to use -std=gnu99 instead of -std=c99 to compile your source or declare the function yourself.
There's a good explanation for the compiler's diagnostic in #H2CO3's answer, so let's only look on the why of things: if you're using glibc (and you probably are), certain POSIX functions may require specific feature test macros to show up.
In particular, you may need to put the following line:
#define _POSIX_SOURCE
// or #define _XOPEN_SOURCE
before
#include <stdio.h>
Certain compilers (such as gcc) also have command line options to the same effect (all the gnu* standards options in gcc).
I'm pretty sure there's such question, but I can't find it :\ Anyway, here's the issue:
What is the difference between wait in stdlib.h and sys/wait.h o.O ?
In details - I just encountered this problem and I could't compile a simple C program. I isolated the problem and here's what I got:
#include <stdlib.h>
//#include <sys/wait.h>
int main()
{
int status;
wait( &status );
return 0;
}
If stdlib.h is included, I got:
$ gcc asd.cpp
asd.cpp: In function ‘int main()’:
asd.cpp:9:16: error: conflicting declaration ‘wait& status’
asd.cpp:8:6: error: ‘status’ has a previous declaration as ‘int status’
What declaration ? O.o What is wait here, that conflicts with int status?
I found a thread in the net, where replacing stdlib.h with sys/wait.h solves the problem, but why is that and what is the difference?
EDIT: Thanks to sidyll's comment, I changed the file extention - from .cpp to .c and it worked! I'm shocked :) How is this so different? And still the same question - what is the different between those two wait-s ?
The difference is that the wait() in <sys/wait.h> is the one you should use.
From the wait(3) man page:
SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
The wait function isn't defined by the ISO C standard, so a conforming C implementation isn't allowed to declare it in <stdlib.h> (because it's legal for a program to use the name wait for its own purposes). gcc with glibc apparently does so in its default non-conforming mode, but if you invoke it with gcc -ansi -pedantic or gcc -std=c99 -pedantic, it doesn't recognize the function name wait or the type pid_t.
I did gcc -E wait.cpp to dump the actual preprocessor expansions that take place. What I found was that on linux, the header /usr/include/bits/waitstatus.h is included which pulls in a union wait { ... } but the function wait() from sys/wait.h is never pulled in. The same thing happens with the c compilation, but the for whatever reason the compiler does not complain in that case.
To prove this to yourself, you can change your main to declare the wait as a variable rather than a function call, and the compiler will not complain:
int main() {
int status;
wait w;
return 0;
}
Note that GCC stands for GNU Compiler Collection, not GNU C Compiler (as many
other tools which were prefixed with a g). It's not a C-only compiler. And
many languages are detected by file extensions. Adam Rosenfield is partialy
correct in his comment. Yes, g++ will add the C++ library in the linker phase,
but that's not the unique difference (more on this later).
To explain how changing the extension solved it, please take a look in this text
straight from GCC's manual:
Compiling C++ Programs
C++ source files conventionally use one of the suffixes.C, .cc, .cpp,
.CPP, .c++, .cp,or.cxx;C++ header files often use.hhor.H;and
preprocessed C++ files use the suffix .ii. GCC recognizes files with
these names and compiles them as C++ programs even if you call the
compiler the same way as for compiling C programs (usually with the
namegcc).
So, "GCC regocnizes files with these names" and your program was being compiled
as C++ source. I guess that C++ has some special use of &, which I can't tell
exactly (I don't know C++). Hence the error.
Now, regarding the difference between g++ and gcc, continue with the next
paragraph:
However, the use ofgccdoes not add the C++ library.g++is a program
that calls GCC and treats.c, .hand.ifiles as C++ source files
instead of C source files unless-xis used, and automatically
specifies linking against the C++ library. This program is also useful
when precompiling a C header file with a.hextension for use in C++
compilations. On many systems,g++is also installed with the name
c++.
On the real question: there aren't two waits here in my system (Darwin 11), only
the standard syscall. Check if what Kevin said isn't happening. It's the same,
stdlib.h includes sys/wait.h:
#include <_types.h>
#if !defined(_ANSI_SOURCE)
#include <sys/wait.h>
#if (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))
#include <alloca.h>
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#endif /* !_ANSI_SOURCE */
Check your header.