Table of Contents
What are the different C data types ?
The C standard groups its available types , in two large categories : the object types , and the function types .
The object types , in C are :
Integer types such as int , short , enum … Floating point number types such as float , double , long double . Structures Unions Arrays Pointers Atomic void
In C , the object types , also have many conceptual division . For example , the scalar category , is the category formed of the integers , such as int , and the floating point types , such as float , and the pointers .
A tutorial about the conversion between the arithmetic types , can be found here , this tutorial is about casting or the conversion , between the non arithmetic types .
What is the void type ?
The void type can be understood as the absence of a value .
For example , when declaring a function to have a void return type , this means that there is no value to be returned by this function . Also when stating that the function has , a not named , void parameter type , this means that this function has no parameters , as such it takes no arguments .
#include<stdio.h>
void helloWorld(void ){
printf("hello world \n" );
/*The helloWorld function , has no parameters ,
as such it has one parameter of type void .
It returns no values , as such as its
return type , it also has the void type .*/}
int main(void ){
helloWorld( );}
/*Output :
hello world .*/
The void type , can also be used to disregard any value , returned by any expression . So an expression cast to the void type , has its value disregarded , or thrown away . A void expression , is evaluated , for its side effect .
(void) 1 ;
/* The literal 1 is of the int type ,
it is cast to the void type . The
result is an expression of the
void type , as a consequence ,
the expression has no value .*/
void helloWorld(void ){
printf("hello world \n" );}
helloWorld("Hello world" );
/*The expression , has a void type ,
since the return type of the
helloWorld function is void . As
a consequence , the expression has
no value . The helloWorld function ,
is used for printing the message
Hello world .*/
An expression of type void , cannot be cast , to any other type .
Pointer types
What are the different available pointer types ?
A pointer in C can be a pointer to an object type , or it can be a pointer to a function .
Which integer types are capable of storing pointers ?
A pointer holds an address . An address has a numeric value , and it has a type .
#include<stdio.h>
int main(void ){
int val_i = 1 ;
/*Declare and initialize the
variable val_i .*/
int *ptr_i = &val_i;
/*ptr_i is a pointer to an int .
It contains a numeric value ,
which is the address of
val_i . This address is of type
int . So when dereferencing
this address , a specific number of
bits , equal to the number of bits
in the type int , is read .*/
printf("@address : %p -> value : %d\n", ptr_i , *ptr_i ) ;
/*Print the address stored in ptr_i ,
and dereference the address ,
to get the stored value .*/
/*Output :
@address : 0x7fff59e438ec -> value : 1 */}
The numeric value stored in a pointer is of an integer data type . The C standard specifies , that the integer data type stored in a pointer , can be stored in intptr_t and uintptr_t . Hence casting between pointers , and intptr_t , or uintptr_t , is always defined , by the C standard . Both intptr_t , and uintptr_t , are defined , in the stdint.h header .
For other integer data types , if the integer value of the pointer , is larger than the range , of the to convert to , integer data type , the result of the casting is not defined . And the inverse , which is if the integer data type value , has a number of bits larger than intptr_t or uintptr_t , the result of the conversion is not defined .
#include<stdio.h>
#include<stdint.h>
int main(void ){
int val_i = 1 ;
/*Declare an int variable
val_i , and initialize
it , with 1 .*/
int *ptr_i = &val_i;
/*Declare a pointer to an integer ,
and initialize it with the address
of val_i */
uintptr_t t_uip = (uintptr_t) ptr_i;
/*Cast a pointer to uintptr_t */
printf("ptr_i : %p , t_uip : %#lx\n" , ptr_i , t_uip );
/*Print the address stored in the pointer
ptr_i , and the hexadecimal numeric value
stored in t_uip .*/
ptr_i = (int * ) t_uip;
/*Cast the integer value stored
in t_uip , to a pointer , to an
int , and assign the result
to ptr_i .*/
printf("ptr_i : %p , *ptr_i : %d\n" , ptr_i , *ptr_i );
/*Print the address of ptr_i , and the
value stored by the address
referenced by ptr_i .*/
}
/*Output :
ptr_i : 0x7fff599d88ec , t_uip : 0x7fff599d88ec
ptr_i : 0x7fff599d88ec , *ptr_i : 1 */
What is a pointer to void ?
A pointer to void , or the void pointer , can be understood as meaning , that for now , the type of the pointer , is not of interest . The absence of value in this case , is the type of the address , not the numeric value stored in the address . As stated earlier , a pointer variable stores address . An address has a numeric value , and a type .
#include<stdio.h>
int main(void ){
void *ptr_v = (void *) 1 ;
/*1 is an int literal , an integer
can be cast to any pointer type .
In this case , it is cast to
a pointer to void .
The gotten address has a numeric value ,
it has a void type , as such it does
not hava a type .*/
printf("The stored address in ptr_v numeric value is : %p\n" , ptr_v );}
/*Output :
The stored address in ptr_v numeric value is : 0x1 .*/
Any pointer type can be cast to a void pointer type , and any void pointer type , can be cast to any pointer type .
/*In this First example , any pointer sent to
the printAddressVar function , is
cast to a void pointer .
The printAddressVar function , prints
the numeric value of the address
stored in the pointer .*/
#include<stdio.h>
void printAddressVar(void *var_vptr){
printf("%p\n" , var_vptr );}
int main(void ){
int flowers_i = 10 ;
printAddressVar(&flowers_i );
float angle_f = 3.4f;
printAddressVar(&angle_f );
double elevation_d = 1.3;
printAddressVar(&elevation_d );}
/*Output :
0x7fff5dc4f8e4
0x7fff5dc4f8e0
0x7fff5dc4f8d8 */
/*Second example .*/
#include<stdio.h>
int add (int x , int y ){return x+y; };
int sub (int x , int y ){return x-y; };
int mul (int x , int y ){return x*y; };
/*Declare , the add , sub , and multiply
function .*/
typedef int (* fct_signature ) (int , int) ;
/*fct_signature is alias to : pointer to a
function that takes two int , and
returns , an int .*/
int main(void ){
void *arr_vptr[] = {add , sub , (void *) mul };
/*add , sub , mul , are functions .
A pointer to each , has a signature of
int (* ) (int , int ) .
Each pointer to each function is cast
to the void pointer .
The gotten casts are stored in
the array arr_vptr .*/
int x = 1 , y = 2 , result = 0;
result = ((fct_signature ) arr_vptr[0] )(x , y );
/*Cast arr_vptr[0] to a pointer to a function .
The pointer to the function has a signature :
int (* ) (int , int ). After the casting ,
the function is called .*/
printf("%d\n" , result );
/*Output : 3 */
result = ((int (* ) (int , int )) arr_vptr[1] )(x , y );
printf("%d\n" , result );
/*Output : -1 */
result = ((fct_signature ) arr_vptr[2] )(x , y );
printf("%d\n" , result );
/*Output : 2 */}
What is the null pointer ?
The null pointer constant , or shortly , the null pointer , is defined by the C standard , as having a value , the constant literal 0 .
A null pointer constant , is also defined by the C standard , as casting its constant literal value of 0 , to a pointer to the void type . The two definitions are equal , or the same .
#define NULL_PTR_CONSTANT 0 #define NULL_PTR_CONSTANT ((void* ) 0 )
The NULL macro , is defined in the standard header , stddef.h , as a null pointer constant .
A null pointer constant can be cast , to any other pointer type , the result is a null pointer , of that type . All null pointers compare equal .
#define NULL ((int * ) 0 )
/*Cast the null pointer 0 , to
a pointer to an int , the
result is a null pointer .*/
#include<stdio.h>
int main(void ){
printf("%d\n" , (int *) 0 == 0 );
/*All null pointers are equal , printf
outputs 1 , which is true .
1 */}
A null pointer is not equal , to a not null pointer , so a null pointer is not equal to a pointer , which does not have the value 0 . A null pointer , can be used , in the initialization of pointer variables .
#include<stdio.h>
int main(void ){
int var_i = 1 ;
int *var2_ip = &var_i;
/*Initialize var2_ip with the address
of var_i .*/
int *var3_ip = 0 ;
/* The null pointer constant 0
is cast , to (int * ) , the
result is a null pointer of the
int type , the null pointer
is stored in var3_ip .*/
printf("%d\n" , var3_ip != var2_ip );
/*A null pointer is not equal , to a
not null pointer , as such , printf
outputs 1 , which is true .*/}
A null pointer constant can be understood , as itself , having a value of the constant literal 0 , the bit pattern used to represent a null pointer is not necessarily all 0 . A null pointer constant symbolizes , the absence of the numeric value , of the address , hence dereferencing a null pointer , is not defined . The address itself can be untyped , such as in the case , of a void null pointer , or typed , such as in the case , of an int null pointer .
Casting a null pointer constant , to an integer type , yields 0 , since the value of a null pointer , not the value of dereferencing a null pointer , is the constant literal 0 .
#include<stdio.h>
int main(void ){
void *ptr_v = 0 ;
printf("%d\n" , (int ) ptr_v );
int *ptr_i = 0;
printf("%d\n" , (int ) ptr_i ); }
/*Output :
0
0*/
Casting object type pointers
The key thing to remember , is that a pointer stores an address . An address has a numeric value , and since C is a typed language , an address has a type .
When dealing with casting between object type pointers , the numeric value stored in the address does not change , only the type of the address , is being reinterpreted .
This being said , object types pointers , can be cast between one another , the only requirement is that the object types have a common alignment . Alignment is where an object , can be placed in memory . If the object types , do not have a common alignment , the result of casting one pointer to another , is not defined .
/*Example 1 */
#include<stdio.h>
int main(void ){
float var_f = 1.0f ;
int var_i = (int ) var_f ;
/*Casting a float type to an int
type , will change its bit
representation .
1.0f is encoded as :
00111111100000000000000000000000
1 is encoded as :
00000000000000000000000000000001*/
printf("%f , %d\n" , var_f , var_i );
/*Output :
1.000000 , 1 */
float *ptr_f = &var_f ;
int *ptr_i = (int *) ptr_f ;
/*ptr_f is cast to a pointer
to an int . The address
stored in ptr_f is copied to
ptr_i .
The bit pattern stored at that
address did not change . It is
00111111100000000000000000000000 */
printf("%f , %d\n" , *ptr_f , *ptr_i );
/*Output :
1.000000 , 1065353216 */ }
/*Example 2 */
#include<stddef.h>
#include<stdio.h>
void toHex(unsigned char *ptr_uc , size_t size_data ){
printf("%p : " , ptr_uc );
/*Print the address of ptr_uc */
for(size_t i = 0 ; i < size_data ; i++ )
printf("%02x" , ptr_uc[i] );
/*Print the hexadecimal representation
Of data .*/
printf("\n" );}
struct flag{
unsigned char num_stars ;
unsigned int num_colors;
};
int main(void ){
struct flag var_struct_flag = {255 , 4294967295 };
toHex((unsigned char *)&var_struct_flag , sizeof(struct flag ));
/*Output :
0x7fff539528e8 : ff000000ffffffff ,
address data hex dump
The structure is padded with 6 bytes ,
this is why , there are six 0 between
255 , and 4294967295 .*/
struct flag var_struct_flag1 = {0 , 0x0000FFFF };
toHex((unsigned char *)&var_struct_flag1 , sizeof(struct flag ));}
/*Output :
0x7fff599b68e0 : 00000000ffff0000
adress data hex dump
This is a little Indian machine ,
since the int type is stored in
reverse .*/
As stated earlier , the void pointer can be cast to any other pointer , and vice versa , and the null constant pointer , can be cast to any other pointer .
Casting between qualified and unqualified pointer types
The C standard defines , that casting a pointer to an unqualified type , such as int * , to a pointer of the same type qualified , such as const int * , is always defined .
The available type qualifiers in C , are const , volatile , restrict and _Atomic.
#include<stdio.h>
int main(void ){
int var_i = 1 ;
int *ptr_i = &var_i ;
const int *ptr_ci = ptr_i;
/* *ptr_ci = 10;
is illegal , because the
pointer is a pointer to
a const int. */
*ptr_i = 10 ;
/* Legal , because the pointer
is a pointer to an int .*/}
Casting function type pointers
Pointers to functions , can be cast between one another . Also , as stated earlier , the void type can be cast to any other type , and any other type can be cast to the void type , and the null constant pointer , can be cast to any other pointer type .
If a pointer to a function , is cast , to another function , pointer type , and the target function type , is not compatible with the source function type , calling the function using the target type , is not defined .
int negate(int x ) {return -x ;}
int subtract(int x , int y ) {return x-y ;}
int main(void ){
int (* sig1_ptr ) (int ) = negate;
int (* sig2_ptr ) (int , int ) = subtract;
sig2_ptr = (int (* ) (int , int )) sig1_ptr;
/* Any function pointer can be cast
to any other function pointer .
If the signature of the casted
function , is not compatible with the
signature of the pointer function type ,
calling the function pointed by the
pointer , is not defined . Hence
sig2_ptr(1 , 1 ) is not defined .*/ }
Comparing pointers for order
The relational operators , less < , less or equal <= , larger > , larger or equal >= , can be used to compare for order .
Comparing pointers for order , is only defined for object type pointers , and it is only to be used , when comparing pointers to members of structures , arrays , and unions .
Pointers pointing to objects in a structure , declared after objects in the same structure , or to elements in an array , having a position higher , then elements in the same array , have a higher order.
The inverse is true , so pointers pointing to object in a structure , declared before objects in the same structure , or to elements in an array , having a position lower, then elements in the same array , have a lower order .
The last element comparable for order in an array , is equal to the array length plus one .
Pointers to any member of the same union , have the same order . Pointers to members of the same array , or to member of the same structure , having the same pointer address : type and numeric value , have also the same order .
#include<stdio.h>
struct rational {
int numerator ;
int denominator; };
union search{
int close_i ;
int far_i; };
int main(void ){
struct rational var_rat = {1 , 2 };
int *num_ip = &var_rat.numerator ;
/*Store the address of numerator , in
the pointer num_ip*/
int *den_ip = &var_rat.denominator ;
/*Store the address of denominator ,
in the pointer den_ip */
printf("%d\n" , num_ip < den_ip );
/*numerator is declared before denominator ,
in struct rational , as such its address
has a lower order .
Output : 1 .*/
union search var_ser;
int *close_ip = &var_ser.close_i;
int *far_ip = &var_ser.far_i;
printf("%d\n" , close_ip <= far_ip );
/*close_i and far_i , are member of the
same union , as such their address
have the same order ,
Output : 1 .*/
printf("%d\n" , close_ip < far_ip );
/*close_i and far_i , are member of the
same union , as such their address
have the same order ,
Output : 0 .*/
int arr_i[] = {1 };
printf("%d\n" , arr_i < arr_i + 1 );
/*address last element of array
plus one , has higher order than
preceding element .
Output : 1 .*/
printf("%d\n" , num_ip <= &var_rat.numerator );
/*Pointers same member of structure , are
equal .*/}
Comparing pointers for equality
When using the equality == or difference operators!= , two pointers are equal , if the address stored has the same numeric value , and the same type .
If one operand is a pointer , and the second one is a null pointer constant ,then the null pointer constant is cast to the pointer type , before performing comparison .
If one operand is a pointer to an object type , and the other operand is a qualified or unqualified pointer to void , then the object pointer is cast to a qualified or unqualified pointer to void .
#include<stdio.h>
int main(void ){
int var_i = 1;
int *ptr_i = &var_i;
int *ptr1_i = &var_i;
double var_d = 1.0;
void *ptr_v = &var_d;
printf("%d\n" , ptr_i == ptr1_i );
/*Both ptr_i , and ptr1_i , address
contain the same numeric value ,
and type , they are equal
Output : 0.*/
printf("%d\n" , ptr_i == 0 );
/*The constant pointer literal
0 , is cast to a null pointer
literal of the type int .
A null pointer is not equal
to a non null pointer , as such
the result is false .
Output : 0.*/
printf("%d\n" , ptr_i == ptr_v );
/*ptr_v is a void pointer , ptr_i
is cast to a void pointer , the
numeric value of the address are
not equal , as such the result
is false .
Output : 0 .*/ }
Logical operators
The logical operators , and && , or || , not ! , can be used with the scalar types . The scalar types are the integer types , the floating point types , and the pointer types .
&& will yield 0 , if any of its operands is 0 .
|| will yield 1 , if any of its operands is 1 .
! will yield 1 , if its operand is 0 , or 1 otherwise .
So in the case of pointers , if the pointer is a null pointer , then && will yield false or 0 , if the pointer is not a null pointer , then || will yield true , or 1 . As for ! , it will yield 0 , if the pointer is not a null pointer , and it will yield 1 if the pointer is the null pointer .
#include<stdio.h>
int main(void ){
int var_i = 1 ;
int *ptr_i = 0;
if(!ptr_i )
/* Apply , the not operator ,
on ptr_i . ptr_i is the null
pointer , it has a value of 0 ,
as such after applying the not
operator , it will have a value of
1 . When 1 , if execute , the
following statement , which initialize
the pointer to the address of
var_i .*/
ptr_i = &var_i;
if(ptr_i || 0 )
/* ptr_i , is not null , as such
|| does not evaluate the ,
second expression , and returns
1. On 1 , if exceutes the
following statement , which
prints the value found ,
in ptr_i .
Output : 1 .*/
printf("%d\n", *ptr_i );
if(ptr_i && 0 )
/* ptr_i is not a null pointer ,
&& evaluates 0 . On 0 ,
it returns 0 .
If , on 0 , does not execute ,
the next statement , hence
printf is not executed .*/
printf("%d\n", *ptr_i );}
Addition
For pointer types , using the addition operator , + , is defined when , one operand is of an integer type , and the second operand is a pointer to a complete object type .
A complete object type , is an object which has a size . For example , the void type is not a complete object type , because it does not have a size , a structure which is declared , but not defined , so which does not have a body , is another example of an incomplete type .
struct t_s;
When adding an integer value , to the numeric value stored in a pointer , it is not the integer value which is added , but the integer value , multiplied by the size of the object , that the pointer points to .
For the addition of an integer to a pointer , to be valid , the pointer must be a pointer to an object member of an array , or to an element past the last object member of an array , and the result of the addition , must be a pointer to an object of the array , or an element one past the last object of the array , or else pointer addition is not defined .
The element past the last object member of the array , is not necessarily a null pointer , but in all cases it must not be dereferenced using the * operator .
#include<stdio.h>
int main(void ){
float arr_f[] = {1.0f , 2.0f };
float *ptr_f = &arr_f[0 ];
printf("sizeof(float ) : %zd\n\n" , sizeof(float ));
/*Print the size of the type of the object
pointed by ptr_f */
printf(" %p : ptr_f\n %p : ptr_f+0\n %p : ptr_f+1\n %p : ptr_f+2 \n" ,
ptr_f , ptr_f + 0 , ptr_f + 1 , ptr_f + 2 );
/*Perform pointer addition , and print the
successive addresses .
add 0 , 1 , and 2 , to ptr_f .
ptr_f points to the first element ,
of the array arr_f . The addition ,
is defined , as long as
the gotten pointer , is a pointer to an
element of the array arr_f , or one past ,
the last element , of the array arr_f .*/
printf("ptr_f + 2 == (void * ) 0 -? %d \n\n\n" , (ptr_f + 2 ) == 0 );
/*The element past the last object ,
member of an array , is not
necessarily the null pointer ,
and it must not be dereferenced .*/
char *ptr_c = (char * ) ptr_f;
/*Cast the pointer ptr_f , to a
pointer to a char .*/
printf("sizeof(char ) : %zd\n\n" , sizeof(char ));
/*Print the size of the type
of the object , pointed by
ptr_c .*/
printf(" %p : ptr_c\n %p : ptr_c+0\n %p : ptr_c+1\n %p : ptr_c+2 \n"
" %p : ptr_c+3\n %p : ptr_c+4\n %p : ptr_c+5 \n"
" %p : ptr_f+6\n %p : ptr_c+7\n %p : ptr_c+8 \n\n\n"
, ptr_c , ptr_c + 0 , ptr_c + 1 , ptr_c + 2 , ptr_c + 3 , ptr_c + 4
, ptr_c + 5 , ptr_c + 6 , ptr_c + 7 , ptr_c + 8 );
/*Perform pointer addition . The pointer is now a pointer
to an object of type char . The addition is still valid ,
as long as the gotten pointer , is a pointer to
an object in the array , or one past the last
oject in the array . The array is now interpreted ,
as being , an array of characters .*/}
/*Output :
sizeof(float ) : 4
0x7fff5230c8c0 : ptr_f
0x7fff5230c8c0 : ptr_f+0
0x7fff5230c8c4 : ptr_f+1
0x7fff5230c8c8 : ptr_f+2
ptr_f + 2 == (void * ) 0 -? 0
sizeof(char ) : 1
0x7fff5230c8c0 : ptr_c
0x7fff5230c8c0 : ptr_c+0
0x7fff5230c8c1 : ptr_c+1
0x7fff5230c8c2 : ptr_c+2
0x7fff5230c8c3 : ptr_c+3
0x7fff5230c8c4 : ptr_c+4
0x7fff5230c8c5 : ptr_c+5
0x7fff5230c8c6 : ptr_f+6
0x7fff5230c8c7 : ptr_c+7
0x7fff5230c8c8 : ptr_c+8 */
Subtraction
Subtraction is defined for pointers , when the subtraction , is the subtraction of one pointer to a complete object type , from another pointer to a complete object type . Subtraction is also defined for pointers , when subtracting a pointer to a complete object type , from an integer value . A complete object type , is one that has a size .
For subtraction of two pointers to be valid , they must point to objects that belong to the same array . Pointing to the element , past the last object that belongs to an array , is also permissible .
The result of subtracting , a pointer from another one , is of type ptrdiff_t . This result is the distance between the two pointers , as a count of a size , of the object type of these two pointers . ptrdiff_t is defined , in the stddef.h header .
#include<stdio.h>
int main(void ){
int arr_i [] = {0 , 1 };
int *ptr_i = &arr_i[0 ];
printf("%td\n" , ptr_i - &arr_i[1 ]);
/*Print the difference between
the two pointers . The result is
of the type ptrdiff_t , hence the use
of %td .
Output : -1 .*/
printf("%td\n" , &arr_i[2] - ptr_i );
/*Print the difference between
the two pointers . The distance from
one past the last object , member of
the array , to the first object member
in the array is 2 int , since the pointers
are of type int .
Output : 2 .*/
char *ptr_c = (char * )ptr_i ;
printf("%td\n" , (char * ) &arr_i[2] - ptr_c );
/*Cast ptr_i , to a pointer to a char .
Print the difference , between one
past the last object member of the
array , now interpreted as a char ,
and the first object member of the
array .
Output : 8 .*/}
For subtraction of a pointer , from an integer , to be valid , the pointer must be a pointer to an object , of an array , or to an element past the last object member of an array , and the result must be a pointer to an object , of the array , or it must point , to an element one past the last object , of the array .
Subtracting by an integer from a pointer , is subtracting the integer , multiplied by the size of the object , that the pointer points to , from the numeric value , of the address stored in the pointer .
#include<stdio.h>
int main(void ){
unsigned char arr_uc[] = {0 , 255 , 0 , 255 };
/*Declare an array of type unsigned char ,
and initialize it .*/
unsigned char *ptr_uc = (unsigned char * ) &arr_uc[4 ];
/*Get a pointer , to one past the last
object , member of the array
arr_uc .*/
int length_arr_uc = sizeof(arr_uc ) / sizeof (unsigned char );
/*Calculate the length of the
array of type , unsigned char .*/
for(int i = length_arr_uc ; i >= 0 ; i-- ){
/*Print the address , and value if any ,
of address accessible using subtraction
by an integer from a pointer , which points
to one past the last object , member
of the array .*/
printf("%p : " , ptr_uc - i );
if(ptr_uc - i != ptr_uc )
printf("%u" , *(ptr_uc -i ));
printf("\n" );}}
/*Output :
0x7fff559338e8 : 0
0x7fff559338e9 : 255
0x7fff559338ea : 0
0x7fff559338eb : 255
0x7fff559338ec : */
When performing addition and subtraction on pointers to complete object types , a pointer to a complete object , which is not a member , or one past the last element of an array , acts as if the object , is an array of length one , and of a type , the type of the object , that the pointer points to .
#include<stdio.h>
int main(void ){
int var_i = 2002874948 ;
/*Declare an int variable ,
having a value of 2002874948 .*/
int size_of_var_i = sizeof(var_i );
/*Get the size of the int
variable . The size is
returned in bytes .
1 char , has a size of 1 byte .*/
char *ptr_c = (char *) &var_i;
/*Get a pointer to the int
variable , and cast it to
pointer , to a char .*/
for(int i = 0 ; i < size_of_var_i ; i++ )
printf("%c" , *(ptr_c + i ));}
/*output :
Draw */
Postfix and prefix , increment and decrement operators
The postfix , and prefix , increment and decrement operators : ++ , -- , can be used on pointer types .
When incrementing or decrementing a pointer , the address stored in the pointer , is incremented or decremented by the size of the object , that the pointer points to .
The result of incrementing and decrementing using the postfix operators , is only accessible from the next statement , so on the statement , where they are being executed , the postfix increment , and decrement operators , return the operand value unchanged .
The prefix , increment and decrement operators , increment or decrement the operand , and return the incremented or decremented operand .
#include<stdio.h>
int main(void ){
char array_c[ ] = "aa";
/*Create an array of length 3 ,
initialized with the characters
a a , and terminated with
the null character . The null
character , has all of its
bits , set to 0 .*/
char *ptr_c = &array_c[0 ];
/*ptr_c is a pointer of
typ char , it contains ,
the address of the first
object , member of the array
array_c .*/
while(ptr_c && *ptr_c != '\0' ){
printf("%#2x\n" , *ptr_c++);}
/*Print the hexadecimal representation ,
of the data stored in array_c .
The pointer is incremented using ,
the postfix operator , hence
ptr_c value is only incremented ,
starting the next statement .
Output :
0x61
0x61 */
printf("\n" );
ptr_c = &array_c[0 ];
/*Rewind the pointer ,
to the address of the
first object , stored in
array_c .*/
while(ptr_c && *ptr_c != '\0' ){
printf("%#2x\n" , *++ptr_c);}
/*ptr_c contains the address
of the first object , member
of the array array_c .
The while statement , loops
through array_c elements , using
the prefix increment operator ,
the address stored in ptr_c , is
first incremented . Next , it is
dereferenced .
Output :
0x61
0 */ }
Multiplication , division
Multiplication and division only applies to the integer and floating point types , as such it does not apply to the pointer types .
Bitwise operations
Bitwise operations , only apply to the integer types , as such they are not defined for the pointer type .
The conditional operator
The conditional operator , has the format :
Operand_One?Operand_Two:Operand_Three
Operand_One must be a scalar . The scalar types in C , are the integer , the floating , and the pointer types .
If a pointer is to be used as the second or the third operand , then :
Either , one operand is a pointer to an object , and the second operand is a pointer to a qualified or unqualified void pointer . In this case , the pointer to the object is converted , to the qualified or unqualified version , of the void pointer .
int *ptr_i ; const void *ptr_v ; ptr_v = 1 ? ptr_i : ptr_v ; /*When the first operand is 1 , the second operand is evaluated . ptr_i is cast using (const void * ) .*/
Either , one operand is a pointer to a C type , and the second operand is a null pointer constant . In this case the null pointer constant is converted , to a pointer of a type , of the pointed to , C type .
volatile short *ptr_vs; ptr_vs = 0 ? ptr_vs : 0 ; /*When the first operand is 0 , the third operand is evaluated. The result is a volatile short null pointer . */
Either , both pointer operand , have the same C type , but with different or same type qualifier . The result is a pointer , pointing to the same type , having all the qualifiers .
volatile int *ptr_vi; const int * ptr_ci; const volatile int *ptr_cvi = 1 ? ptr_vi : ptr_ci ; /*When the first operand is 1 , the second operand ptr_vi is evaluated. The result is a constant volatile int pointer . */
Arrays
An array is an aggregate type , casting to an array type , of the like (int [ ]) , or (int [3 ]) , cannot be performed . Casting can be only performed to a scalar type , such as (int * ) . The scalar types are , the integer types , the floating point types , and the pointer types .
Bitwise operators such as shifting << , do not apply to an array , they only apply to an integer type .
Multiplication and division , only apply to the integer and floating point types , as such they don't apply to arrays .
An array acts as a pointer , when it is being passed to a function , when performing comparison for equality such as != , or for order such as <= . An array is also treated as a pointer , when perform logical operation , such as && and when performing addition , + and subtraction - .
The pointer that the array acts as , is the address of the first element of the array , This is a constant pointer , so its address cannot be changed .
#include<stdio.h>
int add(int *ptr_i , int length ){
int sum = 0 ;
for(int i = 0 ; i < length ; i++ )
sum += *(ptr_i + i );
return sum;}
int main(void ){
int arr_i[] = {1 , 2 , 4 , 6};
/*Create an int array , containing
4 elements .*/
printf("sizeof(arr_i ) : %zd bytes\n", sizeof(arr_i ));
/*Print the size of the array ,
Output :
sizeof(arr_i ) : 16 bytes */
const int *cptr_i = arr_i;
/*When assigning an array to a pointer ,
the array act as a constant pointer to
its first element .*/
printf("sizeof(cptr_i ) : %zd bytes\n", sizeof(cptr_i ));
/*Prints the size of the pointer , not the size
of the array .
Output :
sizeof(cptr_i ) : 8 bytes */
printf("Number of elements in array : %zd\n" , sizeof(arr_i ) / sizeof(arr_i[1 ] ));
/*Print the number of elements in the array .
This can be gotten by dividing the size of the
array , which is 16 bytes , by the size of
an element in an array.
The size of an int on this machine is 4 bytes ,
as such the number of elements is 4 .
Output :
Number of elements in array : 4 */
int * ptr_i = 0;
if(ptr_i != arr_i )
ptr_i = arr_i;
/*When performing comparison
operations , the array
acts as a constant pointer , to
its first element .
The null pointer is different
from a not null pointer , hence
the assignment operation is
performed , and ptr_i is a
pointer , to the first
element of the array .*/
printf("2nd element of array is : %d\n" , *(arr_i + 3 ));
/*Print the value of the second ,
element of the array . When performing
addition or subtraction on an array ,
it acts as constant pointer to its
first element . This is
pointer addition or subtraction .
Output :
2nd element of array is : 6 */
printf("sum elements array : %d\n" , add(arr_i , 4 ));
/*When passed to a function , the array acts
as a constant pointer , to its first element .
The recieving function parameter , get the
refered address .
The add function calculates , the sum of the
elements , of the array .
Output :
sum elements array : 13 */
if( (arr_i < arr_i + 1 ) && arr_i )
printf("True\n" );
/*When using order comparison < <= >= > ,
the array acts as a constant pointer ,
to its first element , the address
of the first element is less than
the address of the second element
of the array .
&& evaluates its first operand ,
which returns 1 . Since the first
operand returned 1 , &&
evaluates , its second operand .
The second operand is arr_i , since
this is a logical operation , arr_i
acts as a constant pointer to its
first element , the gotten pointer
is not a null pointer , hence the
second operand of && evaluates to 1 ,
as such && evaluates to 1 .
When 1 , the if statement ,
executes , the next statement ,
which prints True .
Output:
True .*/}
An array cannot be incremented , or decremented , using the postfix and prefix increment ++ and decrement -- , operators .
Structures , Unions
Casting only applies to the scalar types , the scalar types are the integer types , the floating point types , and the pointer types , as such it does not apply to structures and union .
It is not possible to use the order , or the equality , or the bitwise , or the logical , or the multiplication and division , or the addition and subtraction , or the postfix and prefix increment and decrement operators , with structure and unions .
Structures and unions can be the second , and third operand of the ternary operator ?: , in such case , they must have the same type .
