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

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.)

Related

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.

How does sizeof operator behaves in below code snippet?

Please explain the OP for below code snippet :
int *a="";
char *b=NULL;
float *c='\0' ;
printf(" %d",sizeof(a[1])); // prints 4
printf(" %d",sizeof(b[1])); // prints 1
printf(" %d",sizeof(c[1])); // prints 4
Compiler interprets a[1] as *(a+1) , so a has some address , now it steps 4 bytes ahead , then it will have some garbage value there so how is the OP 4 bytes , even if I do a[0] , still it prints 4 , although it is an empty string , so how come its size is 4 bytes ?
Here we are finding out the size of the variable the pointer is pointing to , so if I say size of a[1] , it means size of *(a+1), Now a has the address of a string constant which is an empty string , after I do +1 to that address it moves 4 bytes ahead , now its at some new address , now how do we know the size of this value , it can be an integer , a character or a float , anything , so how to reach to a conclusion for this ?
The sizeof operator does not evaluate its operand except one case.
From the C Standard (6.5.3.4 The sizeof and alignof operators)
2 The sizeof operator yields the size (in bytes) of its operand, which
may be an expression or the parenthesized name of a type. The size is
determined from the type of the operand. The result is an integer.
If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the
result is an integer constant.
In this code snippet
int *a="";
char *b=NULL;
float *c='\0' ;
printf(" %d",sizeof(a[1])); // prints 4
printf(" %d",sizeof(b[1])); // prints 1
printf(" %d",sizeof(c[1])); // prints 4
the type of the expression a[1] is int, the type of the expression b[1] is char and the type of the expression c[1] is float.
So the printf calls output correspondingly 4, 1, 4.
However the format specifiers in the calls are specified incorrectly. Instead of "%d" there must be "%zu" because the type of the value returned by the sizeof operator is size_t.
From the same section of the C Standard
5 The value of the result of both operators is implementation-defined,
and its type (an unsigned integer type) is size_t, defined in
<stddef.h> (and other headers).
This is all done statically, i.e. no dereferencing is happening at runtime. This is how the sizeof operator works, unless you use variable-length arrays (VLAs), then it must do work at runtime.
Which is why you can get away with sizeof:ing through a NULL pointer, and other things.
You should still be getting trouble for
int *a = "";
which makes no sense. I really dislike the c initializer too, but at least that makes sense.
sizeof operator happens at compilation (except for VLA's). It is looking at the type of an expression, not the actual data so even something like this will work:
sizeof(((float *)NULL)[1])
and give you the size of a float. Which on your system is 4 bytes.
Live example
Even though this looks super bad, it is all well defined, since no dereference ever actually occurs. This is all operations on type information at compile time.
sizeof() is based on the data type, so whilst it's getting the sizes outside the bounds of memory allocated to your variables, it doesn't matter as it's worked out at compile time rather than run time.

Why doesn't sizeof work as expected?

#include <stdio.h>
int main(void)
{
printf("%d", sizeof (getchar()) );
}
What I expect is,
1. Type input.
2. Read input and return input value.
3. Evaluate sizeof value.
4. Print the sizeof value.
But the first step never happens.
Why doesn't the first step happen?
The sizeof operator does not evaluate its operand unless its type is a variable length array type: It looks at the type and returns the size. This is perfectly safe:
char *ptr = NULL; // NULL ponter!
printf("%zu", sizeof *ptr);
It will return 1, since it does not have to evaluate the expression to know the answer.
What I expect is, 1. Type input. 2. Read input and return input value. 3. Evaluate sizeof value 4. Print the sizeof value.
But the first step never happens. Why doesn't the first step happen?
Because, with a very few exceptions, the sizeof operator does not evaluate its operand. Your usage is not one of the exceptions. Not evaluating getchar() means getchar() is not called.
In any event, I'm not sure what you expect from your code. Even if getchar() were called, the result always has the same type (int), which does not depend on the input.
Do also pay attention to #P.P.'s comments. Your printf() format does not match the type of the data being printed, size_t. As he observes, the printf() call has undefined behavior as a result.
In C the sizeof operator is evaluated at run-time only for Variable Size Arrays (VLA). In all other cases the operator does nor evaluate its operand. It deduces the type of the expression and returns the size of object of the deduced type.
Because getchar() return type is an int, not a char. sizeof(int) is 4 on your platform.
Also, you should use %zu to print size_t values. Using incorrect format specifier is technically undefined behaviour.

Storage of variables and dereferencing them

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.

Resources