Why can I cast implicitly an integer to a char in C - c

I was wondering, why is casting implicitly from an integer to a char possible in C ?
If for example :
int i = 2789;
printf("%c\n",i);
would give me a char back, because it would have truncated the bits starting from the most significant ones.
But usually you can't really cast implicitly if it means you'll lose in precision so why can I do it here ?

There is no implicit cast going on here.
printf is a variadic function, which means that any arguments of type char are converted up to int before the function is called. Your argument is already of type int, so no conversion occurs.

All integral arguments passed to a function are generally promoted to the CPU's word size, so regardless if you pass a char, short, int or long, the physical layout of the stack and/or registers upon entry to printf is the same anyway.
Therefore, there's no "cast" going on, merely normal argument passing.
That's not to say that the code is correct, however. Argument passing and type promotion is defined in the ABI, not by C, so it may not work on all platforms. (Though it will work on the vast majority.)

Related

C compiler warning error

A simple program
#include<stdio.h>
int main() {
char b='a';
printf("%s \n", b);
return 0;
}
Output:
test.c: In function ‘main’:
test.c:4:1: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s \n", b);
^
But the second arg is char not int.
Is this a wrong warning by the compiler, or something else I am missing?
The compiler is certainly correct to warn about the call. printf with a "%s" specifier requires an argument of type char* (which must point to a string), and the argument you pass is not of that type.
As for why the warning message refers to int rather than char, it's because printf is a variadic function. The first parameter, the format string, is declared to be of type const char*, but the following arguments are specified only as , .... In this special case, arguments of integer types narrower than int are promoted to int or to unsigned int. So even though the expression b is of type char, the actual argument that's passed to printf is of type int; specifically, it's the result of converting the value of b from char to int.
The warning is accurate, though the reasons are modestly obscure.
The declaration of printf() is:
int printf(const char *restrict format, ...);
The const and restrict keywords aren't important for this discussion. What is important is the ellipsis, .... When arguments are passed to a function with a variable argument list (a variadic function), they undergo 'default argument conversions'. Integer types shorter than int (short and char in their various forms) are promoted to int, and float values are promoted to double. Thus, the value of b is converted to int by the rules for calling a variadic functions.
The "%s" format expects to be given a pointer to a null-terminated character string. The variable b is a single char, not a string. Consequently, the compiler is correctly warning you that you will not get good results from running the program.
In this context, using a format such as %c (to print a character) or %d (to print a decimal integer) is probably best:
printf("%c\n", b);
As a general rule, at this stage in your C programming career, you should assume the compiler is right and that you're wrong. Remember, the C compiler knows a lot more about C than you do. This isn't to say that there are never bugs in compilers. However, the chances of you finding one are slim. Until you know enough about C (maybe in five to ten years time), then you should assume the compiler is right, you're wrong, and work out (a) what the compiler means and (b) how to fix it.
It was not always thus. Thirty years ago, bad compilers existed because the best were not all that much better. It was possible to find bugs in those compilers. There has, however, been a serious winnowing and few incompetent compilers are left on the market. One area where you can sometimes find compilers with surprising limitations (occasionally tantamount to bugs) is in specialized embedded systems for obscure chips. However, in mainstream o/s for desktops and servers (and tablets and smart phones), you're unlikely to come across a seriously defective compiler.
ISO/IEC 9899:2011 §6.5.2.2 Function calls
¶6 If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions. If the number of arguments does not equal the number of parameters, the
behavior is undefined. If the function is defined with a type that includes a prototype, and
either the prototype ends with an ellipsis (, ...) or the types of the arguments after
promotion are not compatible with the types of the parameters, the behavior is undefined.
If the function is defined with a type that does not include a prototype, and the types of
the arguments after promotion are not compatible with those of the parameters after
promotion, the behavior is undefined, except for the following cases:
one promoted type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in both types;
both types are pointers to qualified or unqualified versions of a character type or
void.
The 'integer promotions' are defined in §6.3.1.8 Usual arithmetic conversions. They're more complex than I want to go through here.
a char is really just an 8 bit number, so a single char alone is no different than an int, except that it can only store smaller numbers. In your printf statement you have
%s
but a single char is not considered a string, just a number, so you should use
%c or %d
for you printf statement. If you had an array of chars, then you would use %s
Try this instead:
printf("%c \n", b);
The compiler is casting the char to int on the fly, so the error msg makes sense (at least to someone used to interpreting C compiler messages).

what happens if float,double or even char value is passed in malloc

While studying in detail about malloc() I came across this strange behavior.
int *p;
p=(int*)malloc(10.45);
p=(int*)malloc(10.45f);
p=(int*)malloc('j');
The program compiles with any of these statements with just a warning and returns a valid address.
What's the real result here?
All of those values are implicitly converted to size_t before calling the function, just as if you had an explicit cast. The floating point numbers are truncated, and the 'j' is interpreted as its numerical value in your character set.
"Just a warning" probably hides the explanation.
Various rules for converting/promoting values of non-integer types to integer kick in, making sure malloc() gets a single size_t, i.e. an unsigned integer. The floating point calls will probably just drop the fractions for instance, and attempt to allocate 10 bytes.
Also, please don't cast the return value of malloc() in C.
malloc() takes a size_t argument.
The compiler will truncate 10.45/10.45f to 10 and convert 'j' to its ASCII value (106).
It's an implicit cast to size_t
Malloc is defined to accept an integer 'number of bytes'.
The C language defines the rules for converting from other data types to integers.
I'm sure you can work out for yourself what each value converts to.

