How does format specifier know how many bytes to print? - c

I have the following program.
#include<stdio.h>
int main()
{
char a='b';
int b=11299;
char d[4]="abc";
printf("value of a is %d\n",a);
printf("value of b is %c\n",b);
printf("value of c is %d\n",*d);
char *c=d;
c=c+1;
printf("c is %d\n",*c);
}
I am little confused with %d format specifier. I was thinking that it would print 4 bytes of data. But from the above program (first and last printf) it is evident that it prints only one byte when a char parameter is used. Why does %d print only one byte? How does it know how many bytes to print?

It doesn't print bytes; it prints values -- the value you pass as the corresponding argument -- provided the value passed has the right type.
In your example 1, the argument is the value 'b'. It initially has type char (because the expression a has type char) but variadic arguments are subject to default promotions, which promote any integer type with lower rank than int up to int. Thus, as an argument, the type is int.
In your example 3, the argument is the value 'a'. Likewise it initially has type char (because the expression *d has type char) but it gets promoted to int.
If promotions didn't happen and the types were wrong, though, printf still wouldn't "print fewer bytes". Your program would just have undefined behavior (so anything could happen). For example:
int a = 42;
printf("%lld\n", a); // undefined behavior because int does not
// get promoted implicitly to long long.
In your example 2, the %c format specifier expects an argument of type int; printf converts it to unsigned char and prints the corresponding character. Any value of int is acceptable; it doesn't have to already be a value in the range of unsigned char.

