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 .