In C++, it is possible for pointer values to be compile-time constants. This is true, otherwise, non-type template parameters and constexpr won't work with pointers. However, as far as I know, addresses of functions and objects of static storage are known (at least) at link-time rather than compile-time. Following is an illustration:
main.cpp
#include <iostream>
template <int* p>
void f() { std::cout << p << '\n'; }
extern int a;
int main() {
f<&a>();
}
a.cpp
int a = 0;
I'm just wondering how the address of a could possibly be known when compiling main.cpp. I hope somebody could explain this a little to me.
In particular, consider this
template <int* p, int* pp>
constexpr std::size_t f() {
return (p + 1) == (pp + 7) ? 5 : 10;
}
int main() {
int arr[f<&a, &b>()] = {};
}
How should the storage for arr be allocated?
PLUS: This mechanism seems to be rather robust. Even when I enabled Randomized Base Address, the correct output is obtained.
The compiler doesn't need to know the value of &a at compile time any more than it needs the value of function addresses.
Think of it like this: the compiler will instantiate your function template with &a as a parameter and generate "object code" (in whatever format it uses to pass to the linker). The object code will look like (well it won't, but you get the idea):
func f__<funky_mangled_name_to_say_this_is_f_for_&a>__:
reg0 <- /* linker, pls put &std::cout here */
reg1 <- /* hey linker, stuff &a in there ok? */
call std::basic_stream::operator<<(int*) /* linker, fun addr please? */
[...]
If you instantiate f<b&>, assuming b is another global static, compiler does the same thing:
func f__<funky_mangled_name_to_say_this_is_f_for_&b>__:
reg0 <- /* linker, pls put &std::cout here */
reg1 <- /* hey linker, stuff &b in there ok? */
call std::basic_stream::operator<<(int*) /* linker, fun addr please? */
[...]
And when your code calls for calling either of those:
fun foo:
call f__<funky_mangled_name_to_say_this_is_f_for_&a>__
call f__<funky_mangled_name_to_say_this_is_f_for_&b>__
Which exact function to call is encoded in the mangled function name.
The generated code doesn't depend on the runtime value of &a or &b.
The compiler knows there will be such things at runtime (you told it so), that's all it needs. It'll let the linker fill in the blanks (or yell at you if you failed to deliver on your promise).
For your addition I'm afraid I'm not familiar enough about the constexpr rules, but the two compilers I have tell me that this function will be evaluated at runtime, which, according to them, makes the code non-conforming. (If they're wrong, then the answer above is, at least, incomplete.)
template <int* p, int* pp>
constexpr std::size_t f() {
return (p + 1) == (pp + 7) ? 5 : 10;
}
int main() {
int arr[f<&a, &b>()] = {};
}
clang 3.5 in C++14 standards conforming mode:
$ clang++ -std=c++14 -stdlib=libc++ t.cpp -pedantic
t.cpp:10:10: warning: variable length arrays are a C99 feature [-Wvla-extension]
int arr[f<&a, &b>()];
^
1 warning generated.
GCC g++ 5.1, same mode:
$ g++ -std=c++14 t.cpp -O3 -pedantic
t.cpp: In function 'int main()':
t.cpp:10:22: warning: ISO C++ forbids variable length array 'arr' [-Wvla]
int arr[f<&a, &b>()];
As far as I know, the variables of static storage and functions are stored simply as symbols/place holders in the symbol table while compiling. It is in the linking phase when the place holders are resolved.
The compiler outputs machine code keeping the placeholders intact. Then the linker replaces the placeholders of the variables / functions with their respective memory locations. So in this case too, if you just compile main.cpp without compiling a.cpp and linking with it, you are bound to face linker error, as you can see here http://codepad.org/QTdJCgle (I compiled main.cpp only)
Related
I found this situation in my recent project. I wanna ask if it is designed as so, what's the underlying mechanism, and how is it useful?
Although I know the subscript in parameters list is somehow useless to compiler, but in my situation, it might be better to raise an error and stop compile.
The source code:
#include <cstdio>
template<typename Type>
class A{
public:
// passes compile, although it is not declared
void print(int data[Type::len]) { // Line 7
printf("%d\n", data[0]);
}
// error: not declared
// void print(int data[dummy]) {
// printf("%d\n", data[0]);
// }
};
// error: not declared
// void print(int data[A<double>::len]) {
// printf("%d\n", data[0]);
// }
int main() {
A<int> a;
int x[12] = { 0 };
a.print(x); // Line 23
return 0;
}
Compile command using gcc 11.3.0:
g++ -o a a.cpp -Wall -Wextra
No compile output, program prints a "0" and exits with 0.
But in msvc 19.33.31630, it raises C2825, C2510 on line 7, and C2670 on line 23.
There should be an error and that there isn't one is a known bug in GCC.
If the array bound Type::len was valid, then the type int[Type::len] in the function parameter would be rewritten to int* in the instantiation, as is always the case for array types in function parameters. So the actual value of the array bound will not matter from there on out.
However, if Type::len is not a valid constant expression with a suitable value for an array bound, then substitution should fail and the program should be ill-formed. In case of T=int, Type::len is not a valid expression at all and so it should fail to compile (or at least issue some diagnostic). This should happen already at A<int> a; (which causes implicit instantiation of A<int> including A<int>::print's declaration), even if no actual call to print is present.
It seems GCC is performing the rewriting step too early without verifying that the expression in the array bound is actually valid in the substitution.
GCC has a meta-bug report with multiple linked individual bug reports related to this here.
I have the following code in c++:
#include <iostream>
typedef struct Pair{
int x;
int y;
}Pair;
void dumFun(Pair p){}
int main() {
Pair p;
if (0){
p = {1,2};
}
dumFun(p);
return 0;
}
When I compiled the code, I expected to get a warning for the line with dumFun(p) since I'm calling a function with an uninitialized variable.
What I actually want is that my Makefile will give me warning for uninitialized scalar variable issues that I see with the tool Coverity.
Tried to use flag -Wall and I thought it shows warnings for unused variables usage as this - but it doesn't.
Is there any flag to use on a Makefile that will show me warning for the line I wrote above?
You did not tell the compiler, but at least in Clang there is no warning that would catch this. -Weverything shows all possible warnings, and you can use it to find the specific parameter needed to trigger each warning. Demonstration with -Weverything shows no warnings.
I did not find a suitable parameter in GCC either.
The following code compiles just fine and I'm not sure why. Can someone please explain to me why this is legal?
I am using g++ (Debian 6.1.1-10) 6.1.1 20160724 to compile.
#include <iostream>
int sum(int x, int y) { return x + y; }
int main(int argc, char *argv[])
{
using std::cout;
int (*) (int, int) = ∑
cout << "what" << '\n';
}
Addendum
The following program compiles fine using g++ version 5.4.0 but fails to compile in gcc.
int main()
{
int (*) = 20;
}
It's very likely to be related to this bug reported by Zack Weinberg:
Bug 68265 - Arbitrary syntactic nonsense silently accepted after 'int (*){}' until the next close brace
(From Why does this invalid-looking code compile successfully on g++ 6.0? :)
The C++ compiler fails to diagnose ill-formed constructs such as
int main()
{
int (*) {}
any amount of syntactic nonsense
on multiple lines, with *punctuation* and ++operators++ even...
will be silently discarded
until the next close brace
}
With -pedantic -std=c++98 you do get "warning: extended initializer
lists only available with -std=c++11 or -std=gnu++11", but with
-std=c++11, not a peep.
If any one (or more) of the tokens 'int ( * ) { }' are removed, you do
get an error. Also, the C compiler does not have the same bug.
Of course, if you try int (*) (int, int) {} or other variants, it erroneously compiles. The interesting thing is that the difference between this and the previous duplicate/bug reports is that int (*) (int, int) = asdf requires asdf to be a name in scope. But I highly doubt that the bugs are different in nature, since the core issue is that GCC is allowing you to omit a declarator-id.
[n4567 §7/8]: "Each init-declarator in the init-declarator-list
contains exactly one declarator-id, which is the name declared by
that init-declarator and hence one of the names declared by the
declaration."
Here's an oddity:
int (*) (int, int) = main;
In this specific scenario, GCC doesn't complain about taking the address of main (like arrays, &main is equivalent to main).
This question already has answers here:
What is the strict aliasing rule?
(11 answers)
Closed 9 years ago.
In file included from /usr/local/Qt/linux-g++/include/QtCore/QLinkedList:2,
from /home/bamboo/Packages/Parser.h:17,
from /home/bamboo/Packages/Module.cpp:6:
/usr/local/Qt/linux-g++/include/QtCore/qlinkedlist.h: In member function 'void QLinkedList<T>::clear() [with T = int]':
/usr/local/Qt/linux-g++/include/QtCore/qlinkedlist.h:294: error: dereferencing pointer 'y' does break strict-aliasing rules
/usr/local/Qt/linux-g++/include/QtCore/qlinkedlist.h:293: note: initialized from here
/usr/local/Qt/linux-g++/include/QtCore/qlinkedlist.h:294: error: dereferencing pointer 'y' does break strict-aliasing rules
/usr/local/Qt/linux-g++/include/QtCore/qlinkedlist.h:293: note: initialized from here
where in big lines class Module contains a member template like: Parser<int> and the class parser is defined:
template <typename T> class Parser
{
// some stuff
QLinkedList<T> stuff;
};
and this piece of code compiles nicely with gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3 and nicely with g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2 and fails with g++ (Debian 4.4.5-8) 4.4.5 ... and I have no idea why? Anyone has seen this error message and anyone know what this might mean?... and more importantly how to solve it?
Aliasing means that a pointer int *i points to the same address as double *d.
So if
int i = 5;
int *pi = &i:
double *d = pi;
here d is aliasing pi.
this is in c99 >illegal<
I don't know how exactly c++ treats it, but I can't imagine it is welcome.
If you want to test a code where it will be getting funny, try this code in different optimisation levels.
You will get differen results with gcc 4.2
uint32_t anint;
int main(int arg, char** argv)
{
foo ((uint64_t *)&anint);
return 0;
}
void foo (uint64_t *dblptr)
{
anint = 88;
*dblptr = 86;
dosmthng (anint);
}
void dosmthng (uint32_t val)
{
printf ("%d\r\n", val);
}
if you do -O2 or higher the output will be 88. because the compiler expects you to respect the strict-aliasing rule and expects *dblptr as never been used in this code, and jsut takes the line out.
If you any way see no way of working wihtout aliasing, you can give the compiler the param -fno-strict-aliasing. This forces GCC to do not any optimisation based on this expection.
But anyway in C it is not strict ISO C code if you do wrong type punning.
(So if it may ease you, a lot of C code on famous programms gets compiled with -fno-strict-aliasing)
Is it possible to see what is going on behind gcc and g++ compilation process?
I have the following program:
#include <stdio.h>
#include <unistd.h>
size_t sym1 = 100;
size_t *addr = &sym1;
size_t *arr = (size_t*)((size_t)&arr + (size_t)&addr);
int main (int argc, char **argv)
{
(void) argc;
(void) argv;
printf("libtest: addr of main(): %p\n", &main);
printf("libtest: addr of arr: %p\n", &arr);
while(1);
return 0;
}
Why is it possible to produce the binary without error with g++ while there is an error using gcc?
I'm looking for a method to trace what makes them behave differently.
# gcc test.c -o test_app
test.c:7:1: error: initializer element is not constant
# g++ test.c -o test_app
I think the reason can be in fact that gcc uses cc1 as a compiler and g++ uses cc1plus.
Is there a way to make more precise output of what actually has been done?
I've tried to use -v flag but the output is quite similar. Are there different flags passed to linker?
What is the easiest way to compare two compilation procedures and find the difference in them?
In this case, gcc produces nothing because your program is not valid C. As the compiler explains, the initializer element (expression used to initialize the global variable arr) is not constant.
C requires initialization expressions to be compile-time constants, so that the contents of local variables can be placed in the data segment of the executable. This cannot be done for arr because the addresses of variables involved are not known until link time and their sum cannot be trivially filled in by the dynamic linker, as is the case for addr1. C++ allows this, so g++ generates initialization code that evaluates the non-constant expressions and stores them in global variables. This code is executed before invocation of main().
Executables cc1 and cc1plus are internal details of the implementation of the compiler, and as such irrelevant to the observed behavior. The relevant fact is that gcc expects valid C code as its input, and g++ expects valid C++ code. The code you provided is valid C++, but not valid C, which is why g++ compiles it and gcc doesn't.
There is a slightly more interesting question lurking here. Consider the following test cases:
#include <stdint.h>
#if TEST==1
void *p=(void *)(unsigned short)&p;
#elif TEST==2
void *p=(void *)(uintptr_t)&p;
#elif TEST==3
void *p=(void *)(1*(uintptr_t)&p);
#elif TEST==4
void *p=(void *)(2*(uintptr_t)&p);
#endif
gcc (even with the very conservative flags -ansi -pedantic-errors) rejects test 1 but accepts test 2, and accepts test 3 but rejects test 4.
From this I conclude that some operations that are easily optimized away (like casting to an object of the same size, or multiplying by 1) get eliminated before the check for whether the initializer is a constant expression.
So gcc might be accepting a few things that it should reject according to the C standard. But when you make them slightly more complicated (like adding the result of a cast to the result of another cast - what useful value can possibly result from adding two addresses anyway?) it notices the problem and rejects the expression.