If a value is set to an int e.g. 2, does the compiler convert the int types to the size it needs e.g. int8_t or uint16_t etc.?
Not in vanilla C, no. The compiler can't possible know what you meant if you do not tell it.
If you write
int value = 2;
the type is, by default, signed int. What the compiler does then really depends from the platform, but it has to guarantee that int's size is not less than short int's and not greater than long int'.s
For constants it may true, often the reverse conversion small to large is also done: byte to int for example.
It's somewhat dependent on the implementation and optimization techniques used by the compiler and the data alignment requirements by the architecture/OS. Have a look at this write up.
The compiler first looks at the context of the expression, learning about what type it expects. The context can be i.e.
Left hand side of an assignment
Expected argument type provided by the function header or operator definition
It then evaluates the expression, inserting implicit type conversions as needed (type coercion). It does
Promotion
Truncation
Rounding
In situations where all the bits matter, you need to be extremely carefull about what you write: Types, operators and order.
Integer numbers are values of the type "int". When you assign an integer value to short or char using the operator "=", the int value will be converted to short or char. The compiler may detect this conversion and do optimization to convert the integer value on compile time.
short a = 50; //50 is an int, it will be implicitly converted to short. The compiler may convert 50 to short on compile time.
int b = 60;
short c = b; //the value of b will be converted to short and assigned to c.
short d = b + 70; //b + 70 is a sum of ints and the result will be an int that will be converted to short.
int8_t and uint16_t are not standard types. Many times these types may be defined as something like:
typedef char int8_t;
typedef unsigned short uint16_t;
Related
If the representation of a long int and a int are the same on a platform, are they strictly the same? Do the types behave any differently on the platform in any way according to the C standard?
Eg. does this always work:
int int_var;
long long_var;
void long_bar(long *l);
void int_bar(int *i);
void foo()
{
long_bar(&int_var); /* Always OK? */
int_bar(&long_var);
}
I guess the same question applies to short and int, if they happen to be the same representation.
The question arose when discussing how to define a int32_t-like typedef for an embedded C89 compiler without stdint.h, i.e. as int or long and if it would matter.
They are not compatible types, which you can see with a a simple example:
int* iptr;
long* lptr = iptr; // compiler error here
So it mostly matters when dealing with pointers to these types. Similarly, there is the "strict aliasing rule" which makes this code undefined behavior:
int i;
long* lptr = (long*)&i;
*lptr = ...; // undefined behavior
Some another subtle issue is implicit promotion. In case you have some_int + some_long then the resulting type of that expression is long. Or in case either parameter is unsigned, unsigned long. This is because of integer promotion through the usual arithmetic conversions, see Implicit type promotion rules.
Shouldn't matter most of the time, but code such as this will fail: _Generic(some_int + some_long, int: stuff() ) since there is no long clause in the expression.
Generally, when assigning values between types, there shouldn't be any problems. In case of uint32_t, it doesn't matter which type it corresponds to, because you should treat uint32_t as a separate type anyway. I'd pick long for compatibility with small microcontrollers, where typedef unsigned int uint32_t; will break. (And obviously, typedef signed long int32_t; for the signed equivalent.)
The types long and int have different ranks. The rank of the type long is higher than the rank of the type int. So in a binary expression where there are used an object of the type long and an object of the type int the last is always converted to the type long.
Compare the following code snippets.
int x = 0;
unsigned int y = 0;
the type of the expression x + y is unsigned int.
long x = 0;
unsigned int y = 0;
the type of the expression x + y is unsigned long (due to the usual arithmetic conversions) provided that sizeof( int ) is equal to sizeof( long).
This is very important in C++ than in C where function overloading are allowed.
In C you have to take this into account for example when you are using i/o functions as for example printf to specify a correct conversion specifier.
Even on platforms where long and int have the same representation, the Standard would allow compilers to be willfully blind to the possibility that the act of storing a value to a long* might affect the value of an int* or vice versa. Given something like:
#include <stdint.h>
void store_to_int32(void *p, int index)
{
((int32_t*)p)[index] = 2;
}
int array1[10];
int test1(int index)
{
array1[0] = 1;
store_to_int32(array1, index);
return array1[0];
}
long array2[10];
long test2(int index)
{
array2[0] = 1;
store_to_int32(array2, index);
return array2[0];
}
The 32-bit ARM version of gcc will treat int32_t as synonymous with long and ignore the possibility that passing the address of to array1 to store_to_int32 might cause the first element of that array to be written, and the 32-bit version of clang will treat int32_t as synonymous with int and ignore the possibility that passing the address of array2 to store_to_int32 might cause that array's first element to be written.
To be sure, nothing in the Standard would prohibit compilers from behaving in that fashion, but I think the Standard's failure to prohibit such blindness stems from the principle "the dumber something would be, the less need there should be to prohibit it".
sizeof is a standard C operator.
sizeof yields the size (in bytes) of its operand in type size_t, Quote from ISO/IEC 9899:2018 (C18), 6.5.3.4/5. Phrases surrounded by -- are my addition for clarification of context:
The value of the result of both operators -- (sizeof and _Alignof) -- is implementation-defined, and its type (an unsigned integer type) is size_t, defined in <stddef.h> (and other headers).
Implicitly, if I want my program to be standard conform and want to use sizeof, I need to include one of the header files in which size_t is defined, because the value it yields is of type size_t and I want to store the value in an appropriate object.
Of course, in any program which would not be a toy program I would need at least one of these headers all the way up regardless but in a simple program I need to explictly include those headers, although I do not need them otherwise.
Can I use an unsigned int object to store the size_t value sizeof yields without an explicit cast?
Like for example:
char a[19];
unsigned int b = sizeof(a);
I compiled that with gcc and -Wall and -Werror option flag but it did not had anything to complain.
But is that standard-conform?
It is permissible but it is your responsibility to provide that there will not be an overflow storing a value of the type size_t in an object of the type unsigned int. For unsigned integer types overflow is well-defined behavior.
However it is a bad programming style to use types that were not designed to store values of a wider integer type. This can be a reason of hidden bugs.
Usually the type size_t is an alias for the type unsigned long. On some 64-bit systems, the type unsigned long has the same size as the type unsigned long long, which is 8 bytes instead of the 4 bytes that unsigned int can be stored in.
It is totally conformant, though if you have a very large object (typically 4GB or larger) its size may not fit into an unsigned int. Otherwise there is nothing to worry about.
Having said that, your question and this answer probably have more characters than you would save by not including an appropriate header in a lifetime worth of toy programs.
This is allowed. It is an implicit conversion ("as if by assignment"). See the section labelled "Integer conversions":
A value of any integer type can be implicitly converted to any other integer type. Except where covered by promotions and boolean conversions above, the rules are:
if the target type can represent the value, the value is unchanged
otherwise, if the target type is unsigned, the value 2^(b-1), where b is the number of bits in the target type, is repeatedly subtracted or added to the source value until the result fits in the target type. In other words, unsigned integers implement modulo arithmetic.
In other words, this is always defined behaviour, but if the size is too big to fit in an unsigned int, it will be truncated.
In principle it is ok. The unsigned int can handle almost any sizeof except artificially constructed exotic things.
P.S. I have seen code similar to yours even in Linux kernel modules.
I tried to define a 'natural' type, like that:
typedef unsigned int nat;
But if I define a nat variable, that variable behaves like an ordinary int:
nat natural_index;
natural_index = 10; // That's what I want.
natural_index = -10; // Still a valid option.
In resume, I wanted to know why the compiler does not show a message, like
"-10 is not a unsigned int", and what could I do to define a 'natural' type.
extra information: I "printf" the variable natural_index, and the value '-10' was printed. I expected at least, another positive number (not exactly 10).
C doesn't support what you are trying to do, on two different levels.
First, a typedef in C does not create a new, distinct type; it just creates a shorthand name for the original type. Thus, after
typedef unsigned int nat;
the declaration
nat natural_index;
is 100% equivalent to
unsigned int natural_index;
(What's the point of typedef, then? It's most useful when the "underlying type" might differ based on the target architecture; for instance, the standard typedef uint64_t might be shorthand for unsigned long or unsigned long long depending on the architecture.)
Second, C has no mechanism for changing whether or not arithmetic expressions perform implicit conversions. In
natural_index = -10;
the assignment operator will convert the negative number -10 (with type int) to a large unsigned number (namely (UINT_MAX - 10) + 1, which is probably, but not necessarily, 4,294,967,286) in the process, and there is no way to disable that.
Your options are to work in a language that actually supports this kind of thing (e.g. Ada, Haskell, ML) or to write a "linting" program that parses C itself and enforces whatever rules you want it to enforce (existing examples are lint and sparse).
you forgot to separate the expression-statements with semicolon ;.
natural_index = 10;
natural_index = -10;
-10 to a large signed number during the evaluation of assignment operator =.
6.5.16.1p2 (Simple assignment) from ISO9899 says:
In simple assignment (=), the value of the right operand is converted to the type of the assignment expression.
When you use the storage class specifier typedef, this tells the parser to add an alias identifier nat for the type unsigned int in the environment of the parser or in the parse tree that it outputs. In this case nat will be evaluated to a type in declarations, so when you declare the object with identifier natural_index, the left-value associated with this object will have the type unsigned int.
I am a student currently doing an assignment requiring the casting of different data types in C. Our code must be portable so the use of sizeof is important. It is also suggested we do our own casting in order to be independent from the compiler from doing so. My question is how do I properly cast multiple data types against sizeof which returns size_t which is an unsigned integer value?
Below it looks strange but I am just converting the characters into bytes incase it is compiled on different architectures where a char might be larger/smaller than 1 byte.
short amountOfBytes;
char b = 10;
amountOfBytes+=(short)((unsigned int)b*sizeof(char));
Am I doing this right? Here's another example:
short a, b = 200;
a = (short)(((unsigned int)b+1) * sizeof(char));
Last question, is it better practice to cast by size_t or should I just stick with unsigned int?
Your code can invoke signed type overflow, which is not a defined operation. Thus the code is wrong.
Also sizeof(char) is defined as 1. This does not however mean 8 bits. See CHAR_BIT.
I have a problem with converting unsigned long to Float single precision
Here is my problem
I need to communicate in floating point in my program, but all my internal variables are unsigned long, So the design we decided was such that everything will remain same,(in unsigned long) and once a float needs to be given to application, i will convert to float and pass it on.
So all the variables will have a IEEE 754 bit stream according to
Float converter
Steps i follow :
I get the value in char array
I take the bits and copy into a unsigned long
While giving the data on function call, i typecast to float single precision.
In debugger i see the same bit pattern (in buffers) for steps 1 &2
In step 3 too, i see the same binary pattern, but the value is not interpreted as a float
Input : 21.125
Binary : 01000001101010010000000000000000
Hex : 0x41a90000
Decimal: 1101594624
Code:
void ApplicationApi(void * DataPtr)
{
(*(float32*)((void*)DataPtr))= unsignedLong_val;
}
Result in application DataPtr
* DataPtr = 1.101594624 * e9
Am i missing something here or the type case worked ?
A standard C way to reinterpret the bytes of an unsigned long as a float is:
y = (union { unsigned long u; float f; }) { x } .f;
This code defines a compound literal that is a union, and it initializes the union with the value of x, which should be an unsigned long. Then it accesses the f member of the union, which is a float. This float value is assigned to y, which should be a float.
Thus, assuming the declaration of ApplicationApi must remain the same and that the void *DataPtr it is passed is the result of converting a pointer to a float to a pointer to void, its definition could be:
void ApplicationApi(void *DataPtr)
{
* (float32 *) DataPtr =(union { unsigned long u; float32 f; }) { unsignedLong_val } .f;
}
Notes about this code:
This requires that unsigned long and float32 have the same size.
It is undesirable for unsignedLong_val to have file scope. It would be preferable to pass it as a parameter.
There is no reason inherent in this code that DataPtr should be passed as a void *. It could be passed as a float32 *.
Notes about code leading up to this code:
The way that the bytes have been assembled in the unsigned long may have endian issues.
In C implementations that are currently rare, an unsigned long could have trap values that prevent it from being used to hold arbitrary bytes.
About the C standard and reinterpreting bytes:
Reinterpreting bytes through a union is the result of C 2011 (N1570) 6.5.2.3 3, which says that an expression using the . operator and a member name has the value of the named member. Note 95 of the standard indicates that this may be used to reinterpret bytes.
Another supported way to reinterpret bytes is by converting a pointer to a pointer to a character type and using the new pointer to copy bytes, per 6.3.2.3 7. (memcpy is sometimes used for this.)
6.3.2.3 7 defines this behavior only for pointers to character types. For other types, the C standard generally does not define the behavior of converting a pointer to a pointer of another type and dereferencing the result. Therefore, the behavior of code that does this is undefined. Even if it appears to work in a test, it may later fail due to compiler optimizations or other effects.
Try with this cast :
void ApplicationApi(void * DataPtr)
{
*(float32*)DataPtr = *(float32*)unsignedLong_val;
}
You have to declare that the right value is a float value.
With your cast, the right value is an integer and the left value a float. The compiler convert implicitly the integer to a float value.