Dealing with pointer argument in C

I'm using a library which has a function with the following signature:
void LED_stop_blink_task ( void * callback_parameter );
The actual parameter the void pointer stands for is a pointer to uint32_t, which is the number of the led on the board.
Is there a way to call this function without using a variable to hold the data ?
In my imagination it will be like
LED_stop_blink_task(&35);
or the only way is like this:
uint32_t led_num = 35;
LED_stop_blink_task(&led_num);
If you're asking why I want to throw the variable away, well, I'm just curious if it's possible...
On most platforms it's possible to simply stuff the int in a void *:
LED_stop_blink_task((void *)32);
Then in the function you can cast to int.
An integer may be converted to any pointer type. Except as
previously specified, the result is implementation-defined, might not
be correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation.
Any pointer type may be converted to an integer type. Except as
previously specified, the result is implementation-defined. If the
result cannot be represented in the integer type, the behavior is
undefined. The result need not be in the range of values of any integer
type.
In practice this will work on any POSIX-supported platform. For example TLPI says:
Strictly speaking, the C standards don’t define the results of casting
int to void * and vice versa. However, most C compilers permit these
operations, and they produce the desired result; that is, int j ==
(int) ((void *) j).
Cnicutar's answer is almost perfect; let me extend it with that it's not really portable - int is not guaranteed to be of the same size (or smaller) than a pointer, so you should use intptr_t or uintptr_t instead.

type conversion in c

int main()
{
int x,y;
int z;
char s='a';
x=10;y=4;
z = x/y;
printf("%d\n",s); //97
printf("%f",z); //some odd sequence
return 0;
}
in the above piece of code the char s is automatically converted to int while printing due to the int type in control string, but in the second case the int to float conversion doesn't happen. Why so?
In both cases the second argument is promoted to int. This is how variadic functions work, and has nothing to do with the format string.
The format string is not even looked at by the compiler: it's just an argument to some function. Well, a really helpful compiler might know about printf() and might look at the format string, but only to warn you about mistakes you might have made. In fact, gcc does just that:
t.c:9: warning: format ‘%f’ expects type ‘double’, but argument 2 has type ‘int’
It is ultimately your responsibility to ensure that the variadic arguments match the format string. Since in the second printf() call they don't, the behaviour of the code is undefined.
Functions with variable number of arguments follow the rule of the default argument promotion. Integer promotion rules are applied on arguments of integer types and float arguments are converted to double.
printf("%d\n",s);
sis a char and is converted to int.
printf("%f",z);
z is already an int so no conversion is performed on z
Now the conversion specifier f expects a double but the type of the object after the default argument promotion is an int so it is undefined behavior.
Here is what C says on arguments of library functions with variable number of arguments
(C99, 7.4.1p1) "If an argument to a function has [...] a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined."
The char is not being promoted to int due to the control string. The char is working as an int because all data that is less than 4 bytes when passed to printf is bumped up to 4 bytes, which is the size of an int, because of the cdecl calling convention of variadic functions (the point of this is so that the data that comes next will be aligned on a 4-byte boundary on the stack).
printf is not type-safe and has no idea what data you really pass it; it blindly reads the control string and extracts a certain number of bytes from the stack based on what sequences it finds, and interprets that set of bytes as the datatype corresponding to the control sequence. It doesn't perform any conversions, and the reason you are getting some wierd printout is because the bits of an int are being interpreted as the bits of a float.
due to the int type in control string
That is incorrect. It is being converted because shorter int types are promoted to int by the var_args process. Int types are not converted to float types because the va/preprocessor doesn't know what formats are expected.

Casting unsigned char* to long

What does the following expression mean?
unsigned char *res = malloc(5);
Now I cast res:
(long)res
What does this casting mean?
Using that value will interpret the address to which res points (which is just a number anyway) as a long.
It will work most of the time but it's not completely okay (depends a lot on how you're using it). For example if you simply want to print it, you can get away with
printf("%p", res);
As a rule of thumb: treat any cast with suspicion.
The allocated memory is not read, you're just casting the pointer to the memory to a long.
This doesn't directly answer your question but is a useful bit of information that is more-or-less relevant to your siutation.
A cast from a pointer type to an integer type is implementation defined (that means the implementation decides what happens when you cast a pointer to an integer). C99 implementations that do support some type of reversible conversion should also provide two types found in <stdint.h> specifically for converting pointers to integers, namely uintptr_t and intptr_t. If your implementation provides these two types, then you can safely convert a pointer to these types and back to the original pointer type.
Since these types are implementation defined, you will need to check your implementations documentation for what the underlying types are.

Resources