What is an identifier in c ?

What is an identifier ?

 
An identifier , is used to denote an object type in C . An object in C is a region of memory , used for data storage .

int x ; /*
x is an identifier , or the 
  label for an area of storage 
  of type int .*/

struct time{
  int hours ;
  int seconds;
};/*
time is an identifier , for an 
  aggregate user defined type ,
  struct .
time is also known as a tag . The
  term tag , is also used to refer ,
  to user defined types : enum , 
  union .
struct time can be used to define ,
  new variables , of type struct
  time . 
variables of types struct time , 
   occupy a region of storage 
   in memory capable of holding
   two integers .*/


enum color{red , green , blue }/*
color is an identifier , for a user
  defined type , enum . In this ,
  case the user defined type , is
  an integer . color is also 
  known as a tag . 
enum color , can be used to define 
  new variables of type enum color ,
  which can take integer values .
red , green , blue are integer constants .*/

Object types in c are :

  • integer types , such as int , short , enum …
  • floating types , such as float , double , long double .
  • structures .
  • unions .
  • arrays .
  • pointers .
  • atomic types .

 
An identifier , is also used to denote members of : unions , structs , and enums . So in the previous example , hours , seconds , red , and green are also identifiers .

 
An identifier can also be used to denote a function , for example :

int aFunction(void);/*
aFunction is an identifier . */

 
Identifiers can also be used to denote : label names.

#include<stdio.h>
void edit(int code){
  if(code == 0) 
    goto success ;
  else
    goto failure ;
 success: // success is an identifier for a label
  printf("sucess\n");
 failure: // failure is an identifier for a label
  printf("failure\n");}

int main(void){
  edit(0);
  edit(1);}/*
Output : 
sucess
failure
failure*/

 
Finally an identifier can be used to denote : a typedef name , a macro name , and a macro parameter .

typedef float weight ; /*
weight is an identifier */ 


#define SUM(x,y) (x + y) ;/*
SUM , x , y are identifiers.*/

The characters , and length of an identifier

An identifier is case sensitive , it can contain the following characters : [A-Za-z0-9_] . It must start with a non digit character , so it cannot start with digits from 0 , till 9 .

Universal character names , can also be used in identifiers , so a \u followed by four hex digits , or a \U followed by 8 hex digits , can be used in an identifier . The hex digits , must form a valid , unicode code number .

Universal character names , can be used on systems , where some characters are not available . The range of universal character names , which are allowed , can be found in annex D.1 of the C standard , as for those which are not allowed at the start of an identifier , they can be found in annex D.2 of the C standard .

/*
Example of a valid identifiers*/
int _valid;
int int \u1D00num = 1;

/*
Example of an invalid identifiers */
int 1a = 0 ;

The C standard does not define a limit on the length of an identifier , whereas some implementations might set a limit . As such the C standard impose a limit , on the minimum length , that implementations must support .

The minimum number of characters that must be supported by an implementation , for external linkage identifiers , such as global variables or global functions , is at least 31 characters , as for those with internal linkage , so non global variables or functions , is 63 characters .

Name spaces of identifiers

Identifiers can be in one of these four name spaces :

  • The tag of a struct , union and enum , form one namespace .
  • Each struct , and union , form a namespace for its members .
  • Labels , have their own namespace .
  • All other identifiers , belong to the same namespace , and they are called ordinary identifiers .

We cannot have the same identifiers defined , in the same namespace , unless , they belong to different scope .

struct x{int x;};
enum y{x};
int z;
int func(int z );
/*
The tag x for the struct x , and the 
  tag y for the enum y , belong to 
  the same namespace . 
Each struct and union , form
  a namespace for its members . 
  As such int x; belong to the 
    namespace of the struct x .
Labels form their own namespace .
All others identifiers belong to the 
  same name space . As such , x , z ,
  func , and z , belong to the same 
  namespace , but they might have 
  different scope . Scope is visibility .*/

Scope of an identifier

The scope of an identifier is the visibility of this identifier . There are four scopes , that identifiers can belong to :

The function prototype scope , is for the parameters defined inside each function prototype . It is not necessary to provide an identifier , for parameters defined in a function prototype .

int x ; 
int sum(int x , int y );/*
x , and x belong to the same name 
  space , the ordinary name space , 
  but they have different scopes .
In the sum function prototype declaration , 
  we have two parameters , which have
  the sum function prototype scope ,
  they are : x and y .*/

Labels declared inside functions , have function scope . Each function , has its own function scope .

Block scope , is defined by curly brackets , it starts with { and end with } . Function parameters , belong to the block scope , of the function definition .

#include<stdio.h>

void dimension(int x , int y ){
  for( int i = 0 ; i< 10 ; i++){
    printf("%d",i);}
  printf("\n");
  printf("%d,%d\n", x , y);}/*
x , y , belong to the block scope ,
  following the parameter list ,
  of the dimension function , 
  and are only visible inside this 
  block scope .
i , belong to the block scope , 
  following the for loop clauses , 
  and is only visible inside this 
block scope.*/

int main(void){
  dimension(0,1);}/*
Output :
0123456789
0,1*/

File scope , is everything else , so anything which is not defined in a block scope , function scope , or function prototype scope. File scope is the global scope .

int x = 1 ;/*
x belong to the file scope .*/

An identifier is visible , from the moment it is declared , till the end of its scope . The end of a file scope , is the translation unit . Nested blocks , hide the visibility of identifiers , defined in the outer block .

#include<stdio.h>

int a =	-1;

void options(int a){
  printf("a : %d\n",a);
  if(a == 0){
    int	a = 1 ;	
    printf("a : %d\n",a);}
  printf("a : %d\n",a);}

int main(void){
  printf("a : %d\n",a);
  options(0);
  int z	= -1 , t = z;
  printf("z : %d , t : %d\n", z , t);}/*
Output :
a : -1
a : 0
a : 1
a : 0
z : -1 , t : -1