The following code shows different output with gcc and g++ on using const variable i.
The addresses of i and value of ptr is same, but on accessing that address by printing value of i and derefrencing value of ptr I got value of i as 5 with g++ and 10 with gcc.
How g++ holds const variable in memory?
#include <stdio.h>
int main()
{
const int i =5;
int *ptr =(int*)&i;
*ptr = 10;
printf("\n %u and %u and %d and %d \n",&i,ptr,i,*ptr);
return 0;
}
You are modifying a const qualified object. This is not allowed in C ("undefined behavior"). Anything can happen.
Examples:
The compiler could put i into read-only memory. Writing to *ptr would crash your program.
It could put it into writable memory and you would just see the 10.
It could put it into writable memory but replace all read accesses to i by the number 5 (You promised it is const, didn't you?).
I guess the C compiler chose 2 while the C++ compiler went for 3.
Other have commented on the "undefined" nature of what the code is doing. But to explain how this happens is that it is entirely possible that the compiler applied an optimisation and the runtime value of i is never passed to the printf but instead replaces the i with the constant 5. You did declare it to be const so it is not supposed to change.
It may be in memory or it may be hard-coded into your executable. It is const; the compiler may perform aggressive optimisations on it.
This is why you must not modify it.
You can dereference/cast const as non-cost and overwrite but the behavior is undefined.
As the behaviour is undefined, you may get anything in result, and you should not question why, how etc.
Once the compiler learns your variable is const, it is very well allowed to keep this variable in RO memory and/or replace occurances of this variable with the hardcoded value. A C++ compiler may choose not to assign memory to a const variable unless you ask its address in your code.
Rule of thumb is, decide whether you want to change a variable or not and make it const accordingly.
Related
I never thought I will be going to ask this question but I have no idea why this happens.
const int a = 3;
int *ptr;
ptr = (int*)( &a );
printf( "A=%d\n", &a );
*ptr = 5;
printf( "A=%d\n", ptr );
printf( "A=%d\n", a );
printf( "A=%d\n", *ptr );
Output
A=6945404
A=6945404
A=3
A=5
How can this happen? How can one memory location hold two different values? I searched around and all I find is undefined behavior is undefined. Well that does not make any sense. There must be an explanation.
Edit
I get it, Marks answer makes alot of sense but still I wonder that const was added into the language so that user does not change the value unintentionally. I get that old compilers allows you to do that but I tried this on VS 2012 and I got the same behavior. Then again as haccks said, one memory location can't hold two values it looks like it does, then where is the second value stored?
The optimizer can determine that a is a constant value, and replace any reference to it with the literal 3. That explains what you see, although there's no guarantee that's what's actually happening. You'd need to study the generated assembly output for that.
Modifying a const variable through a non-const pointer results in undefined behavior. Most ikely the optimizer is substituting the original value in this line:
printf( "A=%d\n", a );
Look at the disassembly to verify this.
The C Standard, subclause 6.7.3, paragraph 6 [ISO/IEC 9899:2011], states:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
In fact your program invokes undefined behavior because of two reasons:
1.You are printing an address with wrong specifier %d. Correct specifier for that is %p.
2.You are modifying a variable with const specifier.
If the behavior is undefined then anything could happen. You may get either expected or unexpected result.
Standard says about it;
3.4.3 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,
for which this International Standard imposes no requirements
The problem is that the type of ptr is "pointer to int" not "pointer to const int".
You are then casting the address of 'a' (a const int) to be of type "pointer to int" and storing that address in ptr. The effect of this is that you are casting away the const-ness of a const variable.
This results in undefined behavior so your results may vary from compiler to compiler.
It is possible for the compiler to store 'a' in program ROM since it knows 'a' is a const value that can never be changed. When you lie to the compiler and cast away the const-ness of 'a' so that you can modify it through ptr, it may be invalid for ptr to actually modify the value of 'a' since that data may be stored in program ROM. Instead of giving you a crash, this compiler this time decided to point ptr to a different location with a different value this time. But anything could have happened since this behavior is undefined.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
PS, I know what a pointer is and how to use one, but confused on one thing. I have already tried searching stackoverflow on this question:
int *ptr = 20 //why illegal or seg fault or crash?
printf("%i", *ptr) // Seg Fault
printf("%i", ptr) // Output -> 20
printf("%p", &ptr) // Returns a valid address.
and found that, By directly assigning a value to a pointer without initializing with malloc or null, means that we are saying to the compiler, Hey CPU, Make a space in the memory to store an integer at the exact address given as value, which in this case 20. So basically saying to the compiler make a place for an INT in the ram with the address 20. By doing this we are touching system memory or illegal space.
But what I don't get is,
How the integer 20 can directly be referenced as a memory?
What happens when we do the same for float or char? for example float *ptr = 20.25
I tried directly converting c code to assembly with a website, for a legal and illegal pointer example, where I see that the same registers are called, same MOV operations are done, And no explicit "MAKE SPACE AT given ADDRESS" instructions were set.
Lastly, What exactly happens when we declare strings by doing char
*ptr = "Hello"?
I have tried every possible way to understand this, but couldn't. Can you guys point me to the right direction? Thanks ...
How the integer 20 can directly be referenced as a memory?
Using my C++ compiler, it doesn't compile: I get this error instead:
temp.cpp:22:14: error: cannot initialize a variable of type 'int *' with an
rvalue of type 'int'
int * x = 20;
It does compile as C, albeit with this warning:
temp.c:12:11: warning: incompatible integer to pointer conversion initializing
'int *' with an expression of type 'int' [-Wint-conversion]
int * x = 20;
However, this does compile under both C and C++:
int * x = (int *) 20;
... it compiles because 20 is a well-formed memory-address (it specifies a memory location 20 bytes from the start of the process's memory space).
Note that on most operating systems it is not a usable memory-address though; most operating systems mark the first few pages of the address space as "unreadable/unwritable" specifically so that they can crash the process when someone tries to dereference a NULL-pointer (which otherwise would cause the process to read or write memory at a small offset from the start of the memory space)
What happens when we do the same for float or char? for example float
*ptr = 20.25
Those types won't compile, because floating point (or char) values don't make sense as memory addresses. In most environments, memory addresses are integer offsets from the top of the memory space, so if you want to specify one as a constant (which btw you usually don't want to do, unless you are working at a very low level, e.g. addressing DMA hardware directly in an embedded controller), it needs to be an integer constant.
And no explicit "MAKE SPACE AT given ADDRESS" instructions were set.
That's to be expected -- setting a pointer to a value doesn't implicitly make space for anything, it only sets the pointer to point at the memory-address the constant specified.
Lastly, What exactly happens when we declare strings by doing char
*ptr = "Hello"?
In this case, the compiler recognizes that you have declared a string-constant and adds that string as a read-only array to the process's memory-space. Having done that, it can then set the pointer to point to the start of that array. Note that this behavior is specific to string constants, and doesn't carry over to other data types like int or float.
Also note that it is the declaration of the string constant that triggers the addition of that constant, not the setting of the pointer to point at that constant. For example, if you had this code:
const char * s1 = "Hello";
const char * s2 = "Hello";
printf("s1=%p s2=%p\n", s1, s2);
... you will see output something like this:
s1=0x104608f4e s2=0x104608f4e
... note that both pointers are pointing to the same memory location; since the two strings are identical and read-only, the compiler is free to save memory by only allocating a single instance of the string-data.
Contrariwise, if you did this:
const char * x = (const char *) 20;
... you'd run into the exact same problems you saw with your int * example.
Regarding C++:
int *ptr = 20 //why illegal or seg fault or crash?
This program is ill-formed. Compilers are not required to succesfully compile this program, and they are required to inform you of the issue. There is no implicit conversion from integer literal to pointer (except the literal zero).
How the integer 20 can directly be referenced as a memory?
It cannot be referenced in general. Only if the memory at the address has been allocated, can the pointer be meaningfully used. Furthermore, some usage such as reading the value of the pointed object require that an object exists within its lifetime at the pointed address. Otherwise the behaviour of the program is undefined.
What happens when we do the same for float or char?
Mostly the same as with pointer to int. Unless there is an object of compatible type at the pointed address, the behaviour is undefined when you access the object by indirecting through the pointer. char is slightly different in in that it is compatible with objects of all types. But even char cannot be used to read unallocated memory.
... no explicit "MAKE SPACE AT given ADDRESS" instructions were set.
Well, you didn't tell C++ to allocate any memory, so why would there be any "space made" at the given address?
Lastly, What exactly happens when we declare strings by doing char *ptr = "Hello"?
The program will be ill-formed, since an array of const char doesn't implicitly convert to a pointer to non-const char. Unless standard is pre-C++11, in which case the program is well-formed due to such conversion existing. You would get a deprecation warning instead.
int *ptr = 20 //why illegal or seg fault or crash?
It is illegal because C and C++ standards say so.
by directly assigning a value to a pointer without initializing with malloc or null, means that we are saying to the compiler, Hey CPU, Make a space in the memory to store an integer at the exact address given as value, which in this case 20.
Nothing like this happens. It is simply illegal, full stop.
How the integer 20 can directly be referenced as a memory?
This question is unclear.
What happens when we do the same for float or char? for example float *ptr = 20.25
It is just as illegal as the one above.
I tried directly converting c code to assembly with a website, for a legal and illegal pointer example, where I see that the same registers are called, same MOV operations are done, And no explicit "MAKE SPACE AT given ADDRESS" instructions were set.
There is normally no "MAKE SPACE AT given ADDRESS" instruction that can be contrilled by a C or C++ program.
What exactly happens when we declare strings by doing char *ptr = "Hello"?
In C++, the implementation produces a diagnostic message. What happens next depends on the implementation. In C, the implementation does whatever magic is necessary to cause ptr to point at the first character of a null-terminated character arrray that contains "Hello".
Now for the questions you didn't ask.
What happens in this line
int *ptr = (int*)20;
The number 20 is is interpreted as an address and converted, in an implelmentation-defined way, to a pointer of type int*. No space is allocated at this address. ptr is just made to point there.
How can I allocate an int worth of memory at address 20?
You cannot as far as C and C++ languages go.
This is the code.
int main()
{int v=2;
const int *p=&v;
++v; //Option 1: Does work, but why should it?
// ++*p; //Option 2: Does not work
}
The compiler throws an error for option 2, as expected. But it goes with option 1, when it is modifying the content of a pointer to a constant integer. Why? Or, am I understanding something wrong about the implication of const? Is it applicable only for variables on the heap as opposed to the stack?
Or, am I understanding something wrong about the implication of const?
Yes.
When you have:
int v = 2;
const int *p=&v;
You are not allowed to modify the object through p but you are still allowed to modify the object directly through v. You may also modify the object through another pointer.
int* p2 = &v
*p2 = 10; // OK.
const doesn't really mean 'constant', it basically means "read only". When you define a pointer to a const object, it doesn't mean that object can never change--it just means that you can't write to that object via that pointer.
In fact, it's perfectly allowable (and sometimes meaningful) to specify that an object is both const (so you can't change it) and volatile (indicating that something else might change it). For example, back in the MS-DOS days, the BIOS maintained a timer at a an address of 40:6c (offset 0x6c in segment 0x40), which was updated every ~55 ms, but you shouldn't write it directly, so you could define a pointer to it like:
long const volatile *bios_timer = MK_FP(0x40, 0x6c);
So, attempting to write to this location (at least via this pointer) was not allowed, but the value you'd read from it would change on a regular and ongoing basis.
[Note that contrary to how it may sound above: this (presumably) still exists--but with a protected mode OS, attempting to access it directly in user mode will undoubtedly fail.]
I've checked myself, I wrote a program like this
int main() {
int i;
cout << i;
return 0;
}
I ran the program a few times and the result was same all the time, zero.
I've tried it in C and the result was the same.
But my textbook says
If you don’t initialize an variable that’s defined
inside a function, the variable value remain undefined.That means the element takes on
whatever value previously resided at that location in memory.
How's this possible when the program always assign a free memory location to a variable? How could it be something other than zero(I assume that default free memory value is zero)?
How's this possible when the program always assign a free memory
location to a variable? How could it be something rather than zero?
Let's take a look at an example practical implementation.
Let's say it utilizes stack to keep local variables.
void
foo(void)
{
int foo_var = 42;
}
void
bar(void)
{
int bar_var;
printf("%d\n", bar_var);
}
int
main(void)
{
bar();
foo();
bar();
}
Totally broken code above illustrates the point. After we call foo, certain location on the stack where foo_var was placed is set to 42. When we call bar, bar_var occupies that exact location. And indeed, executing the code results in printing 0 and 42, showing that bar_var value cannot be relied upon unless initialized.
Now it should be clear that local variable initialisation is required. But could main be an exception? Is there anything which could play with the stack and in result give us a non-zero value?
Yes. main is not the first function executed in your program. In fact there is tons of work required to set everything up. Any of this work could have used the stack and leave some non-zeros on it. Not only you can't expect the same value on different operating systems, it may very well suddenly change on the very system you are using right now. Interested parties can google for "dynamic linker".
Finally, the C language standard does not even have the term stack. Having a "place" for local variables is left to the compiler. It could even get random crap from whatever happened to be in a given register. It really can be totally anything. In fact, if an undefined behaviour is triggered, the compiler has the freedom to do whatever it feels like.
If you don’t initialize an variable that’s defined inside a function, the variable value remain undefined.
This bit is true.
That means the element takes on whatever value previously resided at that location in memory.
This bit is not.
Sometimes in practice this will occur, and you should realise that getting zero or not getting zero perfectly fits this theory, for any given run of your program.
In theory your compiler could actually assign a random initial value to that integer if it wanted, so trying to rationalise about this is entirely pointless. But let's continue as if we assumed that "the element takes on whatever value previously resided at that location in memory"…
How could it be something rather than zero(I assume that default free memory value is zero)?
Well, this is what happens when you assume. :)
This code invokes Undefined Behavior (UB), since the variable is used uninitialized.
The compiler should emit a warning, when a warning flag is used, like -Wall for example:
warning: 'i' is used uninitialized in this function [-Wuninitialized]
cout << i;
^
It just happens, that at this run, on your system, it had the value of 0. That means that the garbage value the variable was assigned to, happened to be 0, because the memory leftovers there suggested so.
However, notice that, kernel zeroes appear relatively often. That means that it is quite common that I can get zero as an output of my system, but it is not guaranteed and should not be taken into a promise.
Static variables and global variables are initialized to zero:
Global:
int a; //a is initialized as 0
void myfunc(){
static int x; // x is also initialized as 0
printf("%d", x);}
Where as non-static variables or auto variables i.e. the local variables are indeterminate (indeterminate usually means it can do anything. It can be zero, it can be the value that was in there, it can crash the program). Reading them prior to assigning a value results in undefined behavior.
void myfunc2(){
int x; // value of x is assigned by compiler it can even be 0
printf("%d", x);}
It mostly depends on compiler but in general most cases the value is pre assumed as 0 by the compliers
As we know the value of constant variable is immutable. But we can use the pointer of constant variable to modify it.
#include <iostream>
int main()
{
const int integer = 2;
void* tmp = (void*)&integer;
int* pointer = (int*)tmp;
(*pointer)++;
std::cout << *pointer << std::endl;
std::cout << integer << std::endl;
return 0;
}
the output of that code is:
3
2
So, I am confusing what i modified on earth? what does integer stand for?
Modifying consts is undefined. The compiler is free to store const values in read only portions of memory and throw error when you try to change them (free to, not obliged to).
Undefined behavior is poor, undesirable and to be avoided. In summary, don't do that.
PS integer and pointer are variable names in your code, tho not especially good names.
You have used unsafe, C-style casts to throw away the constness. C++ is not an inherently safe language, so you can do crazy stuff like that. It does not mean you should. In fact, you should not use C-style casts in C++ at all--instead use reinterpret_cast, const_cast, static_cast, and dynamic_cast. If you do that, you will find that the way to modify const values is to use const_cast, which is exactly how the language is designed.
This is an undefined behavior. The output you get is compiler dependent.
One possible explanation for this behavior is as follows.
When you declares integer as a constant, and use it in an expression, a compiler optimization and substitute it with the constant literal you have assigned to it.
But, the actual content of the memory location pointed by &integer is changed. Compiler merely ignore this fact because you have defined it as a constant.
See Const Correctness in C++. Give some attention to the assembler output just above the 'The Const_cast Operator' section of this page.
You're wading into Undefined Behavior territory.
If you write
void* tmp = &integer;
the compiler would give you an error. If you wrote good C++ code and wrote
void* tmp = static_cast<void*>(&integer);
the compiler would still give you an error. But you went ahead and used a C-style unprotected cast which left the compiler no option but to do what you told it.
There are several ways the compiler could deal with this, not least of which:
It might take the address of a location in the code segment where the value was, e.g., being loaded into a register.
It might take the address of a location of a similar value.
It might create a temporary by pushing the value onto the stack, taking the address of the location, and then popping the stack.
You would have to look at the assembly produced to see which variant your compiler prefers, but at the end of the day: don't do it it is undefined and that means next time you upgrade your compiler or build on a different system or change optimizer settings, the behavior may well change.
Consider
const char h = 'h';
const char* hello = "hello";
const unsigned char num = 2 * 50 + 2 * 2; // 104 == 'h'
arg -= num; // sub 104, eax
char* ptr = (char*)(&h);
The compiler could choose to store an 'h' specially for the purpose of 'ptr', or it could choose to make 'ptr' point to the 'h' in hello. Or it could choose to take the location of the value 104 in the instruction 'sub 104, eax'.
The const key word is just a hint for compiler. Compiler checks whether a variable is const or not and if you modify a const variable directly, compiler yield a wrong to you. But there is no mechanism on variable storage to protect const variables. So operating system can not know which variable is const or not.