C++ Pointers: Address - Address Confusion [duplicate] - c++

When we subtract a pointer from another pointer the difference is not equal to how many bytes they are apart but equal to how many integers (if pointing to integers) they are apart. Why so?

The idea is that you're pointing to blocks of memory
+----+----+----+----+----+----+
| 06 | 07 | 08 | 09 | 10 | 11 | mem
+----+----+----+----+----+----+
| 18 | 24 | 17 | 53 | -7 | 14 | data
+----+----+----+----+----+----+
If you have int* p = &(array[5]) then *p will be 14. Going p=p-3 would make *p be 17.
So if you have int* p = &(array[5]) and int *q = &(array[3]), then p-q should be 2, because the pointers are point to memory that are 2 blocks apart.
When dealing with raw memory (arrays, lists, maps, etc) draw lots of boxes! It really helps!

Because everything in pointer-land is about offsets. When you say:
int array[10];
array[7] = 42;
What you're actually saying in the second line is:
*( &array[0] + 7 ) = 42;
Literally translated as:
* = "what's at"
(
& = "the address of"
array[0] = "the first slot in array"
plus 7
)
set that thing to 42
And if we can add 7 to make the offset point to the right place, we need to be able to have the opposite in place, otherwise we don't have symmetry in our math. If:
&array[0] + 7 == &array[7]
Then, for sanity and symmetry:
&array[7] - &array[0] == 7

So that the answer is the same even on platforms where integers are different lengths.

Say you have an array of 10 integers:
int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Then you take a pointer to intArray:
int *p = intArray;
Then you increment p:
p++;
What you would expect, because p starts at intArray[0], is for the incremented value of p to be intArray[1]. That's why pointer arithmetic works like that. See the code here.

"When you subtract two pointers, as long as they point into the same array, the result is the number of elements separating them"
Check for more here.

This way pointer subtraction behaves is consistent with the behaviour of pointer addition. It means that p1 + (p2 - p1) == p2 (where p1 and p2 are pointers into the same array).
Pointer addition (adding an integer to a pointer) behaves in a similar way: p1 + 1 gives you the address of the next item in the array, rather than the next byte in the array - which would be a fairly useless and unsafe thing to do.
The language could have been designed so that pointers are added and subtracted the same way as integers, but it would have meant writing pointer arithmetic differently, and having to take into account the size of the type pointed to:
p2 = p1 + n * sizeof(*p1) instead of p2 = p1 + n
n = (p2 - p1) / sizeof(*p1) instead of n = p2 - p1
So the result would be code that is longer, and harder to read, and easier to make mistakes in.

When applying arithmetic operations on pointers of a specific type, you always want the resulting pointer to point to a "valid" (meaning the right step size) memory-address relative to the original starting-point. That is a very comfortable way of accessing data in memory independently from the underlying architecture.
If you want to use a different "step-size" you can always cast the pointer to the desired type:
int a = 5;
int* pointer_int = &a;
double* pointer_double = (double*)pointer_int; /* totally useless in that case, but it works */

#fahad Pointer arithmetic goes by the size of the datatype it points.So when ur pointer is of type int you should expect pointer arithmetic in the size of int(4 bytes).Likewise for a char pointer all operations on the pointer will be in terms of 1 byte.

Related

Why are malloc-allocated memory values assigned with i++ instead of i+4 (in the case of an int being 4 bytes large)?