First, you're probably declaration specifying the ISO C90, and this forbids mixed declarations and code, as shown in the warning.
The statement pointer of type char c varible char * c = d; must be made for example of the statement string d char d [4] = "abc"; and not in the middle of the code.
Second: int main (){ function does not return anything, but this must specify to not pull warning.
The revised program would be:
#include<stdio.h>
int main()
{
char a='b';
int b=11299;
char d[4]="abc";
char *c=d;
printf("value of a is %d\n",a);
printf("value of b is %c\n",b);
printf("value of c is %d\n",*d);
c=c+1;
printf("c is %d\n",*c);
return 0;
}

Related

What is the difference between int (*p)[10]=s and int (*o)[5]=&s?

On basis of the convention int (*o)[5]=&s; is the right way for a pointer o to point an array having 5 elements.
We can also write this s in this statement
int (*p)[10]=s;
but why preferring
&s at int (*o)[5]=&s;
as both of them return the same output.
#include <stdio.h>
int main()
{
int s[5]={10,1,2,3,4};
int (*p)[10]=s;
printf("%d\n",*p);
printf("%d\n",**p);
printf("%d\n",&s);
printf("\n");
int (*o)[5]=&s;
printf("%d\n",*o);
printf("%d\n",**o);
printf("%d",&s);
return 0;
}
Output of this program is:
-593812272
10
-593812272
-593812272
10
-593812272
This is not valid:
int s[5]={10,1,2,3,4};
int (*p)[10]=s;
Because you're initializing a variable of type int (*)[10] (a pointer to an array of int of size 10) with an expression of type int *. These types are not compatible.
While this is fine:
int (*o)[5]=&s;
Because the type of the initializer matches the type of the variable.
Also, when printing pointer values, you should use the %p format specifier and cast the argument to void *. Mismatching format specifiers with their associated arguments triggers undefined behavior.
This line
int (*p)[10]=s;
is incorrect. The initializer has the type int * due to the implicit conversion of the array designator s to a pointer to its first element. And the two pointers in the left hand side and in the right hand are not compatible. So the compiler should issue a message.
This line
int (*o)[5]=&s;
is correct. The initializer has the type int ( * )[5] that is the same type of the initialized pointer o.
Pay attention to that to output a value of a pointer you have to use the conversion specifier %p. Otherwise using the conversion specifier %d to output a pointer invokes undefined behavior.
So for example instead of these calls
printf("%d\n",*o);
//...
printf("%d",&s);
you have to write
printf("%p\n", ( void *)*o);
//...
printf("%p\n", ( void * )&s);
The expression *o yields value of the array s that is in turn is implicitly converted to a pointer to its first element.
The values of the expression *o and of the expression &s are the same because it is the address of the extent of memory occupied by the array. But their types are different. The first expression used as an argument of the call of printf has the type int * while the second expression has the type int ( * )[5].

Printf with short integer compiler warning

#include <stdio.h>
int main(void)
{
short i = 1;
printf("%hd\n", i);
printf("%hd\n", i * i);
return 0;
}
Compiling this gives the warning warning: format specifies type 'short' but the argument has type 'int' [-Wformat] printf("%hd\n", i * i);.
Removing the printf("%hd\n", i * i); statement produces no warning.
Have I done something wrong? It is my understanding that since i has type short int that the value of the expression i * i has type short int but then printf performs an integer promotion to convert this short int result to int form before converting this int to short again (due to the conversion specifier).
I don't particularly see what is wrong about the code though. Is it just a warning telling me to be careful of the possibility that a more complicated expression than a simple short int variable being passed as an argument to printf corresponding to the %hd conversion specifier might result in overflow? Thanks!
In i * i the arguments to the multiplication undergo "integer promotions" and "usual arithmetic conversions" and become int before the multiplication is done. The final result of the multiplication is an int.
The type of i * i is int (when type of i is int, short, char, or _Bool).
When you call printf with "%hd" the corresponding argument should be a short. What happens is that it is automatically converted to int and it's an int that printf sees. The internal code of printf will convert that int to a short to do its thing.
Problem is if you start with an int value outside the range of short.
short i = SHRT_MAX;
int j = i * i;
short k = i * i; // overflow while assigning
printf("%hd %hd", j, k);
// ^ (kinda) overflow inside the innards of printf

Why type casting not required?

Below are following code written in c using CodeBlocks:
#include <stdio.h>
void main() {
char c;
int i = 65;
c = i;
printf("%d\n", sizeof(c));
printf("%d\n", sizeof(i));
printf("%c", c);
}
Why when printing variable c after it was assigned with int value (c = i), there no need for casting to be made?
A cast is a way to explicitly force a conversion. You only need casts when no implicit conversions take place, or when you wish the result to have another type than what implicit conversion would yield.
In this case, the C standard requires an implicit conversion through the rule for the assignment operator (C11 6.5.16.1/2):
In simple assignment (=), the value of the right operand is converted to the type of the
assignment expression and replaces the value stored in the object designated by the left
operand.
char and int are both integer types. Which in turn means that in this case, the rules for converting integers are implicitly invoked:
6.3.1.3 Signed and unsigned integers
When a value with integer type is converted to another integer type other than _Bool, if
the value can be represented by the new type, it is unchanged.
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or
subtracting one more than the maximum value that can be represented in the new type
until the value is in the range of the new type.
Otherwise, the new type is signed and the value cannot be represented in it; either the
result is implementation-defined or an implementation-defined signal is raised.
In your case, the new type char can be either signed or unsigned depending on compiler. The value 65 can be represented by char regardless of signedness, so the first paragraph above applies and the value remains unchanged. For other larger values, you might have ended up with the "value cannot be represented" case instead.
This is a valid conversion between integer types, so no cast is necessary.
Please note that strictly speaking, the result of sizeof(c) etc is type size_t and to print that one correctly with printf, you must use the %zu specifier.
This assignment is performed on compatible types, because char is not much more than a single byte integer, whereas int is usually 4bytes integer type (machine dependant). Still - this (implicit) conversion does not require casting, but You may loose some information in it (higher bytes would get truncated).
Let's examine your program:
char c; int i = 65; c = i; There is no need for a cast in this assignment because the integer type int of variable i is implicitly converted to the integer type of the destination. The value 65 can be represented by type char, so this assignment is fully defined.
printf("%d\n", sizeof(c)); the conversion specifier %d expects an int value. sizeof(c) has value 1 by definition, but with type size_t, which may be larger than int. You must use a cast here: printf("%d\n", (int)sizeof(c)); or possibly use a C99 specific conversion specifier: printf("%zu\n", sizeof(c));
printf("%d\n", sizeof(i)); Same remark as above. The output will be implementation defined but most current systems have 32-bit int and 8-bit bytes, so a size of 4.
printf("%c", c); Passing a char as a variable argument to printf first causes the char value to be promoted to int (or unsigned int) and passed as such. The promotion to unsigned int happens on extremely rare platforms where char is unsigned and has the same size as int. On other platforms, the cast is not needed as %c expects an int argument.
Note also that main must be defined with a return type int.
Here is a modified version:
#include <stdio.h>
#include <limits.h>
int main() {
char c;
int i = 65;
c = i;
printf("%d\n", (int)sizeof(c));
printf("%d\n", (int)sizeof(i));
#if CHAR_MAX == UINT_MAX
/* cast only needed on pathological platforms */
printf("%c\n", (int)c);
#else
printf("%c\n", c);
#endif
return 0;
}

unusual output of the hexadecimal number

#include<stdio.h>
int main()
{
int a = 0xabcdef;
char *b = &a;
printf("%x",*b);
return 0;
}
above code gives output as ffffffef. What is the reason of this output?
Firstly, variable b is declared with type char *. You are trying to initialize it with value &a of type int *. This is illegal. The code is not valid C. The behavior is undefined.
Secondly, if your compiler managed to let this initialization slip through, then it probably performed an implicit conversion of the int * value to char * type. This makes pointer b to point to just one byte of variable a. The value of that single byte is what you see through *b. Which byte that is (in terms of the original value of a) is implementation-defined. E.g. you will typically you get a lower-order byte (0xEF) or a higher-order byte (0xAB).
Thirdly, it is implementation-defined whether type char is signed or unsigned. That means that the value of *b might be signed or unsigned, depending on your implementation. When you pass a char value *b to a variadic function printf, that char value is automatically converted to int type. The result of that conversion will generally depend on the signedness of type char. If your b points to the 0xEF byte, char is signed and int is 32-bit wide and uses 2's-complement, then the result, in hex terms, will look as 0xFFFFFFEF (which is a negative value).
Fourthly, %x format specifier requires an unsigned int argument, but instead you are passing an int argument with potentially negative value. The behavior is undefined.
In other words, your program is just a big pile of undefined and implementation-defined behavior. There's not much point in trying to explain why you got that specific output.
The pointer *b is a char type pointer and it's pointing to a int type variable. That's the reason of wrong output. Declare the pointer variable *b as int type.
int a = 0xabcdef;
int *b = &a;
printf("%x",*b);

What happens when I assign a string to an int?

If i were to instantiate an int with a string what does the value of that int actually hold?
e.g. for the following code:
#include <stdio.h>
int main(void) {
int a = "abcd";
printf("a as string witn & = %s\n", &a);
printf("a as int witn no & = %d\n", a);
printf("a as int witn & = %d\n", &a);
}
I get values that differ with each execution such as:
a as string witn & = "?????W?
a as int witn no & = 130694946
a as int witn & = 1475726188
or
a as string witn & = "?O?Kp\?
a as int witn no & = 55557922
a as int witn & = 1550863212
what are these values? Why are they always different? And what is 'a' actually storing?
int a = "abcd";
This is illegal in C.
Well, sort of. The C standard doesn't actually use the term "illegal" for this kind of thing. To be painfully precise, it's a constraint violation, which means that any conforming compiler must issue a diagnostic message (which might be a non-fatal warning).
The expression "abcd" is an array expression, of type char[5] (4 for the letters plus 1 for the terminating \0'). In most contexts, including this one (if it were valid), an array expression is implicitly converted to a pointer to the array's first element. After that conversion, the value is of type char*, and it's a pointer to the 'a'.
There is no implicit conversion from char* to int, which is why this initialization is invalid. You could add a cast, which is an explicit conversion:
int a = (int)"abcd";
This would store in a the memory address of the string, converted from char* to int. On many systems, this conversion, though it's legal, yields garbage; for example, on the system I'm typing this on, a char* is 64 bits and an int is only 32 bits.
Compilers for older versions of the C language (prior to 1989) were more lax about implicit conversions, often allowing integers and pointers to be assigned to each other. More modern compilers, even though they'll diagnose this error if you ask them to, might (or might not) still generate code to perform the implicit conversion. (Strictly speaking the behavior is undefined, but an implicit conversion is common.)
If your compiler rejects
int a = "abcd";
it's doing its job. If it merely warns you about it, it's still doing its job as far as the C standard is concerned, but it's really not doing you any favors by generating that implicit conversion.
Bottom line: The value assigned to a is garbage, and if your compiler doesn't complain about it, find out what options you need to give it to make it do so.
As for the output of your printf calls:
printf("a as string witn & = %s\n", &a);
%s requires a char* argument that points to a string. &a is of type int*, and does not point to a string. The behavior is undefined. Most likely printf will print garbage bytes starting at the beginning of a until it happens to encounter a null byte (or crashes).
Don't do that.
printf("a as int witn no & = %d\n", a);
If your program hasn't already crashed at this point, this prints the value of a. That value is garbage, which might typically be the converted value of the address of the string literal, or just the low-order 32 bits of that address.
printf("a as int witn & = %d\n", &a);
%d requires an argument of type int. &a is of type int*. Undefined behavior. This might print the memory address of a as a decimal integer. Don't do that. If you really want to print the address of a, the correct way to do it is:
printf("&a = %p\n", (void*)&a);
Perhaps the most efficient way to answer the question is to stroll through the question code:
#include <stdio.h>
int main(void) {
int a = "abcd";
The above line declares an int called a, and initializes a with the address of the string "abcd". This line will cause a compiler to become a little grumpy, in that it will complain something like: warning: initialization makes integer from pointer without a cast.
printf("a as string witn & = %s\n", &a);
The object of the above line seems to b to print a string. Unfortunately, &a is the memory address of where the variable is stored in memory, which appears it was something like 1550863212 or 0x93E6DF1 on your system. This value is not an ASCII or UTF-8 string; hence it appears to print out garbage: "?????W?, "?O?Kp\?, or some other nonsense string, depending on what the address of a happens to be. Of course, this line will make the compiler even more grumpy; format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int *’
printf("a as int witn no & = %d\n", a);
The above line attempts to print the value of a as a signed integer. It will also appear as nonsense, Since the value of a is the address of the string "abcd". Hence, the value of a (as a signed integer) will be the signed integer representation of the address where the string "abcd" is stored in memory.
printf("a as int witn & = %d\n", &a);
The above line attempts to print the memory address of a as a signed integer. As with the others, this will most likely appear as nonsense again.
printf("a as a string[%s]\n", a);
For your viewing pleasure, I added the above line, which outputs: "a as a string[abcd]". This proves that the variable a was successfully initialized, although (again) the compiler thinks us insane: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
}

Resources