Reputation: 10238
In a comment to another answer, I was shown a code example that seemingly used printf
and puts
without including <stdio.h>
but the online compiler didn't complain.[1] To understand what's going on, I copied it to my local IDE.
Reduced to relevant includes and output, it's basically this:
#include <string>
#include <map>
#include <optional>
int main() {
printf("Answer %d\n", 42);
puts("Question?");
}
Experimenting with gcc 8.1.0 (packaged with Code::Blocks 20.03), I found out, that the includes can be further reduced to
<string>
or <map>
or <optional>
in C++17 (ISO/GCC)<string>
or <map>
in C++11/C++14 (ISO/GCC)<string>
in C++98 (ISO/GCC)Also, a sample test — C++14 (GCC 8.3) — on ideone.com compiles and runs fine:
#include <iostream>
int main() {
printf("printf without #include <stdio.h>\n");
return 0;
}
This is also true for other definitions from <stdio.h>
like FILE
.
I found no information at cppreference.com
I also tried several web and SO searches but wasn't successful so far.
While it may be handy for small examples to get some powerful functions for free, but a serious project may suffer from it: besides comparatively easy to fix compiler errors, I see the danger of serious runtime errors.
How can I effectively control/prevent this kind of inclusion?
2024 Update:
Even today we can observe this behaviour in GCC 13.2:
#include <string>
int main() {
printf("Hello ");
puts("world!");
}
No complains when compiling above code via gcc -std=c++20 -Wall -Wextra -Wpedantic -Werror.
[1] the referenced code in fact containes the include statement, so I obviously didn't copy all the code. Anyway, the observed behavior is there as described above.
Upvotes: 1
Views: 130
Reputation: 10238
If a header exports an interface that has irreducible dependencies on definitions in other headers, they normally include them. This usually leads to introducing irrelevant portions of the depending-on headers into the compilation unit. So some compilation unit may be compiled without any error, even if it missed to explicitly include the official header(s) for the entities it refers to. And, it might happen as well, that your code fails to compile after switching another compiler or to the next GCC release.
So header dependencies should be seen as one aspect of portability.
In fact, there is a standardized section dedicated to implicit includes, named Header dependency changes that is part of most Porting to GCC $RELEASE documents. See these sections for the last three major GCC releases:
Some C++ Standard Library headers have been changed to no longer include other headers that were being used internally by the library. As such, C++ programs that used standard library components without including the right headers will no longer compile.
The following headers are used less widely in libstdc++ and may need to be included explicitly when compiled with GCC 11:
<limits>
(forstd::numeric_limits
)<memory>
(forstd::unique_ptr
,std::shared_ptr
etc.)<utility>
(forstd::pair
,std::tuple_size
,std::index_sequence
etc.)<thread>
(for members of namespacestd::this_thread
.)
Some C++ Standard Library headers have been changed to no longer include other headers that were being used internally by the library. As such, C++ programs that used standard library components without including the right headers will no longer compile.
The following headers are used less widely in libstdc++ and may need to be included explicitly when compiled with GCC 12:
<memory>
(forstd::shared_ptr
,std::unique_ptr
etc.)<iterator>
(forstd::begin
,std::end
,std::size
,std::istream_iterator
,std::istreambuf_iterator
)<algorithm>
(forstd::for_each
,std::copy
etc.)<utility>
(forstd::pair
)<array>
(forstd::array
)<atomic>
(forstd::atomic
)<ctime>
(forstd::time
,std::mktime
etc.)<pthread.h>
(forpthread_create
,pthread_mutex_t
etc.)
Some C++ Standard Library headers have been changed to no longer include other headers that were being used internally by the library. As such, C++ programs that used standard library components without including the right headers will no longer compile.
The following headers are used less widely in libstdc++ and may need to be included explicitly when compiling with GCC 13:
<string>
(forstd::string
,std::to_string
,std::stoi
etc.)<system_error>
(forstd::error_code
,std::error_category
,std::system_error
).<cstdint>
(forstd::int8_t
,std::int32_t
etc.)<cstdio>
(forstd::printf
,std::fopen
etc.)<cstdlib>
(forstd::strtol
,std::malloc
etc.)
In general, we want to ensure that our code behaves in a well-defined way: What we expect from it should be defined either in its own wording or in the standards it refers to, be it at language level or in terms of the standard libraries we include.
The fact that standard headers in turn include standard headers is/was hardly avoidable, considering that many (template) implementations are offered inline; this means that the headers themselves contain not only the interfaces, but also the implementations. In this respect, it must be accepted that implementation-dependent bindings also have an effect on our code. At the same time, however, these concrete include structures are not the subject of the C++ standards to which we explicitly refer.
The analysis of dependencies in the units of our source code isn't a trivial one. (to-do: elaborate on this in the future…)
As commented by Raildex, there are the GCC options -nostdlib
, -nolibc
, and -nodefaultlibs
to control linking that also affect the compilation.
So if you compile above code with the -nodefaultlibs
option being set,
#include <string>
#include <map>
#include <optional>
int main() {
printf("Answer %d\n", 42);
puts("Question?");
}
you'll get two errors in GCC version 7.3 and above,
<source>:6: undefined reference to `printf'
<source>:7: undefined reference to `puts'
while GCC versions above 9.3 stop compiling at the first of them.
Upvotes: 0
Reputation: 162297
Aside from any arguments about "purity", I don't see how including the declarations for these functions could in any way be harmful. As a matter of fact, not having them and letting the compiler implicitly assuming int printf()
– albeit only C compilers do it, C++ won't – is calling for trouble.
printf
is part of the C language standard. And although C++ isn't C, there's so much overlap between their ecosystems, that C++ considers printf
as part of the implementation runtime environment as well, as it does with all the other functions from the C standard library.
And as such, all of these symbols are reserved. Outside of actually implementing the runtime library itself, you have no business defining symbols of those names yourself.
#include
preprocessor directives are just inline text substitution with text pulled in from an additional file.
(extern
) symbol declarations will not create symbols of those names, but they will make sure, that you're not going to redefine those symbols at your own.
So I really don't see where your worries about runtime errors do come from.
Upvotes: 0
Reputation: 149125
I am afraid you cannot.
The standard requires that the well known include files declare the relevant names, but does not prevent them to include other files/names, if the library implementation finds it useful.
Said differently, after including iostream
you are sure that all the names belonging to it are correctly declared, but you cannot know (except by examining the file itself) if other names have been defined, or if other standard files have been included. Here, your implementation chooses to automatically include stdio.h
, but a different (standard library) implementation could choose not to include it. You have reached the world of unspecifiedness...
Upvotes: 3