My mind is confused and blown right now and hopefully someone can help me deconfuse it /put me on the right path again.
Correct me if I'm wrong but in C, you use malloc to 'reserve' / get a certain amount of space in memory in bytes.
So int* ptr = malloc(10 * sizeof(int)); would allocate 40 Bytes of memory / assign a pointer to that region in memory to the variable/pointer ptr (suppose that on this machine an int is 4 bytes large).
What I don't understand is when you assign the array with values why i is i++ (1 increment) instead of i + 4 aka the size of an int. It would make sense since we have a 40 Bytes block of memory and we iterate 10 times à +4 Bytes => 40 Bytes.
Code Example:
Suppose, for the sake of simplicity, a function random() returns a random integer.
(The correct way -> 10 random ints):
for(int i = 0; i < 10; i++) {
*(ptr + i) = random();
}
(My thought of i+4 Bytes, produces weird results, wrong way):
for(int i = 0; i < 10; i+=4) {
*(ptr + i) = random();
}
My question is, why does ptr + i (i+1) select the correct address of each int when an int is 4 bytes large and we even reserve 40 Bytes of memory, not 10. aka why is ptr + i (i+4) wrong?
(To my brain it makes sense to iterate i+4 instead of 1. start / ptr assigned through malloc => 0x400, an int is 4 bytes large so the next address / next int is at 0x404, 0x408 etc.... not 0x401, 0x402 ....)
Pointer arithmetic, and memory allocation, are two different things.
Pointer arithmetic is always done based on the type of the pointed-to object. That is, pointer arithmetic always incorporates an automatic, implicit multiplication by that size. So
int *p;
/* ... */
p++;
is always going to increment the address in p by sizeof(int) bytes.
It has to be this way because, among other things, pointer arithmetic simulates array access. So it has to take the size of the pointed-to object into account in order for notations like
*(p + 1)
and
p[1]
to work properly.
The malloc function, on the other hand, always deals in bytes. One reason -- perhaps the reason -- is precisely that malloc is a function. When you say something like
malloc(10 * sizeof(int))
the number received by the malloc function on your machine is just 40. malloc has no way of knowing whether that's 40 things of size 1, or 10 things of size 4, or one thing of size 40. All it knows is it has to allocate 40 bytes. (There are also requirements pertaining to alignment.)
If you wanted it to work differently -- if you wanted something like
int *ip = malloc(10);
to automatically scale by sizeof(int) -- it's hard to see how it could work in C. (By contrast, in C++ there's new[], which can and does take the size of the allocated object into account.)
If p is a pointer to an object of type T, then p+1 yields the address of the next object of the pointed-to type; p++ advances p to point to the next object.
Given
char *cp = (char *) 0x1000;
short *sp = (short *) 0x1000; // assume 2-byte short
long *lp = (long *) 0x1000; // assume 4-byte long
then the following are true:
char short long
+–––+ +–––+ +–––+
0x1000 | | <––cp | | <––sp | | <-–lp
+–––+ + - + + - +
0x1001 | | <-–cp+1 | | | |
+———+ +———+ + - +
0x1002 | | <––cp+2 | | <––sp+1 | |
+———+ + - + + - +
0x1003 | | <––cp+3 | | | |
+–––+ +–––+ +–––+
0x1004 | | <––cp+4 | | <––sp+2 | | <––lp+1
+———+ + - + + - +
... ... ...

Dynamically Allocating Arrays Depending on User Input in C++

