Optimization, global variables and memory-barriers [duplicate] - c++

This question already has answers here:
Should volatile still be used for sharing data with ISRs in modern C++?
(4 answers)
Multithreading program stuck in optimized mode but runs normally in -O0
(3 answers)
When do I need to use volatile in ISRs?
(3 answers)
Is volatile needed when variable is only read during interrupt
(1 answer)
Closed last month.
In the following example the variable v is not accessible from outside this TU. So the compiler can optimize the loop in main() to an empty loop. This is done with -O3, but with -Os it remains a loop with the load of v outside the loop, so that it is effectively an endless loop without effect.
The function isr() could be optimized away, but is not since it should resemble an ISR and therefore hast the used attribute.
If one changes v to an external visible global, the assemble code remains the same!
How can the compiler reason, that v remains unmodified in this case?
Inserting a memory-barrier sure fixes that in both cases. But why isn't it suffcient to make v external visible?
#include <avr/cpufunc.h>
//int v;
namespace {
int v;
void isr()
__asm__("__vector_5")
__attribute__ ((__signal__,
__used__
));
void isr() {
v += 1;
}
}
int main() {
while(true) {
// _MemoryBarrier();
if (v == 42) {
v += 1;
}
}
}
https://godbolt.org/z/4j7En41sE

Related

implicit default value for class member variable? [duplicate]

This question already has answers here:
How do C++ class members get initialized if I don't do it explicitly?
(8 answers)
Closed 1 year ago.
I'm doing C++ tests for my certification exam and I came across this exercise that i don't understand:
(the question is what is the output of the following program)
#include <iostream>
using namespace std;
class A {
public :
float v;
float set(float v) {
A::v += 1.0;
A::v = v+1.0;
return v;
}
float get(float v){
v +=A::v;
return v;
}
};
int main()
{
A a;
cout<< a.get(a.set(a.set(0.5)));
return 0;
}
I expected to have an error on the first line of the set function since A::v was never initialized, but my program compiles and it seems that A::v has value 0 by default..
Could someone please explain why there is no compilation error?
Like you mentioned, the first line of set used A::v, which was never initialized before. However, that itself doesn't produce an error, it is undefined behavior. What it means is the compiler may initialize it for you, or it might just pickup a random number it sees on the memory, or whatever they are pleased to. The C++ standard doesn't say what needs to happen, so it left the compiler to decide whatever is easy.
However, whatever happens on that line shouldn't matter too much in your code, in most cases. The reason is A::v will be re-assigned to v + 1 on the next line. So it should almost always print 2 at the end.

A reason to forbid recursive call of main() in C++ [duplicate]

This question already has answers here:
Why calling main() is not allowed in C++
(3 answers)
Why are recursive main() calls not allowed in C++? [duplicate]
(2 answers)
Closed 2 years ago.
C++ standard says:
The function main shall not be used within a program.
Also:
Recursive calls are permitted, except to the main function.
At the same time C allows such a usage of main according to this answer. I found a comment under that answer that says the following:
The simplest implementation of global constructors (without special support from the OS and underlying C runtime entry code) is for the C++ compiler to generate a function call at the beginning of main (__main is a common name for it) which calls all the global constructors. Having global objects be reconstructed every time main gets called recursively would be a rather bad thing... :-)
It makes sense, but I've tried the following code:
#include <cstdio>
struct S {
S() { std::puts("S ctor"); }
~S() { std::puts("S dtor"); }
};
S s;
int main() {
static int count = 0;
count++;
if (count <= 10) {
std::printf("%d ", count);
return main();
}
std::puts("");
}
in clang, gcc, msvc. All of these compilers print same output:
S ctor
1 2 3 4 5 6 7 8 9 10
S dtor
So the global s object was contructed only once despite recursive call of main. Yes, I know what are undefined/unspecified/implementation-defined behaviour mean. But would someone explain (or even demonstrate by code for any available in internet compiler) in more details why using a main function in the C++ program is forbidden and may lead to unexpected results in particular?

Return value in C++ [duplicate]

This question already has answers here:
C++ return value without return statement
(6 answers)
Closed 6 years ago.
I am confused with the following output in C++
int add()
{
int c = 2+3;
}
int main()
{
int x = add();
cout << x;
return 0;
}
This prints 5.even if we do not write return statement.
How this is managed in C++.
Please help.
This is UB. You're right to be confused - this can work one day and fail the next. Don't rely on undefined behavior.
If you want to know why it works, it's because parameters & return values are passed on a data structure called stack (well - usually; sometimes passed in the same register). Similarly, most implementations use this same stack for locals. Therefore, the int in add will be located in the same place as where the return value is expected (by your specific implementation) and your implementation doesn't invalidate memory when your int there is destructed. But it's still destructed, it's still UB and it might break in any second.
As the comments wrote, you might turn on warnings to avoid this kind of error.

