#include <iostream>
int main()
{
char *dataptr = new char[33];
char datalocal[33];
dataptr[0] = 'a'; dataptr[1] = 0;
datalocal[0] = 'a'; datalocal[1] = 0;
printf("%p %p %c\n", dataptr, &dataptr, dataptr[0]);
printf("%p %p %c\n", datalocal, &datalocal, datalocal[0]);
delete[] dataptr;
}
Output:
0xd38050 0x7635bd709448 a
0x7635bd709450 0x7635bd709450 a
As we can see, the dynamic pointer data
is really a pointer variable (32 bits or 64 bits at 0x7635BD709448
), containing a pointer to the heap, 0xD38050
.
The local variable is directly a 33 characters long buffer, allocated at address 0x7635BD709450
.
But the datalocal
works also as a char *
value.
I'm a bit confused what the formal C++ explanation of this is. While writing C++ code, this feels quite natural and dataptr[0] is the first element in the heap memory (that is, dereferencing dataptr twice), but in assembler you see the true nature of dataptr
, which is address of the pointer variable. So you have first to load the heap pointer by mov eax,[data]
= loads eax
with 0xD38050
, and then you can load the content of 0xD38050
into XMM0 by using [eax]
.
With a local variable there is no variable with the address of it; the symbol datalocal
is already the address of the first element, so movdqu xmm0,[data]
will work then.
In the "wrong" case you can still do movdqu xmm0,[data]
; it's not a problem of the CPU to load 128 bits from a 32-bit variable. It will simply continue reading beyond the 32 bits and read another 96 bits belonging to other variables/code. In case you are around a memory boundary and this is the last memory page of the application, it will crash on an invalid access.
Alignment were mentioned a few times in comments. That's a valid point; to access the memory through movdqu
it should be aligned. Check your C++ compiler intrinsics. For Visual Studio this should work:
__declspec(align(16)) char datalocal[33];
char *dataptr = _aligned_malloc(33, 16);
_aligned_free(dataptr);
About my C++ interpretation: Maybe I got this wrong since the beginning.
The dataptr
is the value of the dataptr symbol, that is, that heap address. Then dataptr[0]
is dereferencing the heap address, accessing the first element of the allocated memory. &dataptr
is the address of the dataptr
value. This makes sense also with syntax like dataptr = nullptr;
, where you are storing the nullptr value into the dataptr variable, not overwriting the dataptr symbol address.
With datalocal[]
there's basically no sense in accessing the pure datalocal
, like in datalocal = 'a';
, as it's an array variable, so you should always provide the []
index. And &datalocal
is the address of such an array. The pure datalocal
is then an aliased shortcut for easier point math with arrays, etc., having also the char *
type, but if the pure datalocal
would throw a syntax error, it would still be possible to write C++ code (using &datalocal
for pointer, datalocal[..]
for elements), and it would fit with that dataptr
logic completely.
Conclusion: You had your example wrong since the beginning, because in assembly language [data]
is loading the value of data
, which is the pointer to the heap returned by new
.
This is my own explanation, and now some C++ expert will come and tear it to pieces from a formal point of view... :)))
data
is a pointer. – Margaret Bloomchar data[33];
instead of new/delete with pointer can be used directly, as in original post with[data]
? I can't debug now, but I think this may work, as I can imagine the compiled source. What is puzzling me at the moment, what is the C++ difference fromchar *data
. From the C++ point of view they look to be equivalent. I'm probably overlooking something. (and in that second version, thatmov eax,data
is compiled tomov eax,[data]
, right?) – Ped7gxmm0
. Sincexmm0
is larger than a pointer, you are also reading garbage bytes in memory beyond the end of where the pointer is stored. – Raymond Chenmm_loadu_si128
intrinsic. – Raymond Chen