I'm just decided to test malloc and new. Here is a code:
#include <iostream>
#include <string>
struct C
{
int a = 7;
std::string str = "super str";
};
int main()
{
C* c = (C*)malloc(sizeof(C));
std::cout << c->a << "\n";
std::cout << c->str << "\n";
free(c);
std::cout << "\nNew:\n\n";
c = new C();
std::cout << c->a << "\n";
std::cout << c->str << "\n";
}
Why an output of this program stops at std::cout << c->a << "\n";:
-842150451
C:\Code\Temp\ConsoleApplication12\x64\Debug\ConsoleApplication12.exe (process 22636) exited with code 0.
Why does compiler show no errors - I thought, std::string isn't initialized properly in case of malloc, so it should break something.
If I comment out printing of the string, I'm getting a full output:
-842150451
New:
7
super str
C:\Code\Temp\ConsoleApplication12\x64\Debug\ConsoleApplication12.exe (process 21652) exited with code 0.
I use MSVS2022.
You've used malloc. One of the reasons to not do this is that it hasn't actually initialized your object. It's just allocated memory for it. As a result, when accessing member fields, you get undefined behavior.
You have also forgotten to delete the C object you created with new. But you may wish to use a std::unique_ptr in this scenario, to avoid having to explicitly delete the object at all. The smart pointer will automatically free the memory when it goes out of scope at the end of main.
auto c = std::make_unique<C>();
std::cout << c->a << std::endl;
std::cout << c->str << std::endl;
(C*) is an Explicit Conversion. It tells the compiler to shut up and do exactly what it was told to do, no matter how stupid that may be. You don't get an error message because the code explicitly told the compiler not to give you one.
To allow the programmer and compiler the maximum amount of leeway, C++ will not prevent you from doing some pretty insane things. It assumes you know what you are doing and are not trying to shoot yourself.
Related
After compilation, what does the reference become, an address, or a constant pointer?
I know the difference between pointers and references, but I want to know the difference between the underlying implementations.
int main()
{
int a = 1;
int &b = a;
int *ptr = &a;
cout << b << " " << *ptr << endl; // 1 1
cout << "&b: " << &b << endl; // 0x61fe0c
cout << "ptr: " << ptr << endl; // 0x61fe0c
return 0;
}
The pedantic answer is: Whatever the compiler feels like, all that matters is that it works as specified by the language's semantics.
To get the actual answer, you have to look at resulting assembly, or make heavy usage of Undefined Behavior. At that point, it becomes a compiler-specific question, not a "C++ in general" question
In practice, references that need to be stored essentially become pointers, while local references tend to get compiled out of existence. The later is generally the case because the guarantee that references never get reassigned means that if you can see it getting assigned, then you know full well what it refers to. However, you should not be relying on this for correctness purposes.
For the sake of completeness
It is possible to get some insight into what the compiler is doing from within valid code by memcpying the contents of a struct containing a reference into a char buffer:
#include <iostream>
#include <array>
#include <cstring>
struct X {
int& ref;
};
int main() {
constexpr std::size_t x_size = sizeof(X);
int val = 12;
X val_ref = {val};
std::array<unsigned char, x_size> raw ;
std::memcpy(&raw, &val_ref, x_size);
std::cout << &val << std::endl;
std::cout << "0x";
for(const unsigned char c : raw) {
std::cout << std::hex << (int)c;
}
std::cout << std::endl ;
}
When I ran this on my compiler, I got the (endian flipped) address of val stored within the struct.
it heavily depend on compiler maybe compiler decide to optimize the code therefore it will make it value or ..., but as far i know references will compiler like pointer i mean if you see their result assembly they are compiled like pointer.
Can you explain this behaviour to me, pls? Here the code:
int* b = new int;
const int MAX_AGE = 90;
b = (int*)&MAX_AGE;
std::cout << b << std::endl;
std::cout << &MAX_AGE << std::endl;
std::cout << *b << std::endl;
std::cout << MAX_AGE << std::endl;
std::cout << "........." << std::endl;
*b = 2;
std::cout << *b << std::endl; // HERE I get 2, that's ok
std::cout << MAX_AGE << std::endl; // HERE I still get 90, why?
std::cout << b << std::endl;
std::cout << &MAX_AGE << std::endl;
The problem is that you lied to your compiler, and compilers are pretty good at exacting revenge on people who lie to them.
Specifically, on this line you told the compiler that MAX_AGE is changeable:
b = (int*)&MAX_AGE;
This is a lie, because you declared MAX_AGE to be a const. What happens next is called undefined behavior (UB): the compiler is free to produce any results, including complete nonsense, after your code triggers UB.
In your case, however, there is a pretty good explanation of what gets printed: knowing that MAX_AGE is another name for 90, the compiler has optimized std::cout << MAX_AGE << std::endl; to print 90, without looking up its value from memory.
MAX_AGE is declared as const int. With your c-style cast you remove constness and then proceed to modify a const value. This is UB.
This is a prime example for why this is UB: Due to the constness of MAX_AGE the compiler knows that it won't change and can thus replace all occurences of it by the literal 90.
const tells the compiler that the variable MAX_AGE should be stored in the write protected region of the respective segment in memory.
Armed with this knowledge the compiler can obviate the need to repeatedly read the same memory location. In other words the compiler might cache the constant value. That is why you see MAX_AGE showing up with the original value.
Anyway as has been already mentioned you shouldn't be confusing the compiler with your actual intentions. If you intend to store a variable in a write protected region then you shouldn't be modifying it.
Why does my code for overwriting const int variable work? Is it safe?
#include <iostream>
#include <cstring>
using namespace std;
int z = 5;
const int x = *(&z);
int main()
{
cout << "A:" << x << ", " << &x << endl;
int y = 7;
cout << "B:" << y << ", " << &y << endl;
memcpy((int*)&x, &y, sizeof(int));
cout << "C:" << x << ", " << &x << endl;
}
Output would be:
A:5, 0x600f94
B:7, 0x7a7efb68019c
C:7, 0x600f94
I am not sure if this has been asked before since I don't know what to search for in this situation.
Answering your questions:
It's not safe; should never be done, it's example of how not to use C.
It's based on undefined behaviour; that means specification doesn't give exact instruction how such attempt should be treated in the code.
Final answer to question why it works? The answer here is true for the GCC only as other compilers may optimise/treat const different way. You need to understand that technically const int x is a declaration of a variable with qualifier. That means (until it's optimised) it has it's place in the memory (in some circumstances in read-only section). When you removed the qualifier and give the address of the variable to the memcpy() (which is dummy library call unaware of memory protection) it makes attempt to write the new data to that address. If compiler puts that variable into read-only section (I faced that epic failure in the past) the execution on any Unix would end with segmentation fault caused by writing instruction violation memory protection of the read-only memory segment used by your program to hold constant data.
In C++ real constants are qualified by constexpr, however there are other implications.
Consider this code:
int& x=*new int;
Does RHS of the assignment actually dereference the newly-created pointer, leading to UB due to reading uninitialized variable? Or can this be legitimately used to later assign a value like x=5;?
As far as I can tell, none of what you've done involves undefined behavior.
It does, however, immediately create a risk of a memory leak. It can be quickly resolved (since &x would resolve to the address of the leaked memory, and can therefore be deleted) but if you were to leave scope, you would have no way of retrieving that pointer.
Edit: to the point, if you were to write
int& x=*new int;
x = 5;
std::cout << x << std::endl;
std::cin >> x;
std::cout << x << std::endl;
The code would behave as though you had simply declared x as int x;, except that the pointer would also be left dangling after the program exited scope.
You would achieve undefined behavior if you were to attempt to read the uninitialized variable before assigning it a value, but that wouldn't be untrue if x were stack allocated.
I would not advise doing this. Aliasing dynamic memory with a variable that looks static seems dangerous to me. Consider the following code
#include <iostream>
int* dynamicAlloc() {
int& x = *new int;
std::cout << "Inside dynamicAlloc: " << x << std::endl;
std::cout << "Modified: " << (x = 1) << std::endl;
return &x;
}
int main() {
int *dall = dynamicAlloc();;
std::cout << "dall (address): " << dall << std::endl;
std::cout << "dall -> " << *dall << std::endl; // Memory not cleaned
delete dall; // Don't forget to delete
return 0;
}
I get output like this:
Inside dynamicAlloc: -842150451
Modified: 1
dall (address): 00F642A0
dall -> 1
Note how dereferencing dall doesn't result in a segfault. It wasn't deallocated when x fell out of scope. Otherwise a segfault would have likely occurred.
In conclusion:
int& x makes x an alias for the dynamically allocated memory. You can legitimately modify that memory, but it will not cleanup as it falls out of scope automatically easily leading to potential memory leaks.
MemoryBlock(MemoryBlock&& other)
: _data(nullptr)
, _length(0)
{
std::cout << "In MemoryBlock(MemoryBlock&&). length = "
<< other._length << ". Moving resource." << std::endl;
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = nullptr;
other._length = 0;
}
Here is an example from Microsoft msdn.
It's size_t and can't be free. So you don't have to assign it to 0.
Why set other._length to 0?
THX
For completeness sake, no specific technical reason as far as I know. other will be destroyed when exiting its scope, since you defined it as an rvalue reference in the move constructor.
You have to make sure to leave rvalue references in move constructors/assignments in a destructable state, which is the case by assigning nullptr to _data (in general you must assure that pointers point to null). The _length = 0 is just for completeness (or habit): The data is moved, data pointer is nulled and the length must be zero.
It's often a good idea to do that, so you can maintain a stricter invariant, making other parts of the class easier to reason about, and require less if checks. To take a concrete example, suppose MemoryBlock had a length() method that returned length. If you didn't set _length on move, then this length() method would require an if check on _data not being null. By setting it to zero after move, this method can just trivially return _length.
After you move the contents of an object A to another object B, A is still around. It will be destroyed at the end of the scope and, in the meantime, it can be used again. You can't really count on what the contents of A are after they have been moved, but you can assign a new value to it. For example, this is legitimate code:
std::string a = "Some string";
std::cout << "a before move: " << a << endl;
std::string b = std::move(a);
std::cout << "a after move: " << a << endl;
std::cout << "b after move: " << b << endl;
a = "Some other string";
std::cout << "a after reassignment: " << a << endl;
It produces this under Visual Studio:
a before move: Some string
a after move:
b after move: Some string
a after reassignment: Some other string