Storage of variables and dereferencing them - c

Based on the following snippet in C
int c1,c2;
printf("%d ",&c1-&c2);
Output : -1
Why does this code not return a warning saying the format %d expects of type int but it's getting a (void *) instead.
Why does it return -1 as an answer(and not 1)? Even if it's subtracting addresses it should be -4 and not -1. When I change the printf statement to printf("%d ",&c2 - &c1) I get 1 and not any random value! Why?
If I change the printf statement as printf("%d ",(int)&c1 - (int)&c2) am I typecasting an address to an integer value? Does that mean the value of the address stored as hexadecimal is now converted to int and then subtracted?

1) It's getting an int. Pointer-to-something minus pointer-to-something is an integer value (type ptrdiff_t). It tells you how many elements away are two pointers pointing into the same array.
2) As the two pointer do not point into the same array, the difference is undefined. Any value can be obtained.
3) Yes. But the "hexadecimal" part is incorrect. Addresses are stored in bits/binary (as are integers). What changes is the interpretation by your program. This is independent of the representation (hex/dec/oct/...).

There are multiple cases of undefined behavior here.
If we go to the draft C99 standard section 6.5.6 Additive operators it says (emphasis mine):
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
header. If the result is not representable in an object of that type,
the behavior is undefined.
Although, what you have is undefined behavior since the pointers do not point to elements of the same array.
The correct format specifier when using printf for ptrdiff_t would be %td which gives us a second case of undefined behavior since you are specifying the incorrect format specifier.

1) A void* is nothing but an adress. An adress is an number (a long). There, the adress is implicitly cast to an int.
2) In memory, your variable aren't store in the same order than in
your code ;). Furthemore, for the same reason :
int a[2];
a[0] = 3;
*(a + 1) = 5; // same that "a[1] = 5;"
This code will put a "5" in the second case. Cause it will actually do :
*(a + 1 *sizeof(*a)) = 5;
3) Hexadecimal is a number representation. It can be store in a int ! Example :
int a = 0xFF;
printf("%d\n", a); // print 255
I hope that i have answered your questions.

1) Some compilers do issue warnings for malformed printf arguments, but as a variadic function, the run-time has no way of checking that the arguments are of the type specified by the format string. Any mismatch will issue undefined behaviour as the function attempts to cast such an argument to an incorrect type.
2) You say the result should be -4 but that's not correct. Only arrays are guaranteed to have their pointers aligned contiguously. You cannot assume that c2 is at (&c1 + 1).
3) (int)&c1 is converting the address of c1 to an int. That's again, in general, undefined behaviour since you don't know that int is big enough to hold the pointer address value. (int might be 32 bits on a 64 bit chipset). You should use intptr_t in place of the int.

Related

But what exactly is printed when %d is used instead of %p?

As the code shows this random, seemly trash, number printed when %d is called is spaced 'sizeof(type)' numbers of the next element of a vector.
BUT if I convert the hexadecimal address of the first element, it does not match with the number.
So if it isn't the address represented in decimals, then what this printed number exactly is?
0x7ffcd6b154c0 == 140723910431936
Whereas the printed decimal was -693021504
#include <stdio.h>
void main(){
int x[] = {2, 5}, *ptr = &x;
printf("%d\n", ptr);
printf("%p\n", ptr);
printf("\n%d %d\n", ptr, (ptr + 1));
}
It's using the D6B154C0, from the 7ffcd6b154c0 which is -693021504 as a 32 bit number.
It's a dice-roll because you invoke Undefined Behavior:
C11 Standard - 7.21.6.1 The fprintf
function(p9) "If
a conversion specification is invalid, the behavior is undefined. If
any argument is not the correct type for the corresponding conversion
specification, the behavior is undefined."
More likely than not what will print is the actual pointer address on system where pointers are the same size as int (e.g. x86), otherwise, you will print 4-bytes out of the 8-byte pointer if you are lucky.
The size of a typical integer is 4 bytes(range of integers being -2,147,483,648 to 2,147,483,647) for modern compilers.
And when "%d" is used as a type specifier, it prints the address pointed by ptr as a 32-bit integer.
But
Since the size of ptr is typically 8 bytes the value of address that is in Hexadecimal can't be converted to Decimal completely,
Thus an inconsistency is introduced.
Understand it another way:
140723910431936 is nowhere between the range of integers and printing it with type specifier "%d" won't provide correct result

