What if I use %d instead of %ld in c? - c

I am a beginner and look at the book "C primer plus" and was confused about this saying
"To print a long value, use the %ld format specifier. If int and long are the same size on your system, just %d will suffice, but your program will not work properly when transferred to a system on which the two types are different, so use the %ld specifier for long."
I tested it myself as the following code:
int num = 2147483647;
short num_short = 32767;
long num_long = 2147483647;
printf("int: %d; short: %d; long: %d", num, num_short, num_long);
the program worked okay.
I searched online and found this question:%d with Long Int
An answer said:
it works because long int and int are actually the same numeric representation: four byte, two's complement. With another platform (for example x86-64 Linux), that might not be the case, and you would probably see some sort of problem
My computer is 64-bits. The int is 32-bits. The long int is 32-bits. The short int is 16-bits (I checked it. It's all right). So you can see that the int type and short int type is different. This answer also said it would cause error if these types have different numeric representation. So what does the author mean of his saying?

Whatever happens depends on the implementation. The standard is quite clear on this, and it does invoke undefined behavior.
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.
7.21.6.1p9
Roberto explained in detail why it "works" in his answer

What the author of your book says is correct.
Your test invokes undefined behavior, as the standard doesn't specify what will happen when a wrong format specifier is used. It means that the implementation of each compiler will be free to interpret how to manage such cases.
Nevertheless, with some common sense (and with a little knowledge about how things work "in practice") the behavior you experience can be explained.
Every format specifier says to the compiler where, in the list of parameters, it can find the parameter to be printed. For this reason, and that's the core of your book's assertion, passing an integer with an unexpected length makes the following format specifiers retrieve the parameter in the wrong place.
Example 1: a sequence of 32 bits integers
Let's start with a "normal" sequence, in order to have a base example.
int a=1, b=2;
printf("%d %d\n", a, b);
The format specifies tells the compiler that two 4 bytes integers will be found after the format strings. The parameters are actually placed in the stack in the expected way
-------------------------------------------------
... format string | a (4 bytes) | b (4 bytes) |
-------------------------------------------------
Example 2: why printing a 64-bytes long with %d doesn't work?
Let's consider the following printf:
long int a=1;
int b=2;
printf("%d %d\n", a, b);
The format specifies tells the compiler that two 4 bytes integers will be found after the format strings. But the first parameter takes 8 bytes instead of 4
-------------------------------------------------------------
... format string | a (4 bytes) + (4 bytes) | b (4 bytes) |
-------------------------------------------------------------
^ ^
Compiler Compiler
expects 'a' expects 'b'
here here
So the output would be
1 0
because b is searched where the 4 most significant bytes of a are, and they are all 0s.
Example 3: why do printing a 16-bytes short with %d work?
Let's consider the following printf:
short int a=1;
int b=2;
printf("%d %d\n", a, b);
The format specifies tells the compiler that two 4 bytes integers will be found after the format strings. The first parameter takes only two bytes instead of 4, but... we are lucky, because on a 32 bits platform parameters are 4-bytes aligned!
---------------------------------------------------------------------
... format string | a (2 bytes) | (2 bytes PADDING) | b (4 bytes) |
---------------------------------------------------------------------
^ ^
Compiler Compiler
expects 'a' expects 'b'
here here
So the output would be
1 1
because b is searched in the correct place. We would have problems in the representation of a if the alignment was done with non-0 padding, but it's not the case.
So, the real difference in case of %d used for short is just the representation of signed bytes, as the sign bit is expected to be in the most significant one.

Related

I'm curious about the c language printf format output value

I have a question while studying C language.
printf("%d, %f \n", 120, 125.12);
The output value in this code is:
120, 125.120000
That's it. And
printf("%d, %f \n", 120.1, 125.12);
The output value of this code is:
1717986918, 0.000000
That's it.
Of course, I understand that the output value of %d in front of us was a strange value because I put a mistake in the integer format character. But I don't know why %f is 0.000000 after that. Does the character on the back have an effect on the value of the previous character?
I'm asking because I'm curious because it keeps coming out like this even if I put in a different price.
printf is a variadic function, meaning its argument types and counts are variable. As such, they must be extracted according to their type. Extraction of a given argument is dependent on the proper extraction of the arguments that precede it. Different argument types have different sizes, alignments, etc.
If you use an incorrect format specifier for a printf argument, then it throws printf out of sync. The arguments after the one that was incorrectly extracted will not, in general, be extracted correctly.
In your example, it probably extracted only a portion of the first double argument that was passed, since it was expecting an int argument. After that it was out of sync and improperly extracted the second double argument. This is speculative though, and varies from one architecture to another. The behavior is undefined.
why %f is 0.000000 after that.
According to the C standard because %d expects an int and you passed a double, undefined behavior happens and anything is allowed to happen.
Anyway:
I used http://www.binaryconvert.com/convert_double.html to convert doubles to bits:
120.1 === 0x405E066666666666
125.12 === 0x405F47AE147AE148
I guess that your platform is x86-ish, little endian, with ILP32 data model.
The %d printf format specifier is for int and sizeof(int) is 4 bytes or 32 bits in LP64. Because the architecture is little endian and the stack too, the first 4 bytes from the stack are 0x66666666 and the decimal value of it is printed out which is 1717986918.
On the stack are left three 32-bit little endian words, let's split them 0x405E0666 0x147AE148 0x405F47AE.
The %f printf format specifier is for double and sizeof(double) on your platform is 8 or 64 bits. We already read 4 bytes from the stack, and now we read 8 bytes. Remembering about the endianess, we read 0x147AE148405E0666 that is converted to double. Again I used this site and converted the hex to a double, it resulted in 5.11013578783948654276686949883E-210. E-210 - this is a very, very small number. Because %f by default prints with 6 digits of precision, the initial 7 digits of the number are 0, so only zeros are printed. If you would use %g printf format specifier, then the number would be printed as 5.11014e-210.
This can be attributed to the fact that the printf function is not type-safe, i.e. there is no connection between the specifiers in the format string and the passed arguments.
The function converts all integer arguments to int (4 bytes) and all floats to double (8 bytes). As the types are guessed from the format string, a shift in the data occurs. It is likely that the 0.000000 appears because what is loaded corresponds to a floating-point zero (biaised exponent = 0).
Try with 1.0, 1.e34.

Sizeof with different specificators [duplicate]

This question already has answers here:
Using %f to print an integer variable
(6 answers)
Closed 3 years ago.
I want to know why sizeof doesn't work with different types of format specifiers.
I know that sizeof is usually used with the %zu format specifier, but I want to know for my own knowledge what happens behind and why it prints nan when I use it with %f or a long number when used with %lf
int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%d\n", sizeof(a + d)); // prints normal size of expression
printf("%lf\n", sizeof(s)); // prints big number
printf("%f", sizeof(d)); // prints nan
sizeof evaluates to a value of type size_t. The proper specifier for size_t in C99 is %zu. You can use %u on systems where size_t and unsigned int are the same type or at least have the same size and representation. On 64-bit systems, size_t values have 64 bits and therefore are larger than 32-bit ints. On 64-bit linux and OS/X, this type is defined as unsigned long and on 64-bit Windows as unsigned long long, hence using %lu or %llu on these systems is fine too.
Passing a size_t for an incompatible conversion specification has undefined behavior:
the program could crash (and it probably will if you use %s)
the program could display the expected value (as it might for %d)
the program could produce weird output such as nan for %f or something else...
The reason for this is integers and floating point values are passed in different ways to printf and they have a different representation. Passing an integer where printf expects a double will let printf retrieve the floating point value from registers or memory locations that have random contents. In your case, the floating point register just happens to contain a nan value, but it might contain a different value elsewhere in the program or at a later time, nothing can be expected, the behavior is undefined.
Some legacy systems do not support %zu, notably C runtimes by Microsoft. On these systems, you can use %u or %lu and use a cast to convert the size_t to an unsigned or an unsigned long:
int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%u\n", (unsigned)sizeof(a + d)); // should print 8
printf("%lu\n", (unsigned long)sizeof(s)); // should print 4
printf("%llu\n", (unsigned long long)sizeof(d)); // prints 4 or 8 depending on the system
I want to know for my own knowledge what happens behind and why it prints nan when I use it with %f or a long number when used with %lf
Several reasons.
First of all, printf doesn't know the types of the additional arguments you actually pass to it. It's relying on the format string to tell it the number and types of additional arguments to expect. If you pass a size_t as an additional argument, but tell printf to expect a float, then printf will interpret the bit pattern of the additional argument as a float, not a size_t. Integer and floating point types have radically different representations, so you'll get values you don't expect (including NaN).
Secondly, different types have different sizes. If you pass a 16-bit short as an argument, but tell printf to expect a 64-bit double with %f, then printf is going to look at the extra bytes immediately following that argument. It's not guaranteed that size_t and double have the same sizes, so printf may either be ignoring part of the actual value, or using bytes from memory that isn't part of the value.
Finally, it depends on how arguments are being passed. Some architectures use registers to pass arguments (at least for the first few arguments) rather than the stack, and different registers are used for floats vs. integers, so if you pass an integer and tell it to expect a double with %f, printf may look in the wrong place altogether and print something completely random.
printf is not smart. It relies on you to use the correct conversion specifier for the type of the argument you want to pass.

IPV6 address to array of uint64_t is not working as expected

I am trying to convert an IPv6 address in string format to array of uint64_t data. For this i have written below program
typedef struct
{
union
{struct in6_addr sa;
uint64_t addr[2];
}u;
} ipv6_addr_t;
ipv6_addr_t rnc;
char rncs[100] = "2000::200a";
inet_pton(AF_INET6, rncs, &(rnc.u.sa));
printf("%u\", rnc.u.addr[0]);
Expected output is 1st 64 bits of the address which will be 2^61 =
2305843009213693952.
But when i execute the program i am getting output as 32 which is the
first byte of the address.
I am not understanding the reason behind it , please help. Thanks!
You have several problems here.
your method for printing the result is inherently flawed. The printf directive %u is not generically for printing unsigned integers, but rather specifically for printing values of type unsigned int. Although it is possible that your int64_t is the same type as unsigned int, that would be atypical. If they are not the same type, then the mismatch between directive and actual argument result in undefined behavior. #AndrewHenle explains in his answer how to print an int64_t via printf.
By using a union to map the bytes of your struct in6_addr onto an array of int64_t, you are exposing yourself to the details of the byte ordering of your implementation's representation of int64_t. That you can present a specific expected result at all indicates that you are assuming a particular representation (and evidently one that does not match your system).
You seem to think that fewer bytes are printed than should be, but even if you corrected your printf format per Andrew's answer, I'm inclined to think that the output would be the same. You observe that the value that is printed corresponds to the first byte of the address, but think about what the next several bytes are: all zeroes, until you get to the final two. Now consider a bit pattern consisting of one byte having the value 32 (decimal), followed by seven bytes with value zero. If you interpret that pattern as a 64-bit, unsigned, little-endian integer, its value is 32. That's the most likely thing for the corrected code to print on an Intel-based machine.
Evidently you want to produce int64_t values whose logical bit patterns match the address bits. You have instead produced values whose physical bit patterns match the address bits. This is to be expected when you use a union instead of arithmetic to convert between byte array and integer. I suggest using a loop instead:
struct in6_addr sa;
int64_t addr_ints[2] = { 0, 0 };
char rncs[100] = "2000::200a";
inet_pton(AF_INET6, rncs, &sa);
for (int i = 0; i < 16; i++) {
addr_ints[i / 8] = addr_ints[i / 8] << 8 + sa.s6_addr[i];
}
printf( "%" PRIu64 "\n", addr_ints[ 0 ] );
That also avoids problems in the event that struct in6_addr is laid out differently than you seem to expect, as long as the layout conforms to POSIX.
In addition to any problems noted in the comments, using the wrong printf() format specifier is undefined behavior.
Per 7.21.6 Formatted input/output functions, paragraph 9 of the C standard:
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.
Given that
rnc.u.addr[0]
is a uint64_t, the printf() format specifier %u in
printf("%u\n", rnc.u.addr[0]);
is incorrect.
The proper format for a uint64_t is PRIu64:
printf( "%" PRIu64 "\n", rnc.u.addr[ 0 ] );
Note also your code, as posted, is not correct:
printf("%u\", rnc.u.addr[0]);
That's won't even compile - it's an unterminated string. The second " char is escaped as \". I assume you meant "%u\n".

short and int in c

short int a,b,c;
scanf("%d%d",&a,&b);
c = a + b;
printf("%d %d %d",a,b,c);
Input: 5 8
Output: 0 8 8
Why is the value of a 0? Can any one explain this?
Platform --- GCC ubuntu 10.04
scanf with a "%d" format requires an int* argument. You're giving it a short int* argument, thus your program's behavior is undefined.
If you actually want to know why you're getting the results you're getting, I can speculate on that, but it's much easier just to correct your code:
scanf("%hd%hd", &a, &b);
You can continue to use "%d" for printf, since the short int arguments are promoted to int. You could use "%hd" with printf, but it's not necessary. (There is no similar promotion of short int* arguments to int*.)
You can safely stop reading here.
The following is some speculation about what's probably happening in your incorrect code. This is not a solution; the solution is to correct your code so it does what you want it to do. But might be instructive to see just how incorrect code can misbehave.
Assume short is 16 bits and int is 32 bits, which is typical. The first "%d" in the format string tells scanf to read a value (you gave it 5) and store it into a 32-bit int pointed to by the second argument, &a. Since a is only 16 buts, it will store half the 32-bit value in a and the other half in some adjacent chunk of memory. The second "%d" does the same thing with &b; it stores half of the 32-bit representation of 8 in b, and the other half somewhere else.
Based on your output, it appears that the second "%d" caused scanf to store the low-order 16 bits of the value 8 in b, and the high-order 16 bits (with value 0) in a, overwriting the value stored by the first "%d". Note that the high-order 16 bits from the first "%d" were probably stored somewhere else, perhaps clobbering some other variable or perhaps writing to some otherwise unused memory.
So the result is that you've stored 0 in a and 8 in b, which explains the output you're getting.
All this is very speculative, and many many other results are possible. This kind of analysis is useful only for tracking down the behavior of incorrect code, with the goal of correcting it. Writing code that deliberately takes advantage of this kind of thing is an extraordinarily bad idea. The language says absolutely nothing about what incorrect code like this will do; its behavior can vary wildly on different systems, with different compiler settings, or even depending on the phase of the moon.
According to this:
http://www.cplusplus.com/reference/clibrary/cstdio/scanf/
you need to specify a modify if your variable is a "short" int and not a regular int
In VC++ 6.0, The value of c is 13.
#include <stdio.h>
int main()
{
short int a,b,c;
scanf("%d%d",&a,&b);
c = a + b;
printf("%d + %d = %d\n",a,b,c);
return 0;
}

C : Printing big numbers

Take the following :
#include <stdio.h>
main() {
unsigned long long verybig = 285212672;
printf("Without variable : %llu\n", 285212672);
printf("With variable : %llu", verybig);
}
This is the output of the above program :
Without variable : 18035667472744448
With variable : 285212672
As you can see from the above, when printf is passed the number as a constant, it prints some huge incorrect number, but when the value is first stored in a variable, printf prints the correct number.
What is the reasoning behind this?
Try 285212672ULL; if you write it without suffixes, you'll find the compiler treats it as a regular integer. The reason it's working in a variable is because the integer is being cast up to an unsigned long long in the assignment, so that the value passed to printf() is the right type.
And before you ask, no, the compiler probably isn't smart enough to figure it out from the "%llu" in the printf() format string. That's a different level of abstraction. The compiler is responsible for the language syntax, printf() semantics are not part of the syntax, it's a runtime library function (no different really from your own functions except that it's included in the standard).
Consider the following code for a 32-bit int and 64-bit unsigned long long system:
#include <stdio.h>
int main (void) {
printf ("%llu\n",1,2);
printf ("%llu\n",1ULL,2);
return 0;
}
which outputs:
8589934593
1
In the first case, the two 32-bit integers 1 and 2 are pushed on the stack and printf() interprets that as a single 64-bit ULL value, 2 x 232 + 1. The 2 argument is being inadvertently included in the ULL value.
In the second, you actually push the 64-bit 1-value and a superfluous 32-bit integer 2, which is ignored.
Note that this "getting out of step" between your format string and your actual arguments is a bad idea. Something like:
printf ("%llu %s %d\n", 0, "hello", 0);
is likely to crash because the 32-bit "hello" pointer will be consumed by the %llu and %s will try to de-reference the final 0 argument. The following "picture" illustrates this (let's assume that cells are 32-bits and that the "hello" string is stored at 0xbf000000.
What you pass Stack frames What printf() uses
+------------+
0 | 0 | \
+------------+ > 64-bit value for %llu.
"hello" | 0xbf000000 | /
+------------+
0 | 0 | value for %s (likely core dump here).
+------------+
| ? | value for %d (could be anything).
+------------+
It's worth pointing out that some compilers give a useful warning for this case - for example, this is what GCC says about your code:
x.c: In function ‘main’:
x.c:6: warning: format ‘%llu’ expects type ‘long long unsigned int’, but argument 2 has type ‘int’
285212672 is an int value. printf expects an unsigned long long and you're passing it an int. Consequently, it'll take more bytes off the stack than you passed a real value and prints garbage. When you put it in an unsigned long long variable before passing it to the function, it'll be promoted to unsigned long long in the assignment line and you pass that value to printf which works correctly.
Datatype is simply a way of interpreting contents of a memory location.
in first case the constant value is stored in read only memory location as an int, the printf tries to interpret this address as 8 byte location as it is instructed that the value stored is long long in process of which it prints garbage value.
In the second case printf tries to interpret a long long value as 8 bytes and it prints what is expected.

Resources