How does C++ proceed for += with a variable without value? [duplicate]

This question already has answers here:
What happens when I print an uninitialized variable in C++? [duplicate]
(4 answers)
Closed 6 years ago.
i am currently learning C++ and i had a question about some "weird" things i noticed while testing what i have learnt.
So i use this :
int marc[9];
int sum = 0;
for (int x =0; x < 9; x++) {
marc[x] = x*4;
sum += marc[x];
}
cout << sum << endl;
And i get a normal result which is 144
However, if i changed
int sum = 0;
to
int sum;
I got a crazy result like 19557555002
So i was wondering what is happening in the second case?
Thanks for your answer :)
You are operating on uninitialized memory in the second case. Non-static local variables are not initialized to zero by the runtime like in other languages, if you need sum to have a defined value, you must initialize it yourself. The 19557555002 will be the integer interpretation of any bytes that were present at the memory address allocated for sum.
Further reading: What happens to a declared, uninitialized variable in C? Does it have a value?
Its called an undefined behavior and it happens when you don't initialize your variables.
int sum;
above code can only declare a variable but it doesn't initialize it by default so the variable contains a garbage value.
this creates an uninitialized int int sum;
it can have "garbage" values, and this is exactly what happened to you
how this happens: let's say you use an int x in address y, and sets it to 19557555002. now, lets say you "leave" that address (go out of scope, program terminates, OS takes that memory...) and someone else takes it because he wants to put there a new int. if he just declares his int, without initializing it, his int can be stationed (if the OS so desires...) in address y, that previously used to hold your int, which means in address y, he will find 19557555002. That is what could happen to you if you don't initialize variables.
Memory for local variables is typically allocated on the stack. This means that without some initialization, they will hold some data that was residing there previously.
As others said it is undefined behavior, but practically, on most implementations, it results in effects like this:
void foo()
{
int a = 5;
}
void bar()
{
int b;
std::cout << b;
}
void someCaller()
{
foo();
bar();
}
On most implementations, this will usually result in the printing of 5 on the stdout.
Note that some compilers like MSVC initialize all variables in Debug configuration, but usually any kind of optimization flags will avoid initializing memory, if not explicitly requested.

Order of Local Variables : Best way to declare variables(varying in size) in cpp [duplicate]

This question already has answers here:
How declaration of variables behave?
(2 answers)
Closed 9 years ago.
I'm currently reviewing a code, and there are many local variables in varying sizes.
Is declaring in increasing order of size the preferable one or vice versa.
Explain it with memory layout in either of scenarios.
Is memory allocated for the local variables based on order of declaration or on size.
int fun()
{
struct *ptr;
int var1;
long double *ld;
.
.
.
.
}
The best place to declare (and initialize) a local variable in C++ is right at the point where it's first needed.
The size of the variable should not be a consideration at all, unless you have specific evidence to the contrary.
Compiler will reorder local variables as it sees fit, when it does optimizing. In short, order of variables in the same scope does not matter.
What is good idea though, is to declare local variables in the scope where it is used, for example:
void func() {
//int i, j; // not here!
for (int i = 0 ; i<10; ++i) {
int j = func2(i);
...
}
// i and j below are different variables than i and j above
// you can consider changing their names if they also have different meaning
for (int i = 0 ; i<10; ++i) {
int j = func3(i);
...
}
}
Though for good optimizing compiler, that likely will not matter from performance or memory footprint point of view (it will detect when variables are used anyway). It will still make the code more readable, and avoid mixing unrelated values in different scopes, thus protecting from some stupid bugs not caught by compiler warnings (because compiler doesn't know when you are accidentally forgetting re-initialization of re-used variable, but it will know if you forget to initialize a new variable).
Also, important thing when worrying about variables (or anything): remember to turn on warnings for compiler, like -Wall -Wextra for gcc. Also, using valgrind is good idea (if you can get your code to run on OS which has valgrind).
My approach is, that I declare local variables in the smallest possible scope, at the scope's beginning, e.g.
void foo()
{
int local1 = 42;
int local2 = bar(local1);
if ( local2 != local1)
{
double local3 = double(local2)/double(local1);
MyMemoryAllocatingObject mmao; // large memory allocation, deallocation in destructor
baz(local3);
bat(mmao);
} // mmao memory gets freed here
}
For not-sophisticated compilers it helps optimization, for users it helps tracking the information. Plus, it helps keeping memory footprint as small as possible, because the locals go out of scope (sic!), i.e. their destructor is called.