Sie sind auf Seite 1von 21

Data Types

Common definitions of
integral types
Implicit
specifier(s)
specifier
(s)

Explicit
specifier

Number Range
of bits

signed char
unsigned char
char
short
unsigned short

signed char
unsigned char
char
signed short int
unsigned short int

int

signed int

16 or 32

- 231 .. 231 - 1

unsigned

unsigned int

16 or 32

0 .. 232 - 1

long

signed long int

32 or 64

- 231 .. 231 - 1

unsigned long

unsigned long int

32 or 64

0 .. 232 - 1

long long
unsigned long long

signed long long int


unsigned long long int

8
8
8
16
16

64
64

- 27 .. 27-1
0.. 28 - 1
- 215 .. 215 - 1
0 .. 216 - 1

- 263 .. 263 - 1
0 .. 264 - 1

Unambiguous
type
(C99 names
from stdint.h)
int8_t
uint8_t
None
int16_t
uint16_t
int16_t or
int32_t
uint16_t or
uint32_t
int32_t or
int64_t
uint32_t or
uint64_t
int64_t
uint64_t

Unambiguous types
Following definitions are already defined in stdint.h header:
typedef unsigned char
typedef
signed char

uint8_t;
int8_t;

typedef unsigned short uint16_t;


typedef
signed short int16_t;
typedef unsigned long
typedef
signed long

uint32_t;
int32_t;

They should be used in place of the basic types.

typedef vs. #define


In order to define new, unambiguous types #define or typedef can be used
#define INT_PTR int*
typedef int *int_ptr;

When used, it looks like following


INT_PTR ptr1, ptr2;
int_ptr ptr3, ptr4;

WARNING: When you expand #define line you get the following
WARNING
int *

ptr1, ptr2;

It defines ptr2 as integer type and not a pointer to integer as it was expected.
So, when possible, use typedef instead of #define .

Does your compiler defaults


to signed or unsigned char?






The C and C++ standards allows the


character type char to be signed or unsigned,
depending on the platform and compiler.
x86 GNU/Linux and Microsoft Windows, use
signed char
PowerPC and ARM processors typically use
unsigned char
This can lead to unexpected results when
porting programs between platforms which
have different defaults for the type of char.

Signed or unsigned char ?


char c = 255;
if (c > 128)
{
printf ("char is unsigned (c = %d)\n", c);
}
else
{
printf ("char is signed (c = %d)\n", c);
}
return 0;

How to deal with existing


code?
For existing programs which assume that char is
signed or unsigned, GCC provides the options:



-fsigned-char
-funsigned-char

to set the default type of char.


Using these options, previous example code
compiles cleanly when char is unsigned:
$ gcc o signed -Wall -funsigned-char signed.c
$ ./signed
char is unsigned (c = 255)

How to deal with existing


