Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it allowed to name a global variable `read` or `malloc` in C++?

Consider the following C++17 code:

#include <iostream>
int read;
int main(){
    std::ios_base::sync_with_stdio(false);
    std::cin >> read;
}

It compiles and runs fine on Godbolt with GCC 11.2 and Clang 12.0.1, but results in runtime error if compiled with a -static key.

As far as I understand, there is a POSIX(?) function called read (see man read(2)), so the example above actually invokes ODR violation and the program is essentially ill-formed even when compiled without -static. GCC even emits warning if I try to name a variable malloc: built-in function 'malloc' declared as non-function

Is the program above valid C++17? If no, why? If yes, is it a compiler bug which prevents it from running?

like image 930
yeputons Avatar asked Oct 03 '21 11:10

yeputons


People also ask

Can you declare global variables in C?

The C language allows the redeclaration of the global variable. It means that this variable can get declared again when the first declaration doesn't lead to the initialization of the variable. It is possible because the second program works pretty well in the C language even if the first one fails during compilation.

Can 2 global variable have same name?

In other words, all global variables with the same name will be converted to be one variable - so your int a; and int a = 25; will be referring to the same int -sized piece of memory. Formally called tentative definition in C.

Which operator is used for global variable?

In C++: 2) We can access a global variable if we have a local variable with the same name in C++ using Scope resolution operator (::).

Is it OK to use global variable?

Using global variables causes very tight coupling of code. Using global variables causes namespace pollution. This may lead to unnecessarily reassigning a global value. Testing in programs using global variables can be a huge pain as it is difficult to decouple them when testing.

How to test above global variables in different program in C?

First let us create a C program that contains only global variables, save the below program with name global.c. Next, let us write main program to test above global variables in different program, save the below program with name main.c.

What is global scope in C with example?

Global scope. By default, global variables are of global scope. Which means we can access a global variable everywhere in same as well as other C programs (using extern). Example program to use global scope variables. First let us create a C program that contains only global variables, save the below program with name global.c.

What happens when a global variable is not initialized in C++?

In C++, both programs fail in compilation. C allows a global variable to be declared again when first declaration doesn’t initialize the variable. The below program fails in both C also as the global variable is initialized in first declaration itself.

How to restrict access of a global variable only to functions?

In programming, there exists situations when you want to restrict access of a global variable only to all functions of the same program. Static scope global variables has all properties of a global variable, except they are accessible only to all functions of same program. They are declared with static keyword.


Video Answer


2 Answers

The code shown is valid (all C++ Standard versions, I believe). The similar restrictions are all listed in [reserved.names]. Since read is not declared in the C++ standard library, nor in the C standard library, nor in older versions of the standard libraries, and is not otherwise listed there, it's fair game as a name in the global namespace.

So is it an implementation defect that it won't link with -static? (Not a "compiler bug" - the compiler piece of the toolchain is fine, and there's nothing forbidding a warning on valid code.) It does at least work with default settings (though because of how the GNU linker doesn't mind duplicated symbols in an unused object of a dynamic library), and one could argue that's all that's needed for Standard compliance.

We also have at [intro.compliance]/8

A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.

We can consider POSIX functions such an extension. This is intentionally vague on when or how such extensions are enabled. The g++ driver of the GCC toolset links a number of libraries by default, and we can consider that as adding not only the availability of non-standard #include headers but also adding additional translation units to the program. In theory, different arguments to the g++ driver might make it work without the underlying link step using libc.so. But good luck - one could argue it's a problem that there's no simple way to link only names from the C++ and C standard libraries without including other unreserved names.

(Does not altering a well-formed program even mean that an implementation extension can't use non-reserved names for the additional libraries? I hope not, but I could see a strict reading implying that.)

So I haven't claimed a definitive answer to the question, but the practical situation is unlikely to change, and a Standard Defect Report would in my opinion be more nit-picking than a useful clarification.

like image 74
aschepler Avatar answered Oct 16 '22 07:10

aschepler


Here is some explanation on why it produces a runtime error with -static only.

The https://godbolt.org/z/asKsv95G5 link in the question indicates that the runtime error with -static is Program returned: 139. The output of kill -l in Bash on Linux contains 11) SIGSEGV (and 128 + 11 = 139), so the process exits with fatal signal SIGSEGV (Segmentation fault) indicating invalid memory reference. The reason for that is that the process tries to run the contents (4 bytes) of the read variable as machine code. (Eventually std::cin >> ... calls read.) Either somethings fails in those 4 bytes accidentally interpreted as machine code, or it fails because the memory page containing those 4 bytes is not executable.

The reason why it succeeds without -static is that with dynamic linking it's possible to have multiple symbols with the same name (read): one in the program executable, and another one in the shared library (libc.so.6). std::cin >> ... (in libstdc++.so.6) links against libc.so.6, so when the dynamic linker tries to find the symbol read at program load time (to be used by libstdc++.so.6), it will look at libc.so.6 first, finding read there, and ignoring the read symbol in the program executable.

like image 41
pts Avatar answered Oct 16 '22 08:10

pts