Questions about C++, pointers, OS and compiler memory allocation

  • #1
fluidistic
Gold Member
3,949
264
Sorry I don't have the codes right now.

In C plus plus, when I declare an int, but don't assign any value to it, a space in memory is allocated for it. I can define a pointer and check its adress in hexadecimal, so far so good. I can run this program over and over and usually the int is allocated nearly at the same spot in memory,(I am guessing in the RAM). I made some plots, and sometimes it can vary greatly but overall the values don't differ that much, it's a ''big'' number like 194344563 or something like that. As far as I understand, this number heavily depends on the OS and compiler, so if I could save those addresses in memory, I could possibly determine the OS and or compiler on which the program is running.

Now here's the thing. If I use a loop to allocate (freeing or not memory won't change the following behavior), the first value is as usual, i.e. the big int, but.then the next values are completely different. Sometimes I get 0's, other times negative ints. So the behavior is totally different than if I had ran the program N times. I couldn't find a way to create a single Cpp program that would be equivalent.to execute N times the simple cpp code. ChatGPT was also at a loss.

Now here's another strange.thing. If I allocate an array of dimension N of ints instead of a single int, then the 1st entry of this.array is the usual big int, but then some other values are 0's (usually the 3rd to 6th values might be 0's but not always), and this kills me.

How is that possible that several ints are allocated at the 0 memory entry? Why 0? And much more importantly, how can several ints fit in the same memory spot? That doesn't make any sense?
 
Technology news on Phys.org
  • #2
C++:
#include <iostream>
#include <thread>
#include <cstdio> // Include for printf
#include <cstdlib>
#include <sstream>
#include <string>
#include "int_and_pointer.h"

long int generate_int_and_pointer() {
// Allocate memory for an integer
int* x = new int [1];
std::cout << x << ": memory address of x in hexadecimal.\n";
// Deallocate the allocated memory
    //  if (i % 2 == 0) {
    //   delete x;
    //  }
    printf("%ld : Address in memory (decimal)\n", reinterpret_cast<long>(x));
std::cout << "Value at memory location " << x << ": " << *x << std::endl;

return reinterpret_cast<long>(x);
}

long int* save_addresses(int n) {
    long int* addresses_array = new long int[n];
for (int i=0; i< n; i++) {
  addresses_array[i] = generate_int_and_pointer();
}

return addresses_array;
}
 
  • #3
If "new" returns 0, it probably means that the allocation failed, and no memory was allocated.
 
  • #4
HrvojeDjurdjevic said:
If "new" returns 0, it probably means that the allocation failed, and no memory was allocated.
Why? According to stack overflow it should return std::bad_alloc in that particular case, not 0.
0 would mean ''success'', or not, if anything?
 
  • #5
Perhaps it will help understanding your code and question if you include what the program outputs?
It will also be helpful to know what platform you are on, what compiler you use. Note that "long int" (a.k.a. just "long") can be 32 or more depending on the architecture, so perhaps print out "sizeof(long)" as well (you are converting a pointer address to a long so if a long is 32 bit but address is 64 you throwing away something.
 
  • #6
fluidistic said:
Why? According to stack overflow it should return std::bad_alloc in that particular case, not 0.
0 would mean ''success'', or not, if anything?
std::bad_alloc is not a "value that is returned" in that case, it is an "exception that is thrown/raised/whatever verb appropriate" in that case, these are two different things. Actual value returned is nullptr, and when you print it, you might get, I don't know what exactly, perhaps 0. But what you cannot do, is to interpret that as a valid pointer to a memory location, because that is not what it is. Otherwise, the allocation would not be failed. No?
 
Last edited:
  • #7
I think you are confused about what is happening. You allocated memory for x, but you didn't assign any value for the memory allocated at that location. So when you print out the value at that location, it is whatever garbage happens to be at that location, often 0, but it can be anything. Also, when you allocate memory for the array, that memory is different from the memory you allocate in your "generate_int_and_pointer" function. I modified you program slightly and below is the modified program and the output. I think it all makes sense. Please look at this and tell me what confuses you.

C++:
#include <iostream>
#include <thread>
#include <cstdio> // Include for printf
#include <cstdlib>
#include <sstream>
#include <string>

long int generate_int_and_pointer() {
// Allocate memory for an integer
int* x = new int [1];
std::cout << x << ": memory address of x in hexadecimal.\n";
//printf("%ld : Address in memory (decimal)\n", reinterpret_cast<long>(x));
std::cout << "Value at memory location " << x << ": " << x[0] << std::endl;

return reinterpret_cast<long>(x);
}

long int* save_addresses(int n) {
    long int* addresses_array = new long int[n];
for (int i=0; i< n; i++) {
  addresses_array[i] = generate_int_and_pointer();
  std::cout << "Value at array location " << &addresses_array[i] << ": " << addresses_array[i] << std::endl;

 }

return addresses_array;
}

int main()
{   
  std::cout<<" Running only generate_int_and_pointer"<<std::endl;
  generate_int_and_pointer();
  std::cout<<" Running save_addresses"<<std::endl;
  save_addresses(5);
}

Output:

Running only generate_int_and_pointer
0x600001120000: memory address of x in hexadecimal.
Value at memory location 0x600001120000: 0
Running save_addresses
0x600001120010: memory address of x in hexadecimal.
Value at memory location 0x600001120010: 0
Value at array location 0x600001d2c000: 105553134223376
0x600001120020: memory address of x in hexadecimal.
Value at memory location 0x600001120020: 0
Value at array location 0x600001d2c008: 105553134223392
0x600001120030: memory address of x in hexadecimal.
Value at memory location 0x600001120030: 0
Value at array location 0x600001d2c010: 105553134223408
0x600001120040: memory address of x in hexadecimal.
Value at memory location 0x600001120040: 0
Value at array location 0x600001d2c018: 105553134223424
0x600001120050: memory address of x in hexadecimal.
Value at memory location 0x600001120050: 0
Value at array location 0x600001d2c020: 105553134223440
 
  • #8
fluidistic said:
As far as I understand, this number heavily depends on the OS and compiler, so if I could save those addresses in memory, I could possibly determine the OS and or compiler on which the program is running.
Almost certainly not. It mostly depends on what else is running on the system and using memory. Further, the language does not specify where the memory you ask for is coming from: it just gives it to you (or not).
 
  • #9
I am in a rush. Nope @phyz guy, I do not print the value of the int, which happens to be 0 in your case. I print the pointer's value in decimal. It never vanishes when I,execute the program over and over, no matter how quickly I rerun it. I get some 0 values when I try to run the program in a loop in a cpp code. The behavior is not the same as exiting the program and rerun it.
@Vanadium, much much less than I thought. I ran the program for a full day, with firefox, libreoffice, qt desigber and many other resources hungry programs (turned them on and off to see if I could see any difference) and the allocated memory didn't noticeably change of address during.the full day. Maybe it's possible to see some differences by using statistical tools, but visually on a graph I couldn't tell any difference whatsoever.
 
  • #10
fluidistic said:
I print the pointer's value in decimal.
Again, you store (and I assume, print) a pointer reinterpreted as a long. Unless "sizeof(long) == sizeof(*int)" this will loose information. What are the two sizeof for your setup?
 
  • #11
To the specific point, and int takes, say 4 bytes. If you have 4 bytes free between two processes, the OS may give them to you. If you start and stop a bunch of other processes, they are likely to ask for a lot more than 4 bytes, and that free space is always there.

But the more general point is that if the spec does not promise it, you cannot count on it. This is worth printing and taping to your monitor.
 
  • #12
OK, so I put back in your print statement to print the value of the address in decimal. See below. It still makes sense to me. What exactly is your question?

Running only generate_int_and_pointer
0x600002100030: memory address of x in hexadecimal.
105553150869552 : Address in memory (decimal)
Value at memory location 0x600002100030: 0
Running save_addresses
0x600002100040: memory address of x in hexadecimal.
105553150869568 : Address in memory (decimal)
Value at memory location 0x600002100040: 0
Value at array location 0x600002d04240: 105553150869568
0x600002100050: memory address of x in hexadecimal.
105553150869584 : Address in memory (decimal)
Value at memory location 0x600002100050: 0
Value at array location 0x600002d04248: 105553150869584
0x600002100060: memory address of x in hexadecimal.
105553150869600 : Address in memory (decimal)
Value at memory location 0x600002100060: 0
Value at array location 0x600002d04250: 105553150869600
0x600002100070: memory address of x in hexadecimal.
105553150869616 : Address in memory (decimal)
Value at memory location 0x600002100070: 0
Value at array location 0x600002d04258: 105553150869616
0x600002100080: memory address of x in hexadecimal.
105553150869632 : Address in memory (decimal)
Value at memory location 0x600002100080: 0
Value at array location 0x600002d04260: 105553150869632
 
  • #13
phyzguy said:
OK, so I put back in your print statement to print the value of the address in decimal. See below. It still makes sense to me. What exactly is your question?

Running only generate_int_and_pointer
0x600002100030: memory address of x in hexadecimal.
105553150869552 : Address in memory (decimal)
Value at memory location 0x600002100030: 0
Running save_addresses
0x600002100040: memory address of x in hexadecimal.
105553150869568 : Address in memory (decimal)
Value at memory location 0x600002100040: 0
Value at array location 0x600002d04240: 105553150869568
0x600002100050: memory address of x in hexadecimal.
105553150869584 : Address in memory (decimal)
Value at memory location 0x600002100050: 0
Value at array location 0x600002d04248: 105553150869584
0x600002100060: memory address of x in hexadecimal.
105553150869600 : Address in memory (decimal)
Value at memory location 0x600002100060: 0
Value at array location 0x600002d04250: 105553150869600
0x600002100070: memory address of x in hexadecimal.
105553150869616 : Address in memory (decimal)
Value at memory location 0x600002100070: 0
Value at array location 0x600002d04258: 105553150869616
0x600002100080: memory address of x in hexadecimal.
105553150869632 : Address in memory (decimal)
Value at memory location 0x600002100080: 0
Value at array location 0x600002d04260: 105553150869632
If you focus on the addresses in memory (in decimals), you can see the big ints I was talking about. As you can see, they don't differ too much. However if I call the function that generates them in a loop and save the results in an array, the first entry will be a big int like above, and the rest will be totally different, sometimes negative, and sometimes several entries are 0's.
On my machine at home I cannot reproduce this, however. I get what I would have expected, a list of such big ints without any problem. I won't have access to the machine where this was not the case (it had Ubuntu, and I have another Linux distro here on this machine). My question is why would I get a strange array where several entries are 0's sometimes. As if several ints were sharing the same address.
 
  • #14
fluidistic said:
If you focus on the addresses in memory (in decimals), you can see the big ints I was talking about. As you can see, they don't differ too much. However if I call the function that generates them in a loop and save the results in an array, the first entry will be a big int like above, and the rest will be totally different, sometimes negative, and sometimes several entries are 0's.
On my machine at home I cannot reproduce this, however. I get what I would have expected, a list of such big ints without any problem. I won't have access to the machine where this was not the case (it had Ubuntu, and I have another Linux distro here on this machine). My question is why would I get a strange array where several entries are 0's sometimes. As if several ints were sharing the same address.
It probably has to do with unsuccessful conversion of the address from hex to decimal, as @Filip Larsen said above. Why do you want to convert to decimal anyway?
 
  • #15
phyzguy said:
It probably has to do with unsuccessful conversion of the address from hex to decimal, as @Filip Larsen said above. Why do you want to convert to decimal anyway?
Just for the visual. The conversion is successful when I execute the code, except when I call the function to allocate memory in a loop, in which case it will always be successful in the first iteration and then never again. Hmm. I don't buy it unless there's an explanation.
 
  • #16
fluidistic said:
I don't buy it unless there's an explanation.
As mentioned several times now, the explanation was associated with a condition which you can easily test in your code: If sizeof(long) != sizeof(*int) on the architecture you have the issue on then your code is not "showing" the full pointer address. You mentioned you see negative numbers sometimes, so I this size mismatch is a likely source for the issue you see, but you have not given any other information so its only a guess. But is is so easy to verify for you, just print out those two sizeof!

In C++ you have to be mindful of the size of the types you use. I have no idea why you think its a good idea to reinterpret an address as an integer (instead of, say, simply print out the address in decimal), but if you insist then instead of your "long int" at least use something like unsigned long long which is guaranteed to be at least 64-bit, or include <cstdint> and use something like uint64_t which is always 64-bit.
 
  • #17
fluidistic said:
if I could save those addresses in memory, I could possibly determine the OS and or compiler on which the program is running.
When you take an aeroplane flight do you look at the labels when you pick up your baggage to find out what country you are in?
 
  • Like
Likes Nugatory and Vanadium 50
  • #18
I wish I could like @pbuk 's message twice.

Before we go too far down this path, have you looked at the results? 0x600001120000 is ~100 TB. Do you have that much memory installed?

If not, isn't it likely that the pointer is not a simple linear mapping of the physical address space? (In fact, Intel has done this for 4 decades)
 
  • Informative
Likes fluidistic

FAQ: Questions about C++, pointers, OS and compiler memory allocation

How do pointers work in C++?

Pointers in C++ are variables that store memory addresses. They allow you to directly manipulate memory locations, which can be useful for tasks like dynamic memory allocation and passing parameters by reference.

What is memory allocation in C++?

Memory allocation in C++ refers to the process of reserving memory space for variables or objects during program execution. This can be done statically, on the stack, or dynamically, on the heap.

How does the operating system manage memory allocation?

The operating system manages memory allocation by dividing the available memory into different segments, such as kernel space and user space. It also provides mechanisms for processes to request and release memory dynamically.

What role does the compiler play in memory allocation?

The compiler is responsible for translating high-level code into machine code, including managing memory allocation for variables and objects. It determines the size and layout of memory blocks and generates instructions to access them.

How can memory leaks be prevented in C++?

To prevent memory leaks in C++, it's important to always deallocate memory that has been dynamically allocated using functions like delete or delete[]. It's also a good practice to use smart pointers or containers like std::vector to manage memory automatically.

Similar threads

Replies
118
Views
8K
Replies
4
Views
3K
Replies
19
Views
3K
Replies
23
Views
2K
Replies
22
Views
3K
Replies
39
Views
4K
Back
Top