Distance between arbitrary pointers - c

I am trying to print the distance between two pointers, but I have found that sometimes the code doesn't work well.
#include <stdio.h>
#include <math.h>
/**
* Print the distance between 2 pointers
*/
void distance(int * a0, int * a1){
size_t difference = (size_t) a1 - (size_t) a0;
printf("distance between %p & %p: %u\n" ,a0, a1, abs((int) difference));
}
Trying this works perfectly!!
int main(void){
int x = 100;
int y = 3000;
distance(&x, &y);
return 0;
}
printing (example):
distance between 0028ff18 & 0028ff14: 4
But start to going wrong with this code
int main(void){
int x = 100;
int p = 1500;
int y = 3000;
distance(&x, &y);
p = p + 2; // remove unused warning
// &p
return 0;
}
printing (example):
distance between 0028ff18 & 0028ff14: 4
When it has to print 8 because of an integer separating this values!
But if I uncomment //&p, it works again.
It is as if the variable p does not exist until its memory address is used.
I'm using gcc 4.9.3 on windows 7 (64 bits)

p is not used in your program and is likely to be optimized out. Anyway this is implementation details and the compiler is free to even change the order of the x and y objects in memory.

it is as if the variable p not exist until its memory address is used
gcc will optimize away any variables that do not affect the behavior of the program.
Secondly, you cannot assume that variables are laid out in any particular order in memory, and the "distance" between any two may be completely meaningless. The only time you can rely on the distance between two pointers to have any meaning is when they are both pointing to elements within the same array object.

Pointer arithmetic is valid only between pointers to elements of the same array, or one past the end. Going against that will have unpredictable and implementation-defined results. In this case, it seems you have found the answer yourself: gcc is optimizing p away, but in any case, you can't assume any specific order of variables in memory, yet alone do pointer arithmetic.
Also, this:
size_t difference = (size_t) a1 - (size_t) a0;
Should be:
ptrdiff_t difference = a1 - a0;
The correct type for the difference between two pointers is ptrdiff_t (defined in stddef.h). You shouldn't cast the pointers to size_t because pointer values are not necessarily representable in a size_t (if you want a numeric type to convert pointers to, use uintptr_t or intptr_t).
The %p format specifier expects a void *, so you should cast the pointers, and the correct format specifier for ptrdiff_t is %td:
printf("distance between %p & %p: %td\n", (void *) a0, (void *) a1, difference);

Related

Pointer and memory arithmetic in C