code?
When compiling with the -fsigned-char the value
255 wraps around to -1, and should give warning:
$ gcc o signed -Wall -fsigned-char signed.c
signed.c: In function `main':
signed.c:7: warning: comparison is always false due to
limited range of data type
$ ./signed
char is signed (c = -1)

The warning message is one symptom of code which


assumes a definition of char which is different from the
actual type.

Floating point types

Floating point types are used to store real values, or to be more precise,
to store close approximation of real values.
According to the precision of the reals we have following types:

float = single-precision - occupies 32 bits (4 bytes) and its significant


has a precision of 24 bits (sign + about 7 decimal digits).

double = double-precision - occupies 64 bits (8 bytes) and its


significant has a precision of 53 bits (sign + about 16 decimal digits).
The standard header file float.h defines the minimum and maximum
values of the floating-point types

Type

Sign

Significant

Exponent

Total bits

float

23

32

double

52

11

64

enum example


Represent values across a series of


named constants

The first constant is assigned value


zero if value is not explicitly assigned
enum Days
{
Sunday = 1,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};

Each subsequent value is incremented


by one over the previous constant.

#defines are often used to create a


series of named constants
#define
#define
#define
#define
#define
#define
#define

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

1
2
3
4
5
6
7

What is the enums size ?




The compiler is free to choose the actual type


used based on the enumeration constants
defined, so:



it can choose a smaller type if it can represent the


values you define.
if you need enumeration constants that don't fit
into an int you will need to use compiler-specific
extensions to do so.

An enum is only guaranteed to be large


enough to hold int values if needed

Unions





A union is a structured type that aggregates a fixed set of labeled


objects (members), possibly of different types, into a single
object.
Difference to the struct is that in union each data member begins
at the same location in memory.
The size of a union is equal to the size of it's largest data
member.
typedef union _my_union
{
unsigned int
d1;
unsigned short d2;
unsigned char d3[3];
}my_union;

Type conversion



Use of different types of data within C programs creates a need for data
type conversions
Type conversion could be:


Implicit





Assignment automatic conversion when assigning value of variable of one


type to a variable of different type, e.g. assigning value of int variable to a
float variable).
Function call automatic conversion of function arguments, e.g. chars are
automatically converted to ints (if arguments are defined as ints).
Returned values automatic conversion of function return value, e.g. if a
function was declared to return a double and the return statement has an
integer expression, the integer value is automatically converted to a double.
Arithmetic conversions While preparing arithmetic or logical operations
and if two operands are not of the same type, the compiler may convert one or
both to a common type before the operation is performed.

Explicit


Typecasting when you explicitly force a conversion from one type to


another using a cast operation.

Type Promotions
Type promotion is one case of implicit type conversion, where
the compiler automatically expands the binary representation of
objects of integer or floating-point types. There are:


Integral Promotions


The narrower types are widened by following value preserving rules for
the conversion.:



(unsigned) char and (unsigned) short are widened to an int


all other arithmetic types are unchanged by the integral promotion

Wherever an int or an unsigned int may be used in an


expression, an instance of the narrower integral type (char,
short) may also be used.


Float Promotions


Type float is promoted to double

Arithmetic conversions
If both operands are of an integral type, integer division is used, else
real division is used. For example:
double half = 1/2;
Result: half = 0

Why ?

Because 1 and 2 are integer constants.


To fix this, change at least one of them to a real constant.
double half = 1.0/2;
Also, if both operands are integer variables and real division is desired, cast
one of the variables to double (or float).
int x = 5, y = 2;
double d = ((double) x)/y;

Arithmetic conversions





If either operand is of type long double, the other operand is converted to type long
double.
If the above condition is not met and either operand is of type double, the other
operand is converted to type double.
If the above two conditions are not met and either operand is of type float, the other
operand is converted to type float.
If the above three conditions are not met (none of the operands are of floating types),
then integral conversions are performed on the operands as follows:






If either operand is of type unsigned long, the other operand is converted to


type unsigned long.
If the above condition is not met and either operand is of type long and the other of
type unsigned int, both operands are converted to type unsigned long.
If the above two conditions are not met, and either operand is of type long, the other
operand is converted to type long.
If the above three conditions are not met, and either operand is of type unsigned int,
the other operand is converted to type unsigned int.
If none of the above conditions are met, both operands are converted to type int.

(Non)Safe conversions


Non-safe conversions
 Loss of value: Conversion to a type where the magnitude of the
value cannot be represented (e.g. uint32_t > 255 to uint8_t)
 Loss of sign: Conversion from a signed type to an unsigned
type resulting in loss of sign (e.g. int32_t to uint32_t)
 Loss of precision: Conversion from a floating point type to an
integer type with consequent loss of precision (e.g. float to
int)
Safe conversions
 Conversion of an integral value to a wider type of the same
signedness (e.g. uint8_t to uint32_t)
 Conversion of a floating type to a wider floating type (e.g. float
to double)

What to avoid to be safe ?


Avoid implicit conversions:






between signed and unsigned types


between integer and floating types
from wider to narrower types
of function arguments
of function return expressions

Pointer type conversions




Pointers can be classified as:







pointer to object
pointer to function
pointer to void
the null pointer

Pointer cast


A cast should not be performed




between a pointer type and an integral type.





The size of integral type may not be adequate to store pointer value.
Sometimes, it may be unavoidable, e.g. when addressing memory mapped
registers or other hardware specific features.
#define CPU_REG_1 (*(volatile unsigned long*)(0x81001000))

between a pointer to object type and a pointer to other object type.




Conversions of this type may be invalid if the new pointer type requires a
stricter alignment (which will be discussed later)
uint8_t p1[4];
uint32_t *p2;
p2 = (uint32_t *)p1; /* incompatibile alignment */

A cast shall not be performed if removes any const or volatile


qualification.

Pointer converisons


Valid conversions are following:






A pointer to void to a pointer to an object of any type and vice versa.


constant 0 to pointer (the only integer that can be safely converted to ptr)
A null pointer to a particular pointer type is converted automatically in the
case of assignment or comparison

Invalid conversions are following






without an explicit cast - pointer to an object of any type to a pointer to an


object of a different type
without an explicit cast - a pointer to a function of one type to a pointer to
a function of a different type
even with an explicit cast - a function pointer to an object pointer or a
pointer to void, or vice-versa.

Das könnte Ihnen auch gefallen