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 .