im watching this tutorial on youtube https://www.youtube.com/watch?v=8XAQzcJvOHk Dynamically Allocating Arrays Depending on User Input in C++
this is his code
1 int main()
2 {
3 int *pointer = nullptr;
4
5 cout << "how many items u are gonna enter" << endl;
6 int input;
7 cin >> input;
8
9 pointer = new int[input];
10
11 int temp;
12
13 for (int counter = 0; counter < input; counter++) {
14 cout << "enter the item " << counter + 1 << endl;
15 cin >> temp;
16 *(pointer + counter) = temp;
17 }
18
19 cout << "the items you have entered are" << endl;
20 for (int counter = 0; counter < input; counter++) {
21 cout << counter + 1 << " item is " << *(pointer + counter) << endl;
22 }
23
24 delete[]pointer;
25
26 return 0;
27}
im stuck in line 16, i dont understand why is that, inside the (), the pointer variable and counter are added to each other
Pointer Arithmetic is a good point where to start.
I'm going to try to explain you briefly how it works, but I strongly suggest you to integrate those concepts with a good book or internet references because they are very important for proper handling pointers.
A pointer (as you can imagine from the name) points a memory cell:
int* ptr = /*an address to the memory cell*/
Your memory is composed by sequentially cells, graphically:
Address| Value
-------|------------
0x00 | [#some value] 8 bit
0x01 | [#some value] 8 bit
... | ...
0xN | [#some value] 8 bit
Just to make this example not so long, we can assume each cell contains 8 bits and a integer value is represented with exactly 32 bit (usually that is not true, and it depends on the machine architecture and compiler).
Then a int value is stored exactly in 4 cells. (We explicitly don't consider memory alignment).
So your pointer contains a memory location, the address in the memory which contains the value you've allocated (with the usage of dynamic memory).
For example:
int* ptr = 0x01
That means the variable ptr, stored somewhere in the memory, contains the address 0x01. In the memory cell 0x01 there will be the integer value allocated dynamically.
But, since the value is an integer type, the data will take 4 cell in order to store the complete information. So the data will be "split" among the cell 0x01, 0x02, 0x03, 0x04.
The pointer will points the first memory location of the data, and the number of cell occupied is given by the type of pointer (in that case pointer int so the compiler knows the information starts from cell 0x01 and ends 0x04).
A variable pointer can be evaluated in an arithmetic expression, such sums and differences.
Fo example:
ptr + 10
ptr - 10
Simply, the meaning of that expression is to access to memory address starting from the address stored in ptr and jumping 10 int cells forward or backward.
Attention Note: the expression does not mean to simply add the value to the address obtaining a new address.
Indeed, assuming ptr = 0x01, then the expression:
ptr + 10;
does not mean 0x01 + 10 = 0xa!
Instead that means to jump 10 "block" of size equal to the type's size
pointed by the pointer itself.
That is, 0x01 + 10 * 4bytes.
Since ptr is a pointer to int, then +10 means "plus 10 block of integers", and, in this example, each int occupies 4 bytes (32 bit).
To conclude, the expression:
*(pointer + counter) = temp;
means to access to the address start from pointer and adding #counter block of int, then deference that address with the operator* and write in that address the value temp.
That notation can be easily simplify with the operator[]:
pointer[counter] = temp;
where the meaning is exactly the same, but the notation is more readable, especially when you have to do with array.
This part:
*(pointer + counter)
is just simple pointer arithmetic: we are adding counter (of type int) to the pointer address and then dereferencing it using *. It is the same as pointer[counter]. After that, we are savig value of temp into that particular (dereferenced) location in memory.
*(pointer + counter) is equivalent to pointer[counter] as has been pointed out, the reason it's equivalent is because pointer holds a memory address, when you add say 1 to the that memory address you are infact adding the size of whatever the data type that pointer is pointing to is, multiplied by 1.
If you have a primitive array
int arr[2] = {1,55};
*arr would give you 1 and *(arr + 1) would give you 55
*(pointer + counter) = temp;
is same as
pointer[counter] = temp;
The variable pointer contain the address of the first element of the array.
Adding counter means selecting the address of counter away from starting address.
counter is simply an offset from pointer.

Difference between the address of adjacent array elements..? [duplicate]

This question already has an answer here:
When I subtract memory addresses, why is the result smaller than I expected?
(1 answer)
Closed 8 years ago.
I am just confused with the output which I got for the following code :
int arr[] = {10,20,30};
cout<<&arr[1]<<"\t"<<&arr[0]<<"\t"<<&arr[1] - &arr[0];
the output which I got was like
0046F7A0 0046F79C 1
I want to know why the difference between the address gave 1 ( I expected 4)...?
Is it something to do with pointer subtraction..?
Yes, this is the result of pointer arithmetic. This is the same reason why arr + 1 would point to arr[1]. Pointer arithmetic is only well-defined when both pointers point to elements in the same array. If two such pointers, P and Q, point to array locations i and j, then P-Q = i-j.
Also, if you look at the differences of the actual addresses printed, they match your expectations - the difference is 4.
You are correct, this has to do with pointer arithmetic. Subtracting two int pointers gives you the difference between them measured in sizeof (int) units. You can get the difference in plain bytes by casting your pointers to char pointers, as chars are guaranteed to be of size 1.
uint arr[] = {10,20,30};
cout << &arr[1] << "\t" << &arr[0] << "\t" << (char*)&arr[1] - (char*)&arr[0];
Output:
0x23fe44 0x23fe40 4
0046F7A0 - 0046F79C is actually 4 but &arr[0]-&arr[1] = (0046F7A0 - 0046F79C)/sizeof(int= 4 bytes) because subtracting two pointers gives you the number of elements between them.

Aligned malloc in C++

I have a question on problem 13.9 in the book, "cracking the coding interview".
The question is to write an aligned alloc and free function that supports allocating memory, and in the answer the code is given below:
void *aligned_malloc(size_t required_bytes, size_t alignment) {
void *p1;
void **p2;
int offset=alignment-1+sizeof(void*);
if((p1=(void*)malloc(required_bytes+offset))==NULL)
return NULL;
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
p2[-1]=p1; //line 6
return p2;
}
I am so confused with the line 5 and line 6. Why do you have to do an "and" since you have already add offset to p1? and what does [-1] mean? Thanks for the help in advance.
Your sample code is not complete. It allocates nothing. It is pretty obvious you are missing a malloc statement, which sets the p1 pointer. I don't have the book, but I think the complete code should goes along these lines:
void *aligned_malloc(size_t required_bytes, size_t alignment) {
void *p1;
void **p2;
int offset=alignment-1+sizeof(void*);
p1 = malloc(required_bytes + offset); // the line you are missing
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
p2[-1]=p1; //line 6
return p2;
}
So ... what does the code do?
The strategy is to malloc more space than what we need (into p1), and return a p2 pointer somewhere after the beginning of the buffer.
Since alignment is a power of two, in binary it has the form of 1 followed by zeros. e.g. if alignment is 32, it will be 00100000 in binary
(alignment-1) in binary format will turn the 1 into 0, and all the 0's after the 1 into 1. For example: (32-1) is 00011111
the ~ will reverse all the bits. That is: 11100000
now, p1 is a pointer to the buffer (remember, the buffer is larger by offset than what we need). we add offset to p1: p1+offset.
So now, (p1+offset) is greater than what we want to return. We'll nil all the insignificant bits by doing a bitwise and: (p1+offset) & ~(offset-1)
This is p2, the pointer that we want to return. Note that because its last 5 digits are zero it is 32 aligned, as requested.
p2 is what we'll return. However, we must be able to reach p1 when the user calls aligned_free. For this, note that we reserved location for one extra pointer when we calculated the offset (that's the sizeof(void*) in line 4.
so, we want to put p1 immediately before p2. This is p2[-1]. This is a little bit tricky calculation. Remember that p2 is defined as void**. One way to look at it is as array of void*. C array calculation say that p2[0] is exactly p2. p2[1] is p2 + sizeof(void*). In general p2[n] = p2 + nsizeof(void). The compiler also supports negative numbers, so p2[-1] is one void* (typically 4 bytes) before p2.
I'm going to guess that aligned_free is something like:
void aligned_free( void* p ) {
void* p1 = ((void**)p)[-1]; // get the pointer to the buffer we allocated
free( p1 );
}
p1 is the actual allocation. p2 is the pointer being returned, which references memory past the point of allocation and leaves enough space for both allignment AND storing the actual allocated pointer in the first place. when aligned_free() is called, p1 will be retrieved to do the "real" free().
Regarding the bit math, that gets a little more cumbersome, but it works.
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
Remember, p1 is the actual allocation reference. For kicks, lets assume the following, with 32bit pointers:
alignment = 64 bytes, 0x40
offset = 0x40-1+4 = 0x43
p1 = 0x20000110, a value returned from the stock malloc()
What is important is the original malloc() that allocates an additional 0x43 bytes of space above and beyond the original request. This is to ensure both the alignment math and the space for the 32bit pointer can be accounted for:
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
p2 = (0x20000110 + 0x43) &~ (0x0000003F)
p2 = 0x20000153 &~ 0x0000003F
p2 = 0x20000153 & 0xFFFFFFC0
p2 = 0x20000140
p2 is aligned on a 0x40 boundary (i.e. all bits in 0x3F are 0) and enough space is left behind to store the 4-byte pointer for the original allocation, referenced by p1.
It has been forever since i did alignment math, so if i f'ed up the bits, please someone correct this.
And by the way, this is not the only way to do this.
void* align_malloc(size_t size, size_t alignment)
{
// sanity check for size/alignment.
// Make sure alignment is power of 2 (alignment&(alignment-1) ==0)
// allocate enough buffer to accommodate alignment and metadata info
// We want to store an offset to address where CRT reserved memory to avoid leak
size_t total = size+alignment+sizeof(size_t);
void* crtAlloc = malloc(total);
crtAlloc += sizeof(size_t); // make sure we have enough buffer ahead to store metadata info
size_t crtArithmetic = reinterprete_cast<int>(crtAlloc); // treat as int for pointer arithmetic
void* pRet = crtAlloc + (alignment - (crtArithmetic%alignment));
size_t *pMetadata = reinterprete_cast<size_t*>(pRet);
pMetadata[-1] = pRet - crtArithmetic- sizeof(size_t);
return pRet;
}
As an example size = 20, alignement = 16 and crt malloc returned address 10. and assuming sizeof(size_t) as 4 byte
- total bytes to allocate = 20+16+4 = 40
- memory committed by crt = address 10 to 50
- first make space in front by adding sizeof(size_t) 4 bytes so you point at 14
- add offset to align which is 14 + (16-14%16) = 16
- move back sizeof(size_t) 4 bytes (i.e. 12) and treat that as size_t pointer and store offset 2 (=12-10) to point to crt malloc
start
Same way, align_free will cast void pointer to size_t pointer, move back one location, read value stored there and adjust offset to move to crt alloc beginning
void* align_free(void* ptr)
{
size_t* pMetadata = reinterprete_cast<size_t*> (ptr);
free(ptr-pMetadata[-1]);
}
Optimization: You don't need sizeof(size_t) extra allocation if your alignment was more than sizeof(size_t)

Pointer addition vs subtraction

$5.7 -
"[..]For addition, either both operands shall have arithmetic or enumeration type, or one operand shall be a pointer to a completely defined object type and the other shall have integral or enumeration type.
2 For subtraction, one of the following shall hold:
— both operands have arithmetic or enumeration type; or
— both operands are pointers to cv-qualified or cv-unqualified versions of the same completely defined object type; or
— the left operand is a pointer to a completely defined object type and the right operand has integral or enumeration type.
int main(){
int buf[10];
int *p1 = &buf[0];
int *p2 = 0;
p1 + p2; // Error
p1 - p2; // OK
}
So, my question is why 'pointer addition' is not supported in C++ but 'pointer subtraction' is?
The difference between two pointers means the number of elements of the type that would fit between the targets of the two pointers. The sum of two pointers means...er...nothing, which is why it isn't supported.
The result of subtraction is distance (useful).
The result of adding a pointer and a distance is another meaningful pointer.
The result of adding 2 pointers is another pointer, this time meaningless though.
It's the same reason there are distinct TimeSpan and DateTime objects in most libraries.
The first thing that comes to mind is that it doesn't make sense to do pointer addition, so it's not supported. If you have 2 pointers 0x45ff23dd, 0x45ff23ed. What does it mean to add them?? Some memory out-of-bounds. And people in standard comittee have not found good enough reasons to support stuff like that, and rather warn you at compile time about possible problem. While pointer subtraction is fine because it indicates memory distance, which is often useful.
Because adding two pointers doesn't make sense.
Consider I have two ints in memory at 0x1234 and 0x1240. The difference between these addresses is 0xc and is a distance in memory. The sum is 0x2474 and doesn't correspond to anything meaningful.
You can however, add a pointer to an integer to get another pointer. This is what you do when you index into an array: p[4] means *(p + 4) which means "the thing stored at the address 4 units past this address."
In general, you can determine the "pointerness" of an arithmetic operation by assigning each pointer a value 1 and each integer a value zero. If the result is 1, you've got a pointer; if it's 0, you've got an integer; if it's any other value, you have something that doesn't make sense. Examples:
/* here p,q,r are pointers, i,j,k are integers */
p + i; /* 1 + 0 == 1 => p+i is a pointer */
p - q; /* 1 - 1 == 0 => p-q is an integer */
p + (q-r); /* 1 + (1-1) == 1 => pointer */
Result of pointer subtraction is number of objects between two memory addresses. Pointer addition doesn't mean anything, this is why it is not allowed.
subtraction of pointers is only defined if they point into the same array of objects. The resulting subtraction is scaled by the size of object they point to. ie, pointer subtraction gives the number of elements between the two pointers.
N.B. No claims on C standards here.
As a quick addendum to #Brian Hooper's answer, "[t]he sum of two pointers means...er...nothing" however the sum of a pointer and an integer allows you to offset from the initial pointer.
Subtracting a higher value pointer from a lower value pointer gives you the offset between the two. Note that I am not accounting for memory paging here; I'm assuming the memory values are both within the accessible scope of the process.
So if you have a pointer to a series of consecutive memory locations on the heap, or an array of memory locations on the stack (whose variable name decays to a pointer), these pointers (the real pointer and the one that decays to a pointer) will point to the fist memory location question (i.e. element [0]). Adding an integer value to the pointer is equivalent to incrementing the index in brackets by the same number.
#include <stdio.h>
#include <stdlib.h>
int main()
{
// This first declaration does several things (this is conceptual and not an exact list of steps the computer takes):
// 1) allots space on the stack for a variable of type pointer
// 2) allocates number of bytes on the heap necessary to fit number of chars in initialisation string
// plus the NULL termination '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// 3) changes the value of the variable from step 1 to the memory address of the beginning of the memory
// allocated in step 2
// The variable iPointToAMemoryLocationOnTheHeap points to the first address location of the memory that was allocated.
char *iPointToAMemoryLocationOnTheHeap = "ABCDE";
// This second declaration does the following:
// 1) allots space on the stack for a variable that is not a pointer but is said to decay to a pointer allowing
// us to so the following iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
// 2) allots number of bytes on the stack necessary to fit number of chars in initialisation string
// plus the NULL termination '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// The variable iPointToACharOnTheHeap just points to first address location.
// It just so happens that others follow which is why null termination is important in a series of chars you treat
char iAmASeriesOfConsecutiveCharsOnTheStack[] = "ABCDE";
// In both these cases it just so happens that other chars follow which is why null termination is important in a series
// of chars you treat as though they are a string (which they are not).
char *iJustPointToMemoryLocationsYouTellMeToPointTo = NULL;
iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
// If you increment iPointToAMemoryLocationOnTheHeap, you'll lose track of where you started
for( ; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '\0' ; ) {
printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iPointToAMemoryLocationOnTheHeap);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '\0' ; i++) {
printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iPointToAMemoryLocationOnTheHeap);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack;
// If you increment iAmASeriesOfConsecutiveCharsOnTheStack, you'll lose track of where you started
for( ; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '\0' ; ) {
printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iAmASeriesOfConsecutiveCharsOnTheStack);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack;
for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '\0' ; i++) {
printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iAmASeriesOfConsecutiveCharsOnTheStack);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i);
}
return 1;
}
The first notable thing we do in this program is copy the value of the pointer iPointToAMemoryLocationOnTheHeap to iJustPointToMemoryLocationsYouTellMeToPointTo. So now both of these point to the same memory location on the heap. We do this so we don't lose track of the beginning of it.
In the first for loop we increment the value we just copied into iJustPointToMemoryLocationsYouTellMeToPointTo (increasing it by 1 means it points to one memory location further away from iPointToAMemoryLocationOnTheHeap).
The second loop is similar but I wanted to more clearly show how incrementing the value is related to the offset, and how the arithmetic works.
The third and fourth loops repeat the process but work on an array on the stack as opposed to allocated memory on the heap.
Note the asterisk * when printing the individual char. This tells printf to output whatever is pointed to by the variable, and not the contents of the variable itself. This is in contrast to the line above where the balance of the string is printed and there is no asterisk before the variable because printf() is looking at the series of memory locations in its entirety until NULL is reached.
Here is the output on ubuntu 15.10 running on an i7 (the first and third loops output starting at an offset of 1 because my loop choice of for increments at the beginning of the loop as opposed to a do{}while(); I just wanted to keep it simple):
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Because the result of that operation is undefined. Where does p1 + p2 point to? How can you make sure it points to a properly initialized memory so that it could be dereferenced later?
p1 - p2 gives the offset between those 2 pointers and that result could be used further on.