Taking the following program:
#include <stdio.h>
#include <stddef.h>
int main(void) {
int *a = &(int) {4};
ptrdiff_t b = (a+1) - a;
int c = (char*) (a+1) - (char*) a; // actual memory difference
printf("The first memory address is at: %p and the one after is: %p\n"
"The difference with pointer arithmetic is: %td\n"
"And actual difference in memory address is: %d\n",
a, a+1, b, c);
}
The first memory address is at: 0x7fff9cb3a1e0 and the one after is: 0x7fff9cb3a1e4
The difference with pointer arithmetic is: 1
And actual difference in memory address is: 4
Is this the proper way to do:
ptr arithmetic: ptrdiff_t b = (a+1) - a;
actual arithmetic between memory addresses: (char*) (a+1) - (char*) a;
Formatters: %td for ptrdiff_t, and %d for actual memory difference (wouldn't this just be an int or is there a better formatter for this?)
In
int *a = &(int) {4};
{4} is a compound literal, its address can be taken just as you did.
ptrdiff_t b = (a+1) - a;
Pointer subtraction is well defined when the pointers involved point to elements of the same array, or the element past the last. Pointer a can point to an object or an array of objects. An object and array are different types, but a pointer doesn't distinguish the two, so that a scalar is treated as an array of 1 element. This code is well defined.
In
int c = (char*) (a+1) - (char*) a;
the type of difference is ptrdiff_t, which is int only on a 32-bit architecture.
Anytime you take the difference of two pointers, the result has type ptrdiff_t.
So both b and c should be of this type, and %td is the format specifier you would use to print it.
Also, the %p format specifier expects a void * as a argument. And since the implicit conversion to/from void * can't happens with variadic parameters, this is one of the few times an explicit cast to void * is required.

Typecasting the Reference of a Double Variable

I have a question about typecasting the reference of a variable of type double. What is exactly happening when &d becomes typecasted into an unsigned char*? How is typecasting the address of a variable valid and what is it actually doing?
#include <stdio.h>
int main(void) {
// examining object representation is a legitimate use of cast
double d = 3.14;
printf("The double %.2f(%a) is: ", d, d);
for(size_t n = 0; n < sizeof d; ++n)
printf("0x%02x ", ((unsigned char*)&d)[n]); // <--
}
And how do the array brackets work in this case?
&d represents a pointer to the location at which the value of the double is stored.
A pointer is simply a memory address (likely a 64-bit value) which identifies a location in memory, regardless of the type of data that is being pointed to.
This makes the cast from a double * to an unsigned char * possible as the representation of both is simply a word of memory. The array notation then dereferences this pointer and adds n times the size of a char (1 byte), and then reads the next char-many bits.
The array notation a[n] is equivalent to *(a+n). It is simply adding an offset to the pointer referred to by a.

Subtracting two pointers giving unexpected result

#include <stdio.h>
int main() {
int *p = 100;
int *q = 92;
printf("%d\n", p - q); //prints 2
}
Shouldn't the output of above program be 8?
Instead I get 2.
Undefined behavior aside, this is the behavior that you get with pointer arithmetic: when it is legal to subtract pointers, their difference represents the number of data items between the pointers. In case of int which on your system uses four bytes per int, the difference between pointers that are eight-bytes apart is (8 / 4), which works out to 2.
Here is a version that has no undefined behavior:
int data[10];
int *p = &data[2];
int *q = &data[0];
// The difference between two pointers computed as pointer difference
ptrdiff_t pdiff = p - q;
intptr_t ip = (intptr_t)((void*)p);
intptr_t iq = (intptr_t)((void*)q);
// The difference between two pointers computed as integer difference
int idiff = ip - iq;
printf("%td %d\n", pdiff, idiff);
Demo.
This
int *p = 100;
int *q = 92;
is already invalid C. In C you cannot initialize pointers with arbitrary integer values. There's no implicit integer-to-pointer conversion in the language, aside from conversion from null-pointer constant 0. If you need to force a specific integer value into a pointer for some reason, you have to use an explicit cast (e.g. int *p = (int *) 100;).
Even if your code somehow compiles, its behavior in not defined by C language, which means that there's no "should be" answer here.
Your code is undefined behavior.
You cannot simply subtract two "arbitrary" pointers. Quoting C11, chapter ยง6.5.6/P9
When two pointers are subtracted, both shall point to elements of the same array object,
or one past the last element of the array object; the result is the difference of the
subscripts of the two array elements. The size of the result is implementation-defined,
and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. [....]
Also, as mentioned above, if you correctly subtract two pointers, the result would be of type ptrdiff_t and you should use %td to print the result.
That being said, the initialization
int *p = 100;
looks quite wrong itself !! To clarify, it does not store a value of 100 to the memory location pointed by (question: where does it point to?) p. It attempts to sets the pointer variable itself with an integer value of 100 which seems to be a constraint violation in itself.
According to the standard (N1570)
When two pointers are subtracted, both shall point to elements of
the same array object, or one past the last element of the array
object; the result is the difference of the subscripts of the two
array elements.
These are integer pointers, sizeof(int) is 4. Pointer arithmetic is done in units of the size of the thing pointed to. Therefore the "raw" difference in bytes is divided by 4. Also, the result is a ptrdiff_t so %d is unlikely to cut it.
But please note, what you are doing is technically undefined behaviour as Sourav points out. It works in the most common environments almost by accident. However, if p and q point into the same array, the behaviour is defined.
int a[100];
int *p = a + 23;
int *q = a + 25;
printf("0x%" PRIXPTR "\n", (uintptr_t)a); // some number
printf("0x%" PRIXPTR "\n", (uintptr_t)p); // some number + 92
printf("0x%" PRIXPTR "\n", (uintptr_t)q); // some number + 100
printf("%ld\n", q - p); // 2

Difference between returning address and returning pointer

#include <stdio.h>
#include <conio.h>
void main()
{
int m = 20;
int n = 30;
int *x = (int *)m;
int *y = (int *)n;
printf("%d", y-x); //Output is 5
}
How is the output 5? One of the reason could be that x and y consider 20 and 30 as address and during pointer arithmetic the value could be (30-20)/(size of int) which is 10/2 = 5.
My doubt is what is the difference between returning pointer and returning address ? Why the address of m was not stored in pointer variable x?
The behaviour of your program is undefined.
Pointer arithmetic - including the difference between two pointers - is only valid within arrays (including one past the end of the arrray), or between the address of a scalar and one past that.
The behaviour of your casts from an int to a pointer to an int is also undefined.
If you do have a valid difference between two pointers, then use the format specifier %td to output it.
The address of m wasn't stored in x because you didn't assign it the address of m. You assigned it the value of m. The cast you applied masked the fact that you attempted to assign an integer to a pointer, which the compiler would have warned you about.
If you want to use the address of a variable, use the address-of operator &:
int *x=&m;
int *y=&n;
You are correct regarding why the output is 5. The values of m and n are assigned to pointers and are thus treated as addresses.
Note however that pointer subtraction is undefined unless both operands point to members of the same array (or one past the end of that array). Also note that it is undefined behavior to print pointers with the %d format specifier. You need to use %p instead, and you need to cast the given parameter to void * (one of the rare cases where a cast to/from void * is required).
if you want to store the adress of m in x simply do int* x = &m;
the casting to int type pointer here is causing the problem in my optinion.
also if you want to calculate the values of y-x then do *y-*x. as it is in your code you calculate the adress of n - the adress of m.
You have to use %p if you would like to show the address that is stored in a pointer. In your code, you should use * before the name of the pointer.
#include<stdio.h>
int main()
{
int m=20; int n=30;
int *x= &m;
int *y= &n;
printf("\n%d", (*y)-(*x)); //Output is 10
printf("\n%p address of m", x);
printf("\n%p address of n", y);
return 0;
}
int *x=(int *)m;
int *y=(int *)n;
By doing this what you would be doing is -
in a pointer x - you put the value m ( Treat this as a pointer as well as you are doing int *.
Hence x would have a value of 20 ( 0x14 ) and y would have a value of 30 ( 0x1E).
Pointer arithmetic is bit different from normal arithmetic operation
https://www.tutorialspoint.com/cprogramming/c_pointer_arithmetic.htm
And about assigning address you should use a &variable to do that

C Programming Simple Pointers

I'm a beginner at learning pointers. Here is my code. (Note: I'm still trying to get my head around pointers so my code won't be clean.)
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]){
int a = 1;
char b = 's';
double c = 3.14;
int *ptra;
int *ptrb;
int *ptrc;
ptra = &a;
ptrb = &b;
ptrc = &c;
printf("I initialised int as %d and char as %c and double as %.2f\n", a, b, c);
printf("The address of A is %p and the contents of A is %d\n", ptra, *ptra);
printf("The address of B is %p and the contents of B is %c\n", ptrb, *ptrb);
printf("The address of C is %p and the contents of C is %.2f\n", ptrc, *ptrc);
I expected the following output:
I initialised int as 1 and char as s and double as 3.14
The address of A is 0xbf933094 and the contents of A is 1
The address of B is 0xbf933093 and the contents of B is s
The address of C is 0xbf933098 and the contents of C is 3.14
But instead I get this:
I initialised int as 1 and char as s and double as 3.14
The address of A is 0xbf933094 and the contents of A is 1
The address of B is 0xbf933093 and the contents of B is s
The address of C is 0xbf933098 and the contents of C is 427698.00000
Can someone help for the large number I got when printing the contents of C? Why don't I get 3.14? (The number is actually longer than that but it didn't fit into this textbox. :-))
You are declaring ptra, ptrb and ptrc as pointers to ints. But the type of a pointer is based on what it points to, so it really should be:
int *ptra;
char *ptrb;
double *ptrc;
In your specific case, your program is trying to interpret a double value through an int pointer. Since the sizes of these data types differ on your machine, some of the bits of the double get discarded and you end up with the strange number you're seeing.
This may not always happen the same way - the result of accessing something through the wrong type of pointer is not defined by the C language, but it still might compile. C programmers refer to this as undefined behaviour (a phrase you should really come to terms with if you want to learn C!).
There is also the fact that when you call printf, you need to give it variables of the type it expects from the format string. So if you give it a format string where the first placeholder is %.f, you must give it a first argument that's a double. If you don't, printf will also exhibit undefined behaviour and could do anything (the undefined behaviour may be strange output, crashing, or simply putting out the number you expect... until the worst possible moment).
Your pointers are all of type int. That is not correct. Replace those by
int *ptra;
char *ptrb;
double *ptrc;
Because your pointers are all int*. If you want it to dereference to a double, you need it to be double*. Your compiler should have warned you about incompatible pointer assignment.
You should declare pointers using the corresponding type.
int *ptra;
char *ptrb;
double *ptrc;
you need to change your pointer type to match your data type so the size will be set accordingly.
char *ptrb;
double *ptrc;
If I can just say a few words about typed pointers.
Pointers with a type (as opposed to void* pointers) know how many bytes to advance in memory. For example on 32 bit systems and integer pointer would typically advance four bytes in memory when iterating through an array containing integer values.
A char pointer (guaranteed by the C standard to be always 1 byte) would naturally advance 1 byte at a time.
Let me illustrate this with a small code snippet:
#include <stdio.h>
int main()
{
char array [] = "This is a char array.";
int* int_ptr;
char* char_ptr;
char_ptr = array; /* This is okay, we have a char array and we assign its address to a char pointer */
int_ptr = array; /* It will complain but let's go along with it */
printf("%p, %p, %p\n", array, char_ptr, int_ptr); /* They should all point to the same address in memory */
printf("%p\n", ++char_ptr); /* it will have advanced by one byte */
printf("%p\n", ++int_ptr); /* it will have advance by four bytes */
return 0;
}
I have the following output on my machine:
$ ./a.out
0xbf8b85d2, 0xbf8b85d2, 0xbf8b85d2
0xbf8b85d3
0xbf8b85d6
As you can see they have indeed advanced as we predicted. It is fairly obvious this can cause all sorts of problems when we start dereferencing our pointers and they don't match the underlying type.
Regarding void* pointers, arithmetic on them is illegal.
here the pointer ptrc is referring to the addressof varaible whose data type is integer but you are using it for double.

Resources