Meaning of &st[3]-st in printf("%ld", &st[3]-st)

I'm new to C and in an exercise, I have to write the output of the following portion of code, which is 3. But I couldn't understand why it is that.
int main() {
char st[100]="efgh";
printf ("\n%ld\n",&st[3]-st);
return 0;
}
When you use an array in an expression, unless it is the argument of & or sizeof, it evaluates to the address of its first element.
Thus &st[3] - st evaluates as &st[3] - &st[0], which is just pointer arithmetic: The difference between the addresses of two array elements is just the difference between their indices, i.e. 3 - 0, which gives 3.
The only problem is that the result is of type ptrdiff_t, but printf %ld expects a long int. If those types are different on your machine, it won't work. In a printf() format string, the correct length modifier for ptrdiff_t is t — use "\n%td\n".
By definition, &st[3] is the same as st+3. st+3-st is 3. (st in that expression decays from array to a pointer. For portability, the printf format string should technically have %td instead of %ld.)

Difference b/w getting an address of variable using %p and %d

Here is an example
#include <stdio.h>
int main()
{
int a;
printf("%d\n",&a);
printf("%p\n",&a);
return 0;
}
======Output=======
-2054871028
0x7ffd8585280c
Do these two address point to same address in RAM ?
And how can i get the value by using each one of them, especially the second one.
%d format specifier is used to output a signed decimal integer.
From C Standard#7.21.6.1p8
d,i
The int argument is converted to signed decimal in the style [-]dddd. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it is expanded with leading zeros. The default precision is 1. The result of converting a zero value with a precision of zero is no characters.
%p prints the pointer.
From C Standard#7.21.6.1p8
p
The argument shall be a pointer to void. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner. [emphasis mine]
This statement
printf("%d\n",&a);
lead to undefined behavior because %d is not valid for printing a pointer.
From C Standard#7.21.6.1p9
If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
When you take the address of the variable a by writing &a, what you're really doing is generating a pointer to a.
%p is designed for printing pointers. You should use %p to print pointers.
%d is not designed for printing pointers. It tries to print their values as signed decimal, which can be confusing (as you've seen), and it may not print the entire value, on a machine where pointers are bigger than integers. (For example, if you try to print pointers with %d in most "64 bit" environments, you can get even stranger results -- and that might be part of what happened here.)
This is an easy mistake to make. Good compilers should warn you about it. Mine says "warning: format specifies type 'int' but the argument has type 'int *'".
But yes, both 0x7ffd8585280c and -2054871028 do "point to the same address in RAM", because they're both the same number, the same address. (Well, they're trying to be the same address. See footnote below.)
I'm not sure what you mean by "And how can I get the value". Are you trying to get the value of the pointer, or the value of what the pointer points to?
You've already got the value of the pointer -- it's the address 0x7ffd8585280c. And since we know it points to the variable a, we know the value it points to, too. Things will be a bit more clear if we do it like this:
int a = 5;
int *ip = &a;
printf("value of pointer: %p\n", ip);
printf("pointed-to value: %d\n", *ip);
Without the explicit pointer variable ip, we could write
int a = 5;
printf("value of pointer: %p\n", &a);
printf("pointed-to value: %d\n", *&a);
But that's pretty silly, because the last line is equivalent to the much more straightforward
printf("pointed-to value: %d\n", a);
(Taking the address of a variable with & and then grabbing the contents of the pointer using * is a no-op: it's a lot like like writing a + 1 - 1.)
Footnote: I said that 0x7ffd8585280c and -2054871028 were the same number, but they're not, they're just trying to be. 0x7ffd8585280c is really -140748133160948, and -2054871028 is really 0x8585280c, which is the lower-order 8 digits of 0x7ffd8585280c. It looks like %p on your machine is printing pointers as 48-bit values by default. I was about to be surprised by that, but then I realized my Mac does the same thing. Somehow I'd never noticed that.

Why does %d,and %p give different values? [duplicate]

This question already has answers here:
What happens when I use the wrong format specifier?
(2 answers)
Correct format specifier to print pointer or address?
(5 answers)
Closed 4 years ago.
As you can see in the program, The first output is 6356744 and the second output is 0060FF08, why is it different? Is the %d typecasting it into an integer, if so, how?
#include<stdio.h>
int main()
{
int *a;
int b = 7;
a = &b;
printf(" The value of a = %d",a);
printf("\n The value of a= %p",a);
}
Printing a pointer with %d is formally undefined behavior, meaning anything can happen, including a program crash. Your program will for example likely break when you compile it as a 64 bit application, where int is 32 bits but a pointer is likely 64 bits. Therefore, always use %p and never anything else when printing a pointer.
There is no implicit conversion taking place - the printf family of functions doesn't have that kind of intelligence - it doesn't know the type passed. With the format specifier, you tell the function which type it is getting. And if you lie to printf and say "I'll give you an int" and then give it a pointer, you unleash bugs. This makes the printf family of functions very dangerous in general.
(The only implicit conversion that take place in printf is when you pass small integer types or float, in which case the "default argument promotions" take place and promote the parameter either to int or double. This is not the case here, however.)
In this specific case, you happened to get the decimal representation of 0x0060FF08, which is by no means guaranteed.
Pedantically, you should also cast the pointer to type (void*) since this is what %p expects.
%p prints a pointer and it's not necessarily hexadecimal, or even a number
If format specifiers do not match the datatype of the provided parameter, you yield undefined behaviour. %d expects an integral value, such that when you pass a pointer value, you get undefined behaviour (cf., for example, cppreference.com-printf):
...If any argument after default argument promotions is not the type
expected by the corresponding conversion specifier, or if there are
fewer arguments than required by format, the behavior is undefined.
The (only) correct format specifier for printing pointer values is %p, usually printing the address in hex format.
One of the undefined behaviours is that %d takes the pointer value as an 32/64 bit integral value and hence prints decimals, which - if you printed it using the correct %p-format - corresponds to the hex-value of the address (yet in decimal format).
%d prints an int, in decimal.
%p prints a pointer (strictly speaking, a void * pointer) in an implementation-defined way, typically in hexadecimal.
On a machine where ints and pointers have different sizes, trying to print a pointer using %d will typically give a meaningless result. For example, on a lot of machines these days, ints are 32 bits while pointers are 64 bits. So if you try to print a pointer using %d, what you might get is half the pointer value, in decimal.
(Strictly speaking, trying to print a pointer using %d is undefined, no matter what the relative sizes are.)
Bottom line, use the right printf specifier for the job. Use %d to print ints. Use %p to print pointers. Don't try to mix 'n' match.

Divide int and store back into original variable

I have a function that does some stuff (that portion is irrelevant) then it sets a value called num, then prints that value. My issue is as a final step of my function before printing the value it must divide by 4. That is where I am hitting a snag... See below:
int *num = 0;
.
. //num is determined here...
.
num = num/4; // <-- My issue
printf("%d\n", num);
The error I get is:
error: invalid operands to binary / (have 'int *' and 'int')
Which, from so searching means I need to deference it like so:
num = (*num)/4;
Though this also does not work correctly (it doesn't give any errors, it actually puts my code into an infinite loop and Windows stops it). What I am trying to do is take the value stored at num (the address) and divide it by for and then set num value to that new value. But I see to be struggling with getting this work correctly.
num is of type pointer. So num = num /4 ; is a very unlikely thing to do. It is not even allowed.
As per standard 6.5.5Each of the operands shall have arithmetic type.(Multiplicative expression)
Compiler also tells you that thing error: invalid operands to binary / (have 'int *' and 'int').
/ takes two arguments. But compiler is looking at the operands and it sees that they are of type *. So it complains because it not allowed as oer standard.
num =*num/4; is basically assigning an integer result to a pointer variable. Which even though may not lead to desired behavior but allowed. (We also do int *p =NULL)
This statement below declares a pointer
int *num = 0;
meaning points to some memory where value is there;
This statement below is very interesting
.
. //num is determined here...
.
when you say num is determined here there are couple of ways to interpret this
is there an assignment to value as below in which case RHS must be value
*num = some value;
or assignment to pointer as below in which case RHS must be an address or pointer.
num = some address;
Finally when you do this, you are operating on pointer because num represents a pointer where as *num represents value and below statement is invalid for pointers as Multiplication or Division of a pointer is not allowed in C
num = num/4; // <-- My issue
also when you are printing here, you are basically printing the address of num than printing the value.
printf("%d\n", num);

Resources