Footnotes: C language


Boolean variables and boolean functions

A variable or function is boolean if it admits only two values:  0 and 1.  These values stand for false and true respectively.

(By the way, see the stdbool.h library interface. It defines the type bool and the constants true and false.)


Value of an expression in C

Every expression in C has a value. For example, if the value of x is 20 then the value of the expression  3 * x + 4  is 64.  Under the same conditions, the value of the expression

y = 3 * x + 4

(the y = is part of the expression) is 64.  The evaluation of this expression has the side effect of assigning the value of the subexpression 3 * x + 4 to the variable y.  Hence, the value of y becomes 64.

Another example:  if x is 20 then the value of the expression  x = x + 1  is  21.  The evaluation of this expression has the side effect of incrementing the value of x.

Here is a less obvious example, very common in C:  if x is 20 then the value of the expression

x++

is  20.  A subtly different example:  if x is 20 then the value of the expression

++x

is  21.  Em both cases, the side effect of the evaluation of the expression is the incrementation of the value of x: the value of x becomes 21.

Something analogous goes for expressions of the form  x--  and  --x.

(See the answer to the question What is the difference between I++ and I = I+1 in C language? in Quora.)

One more example: The value of the expression  c = (a = 1) + (b = 2)  is  3.  The evaluation of the expression has the side effect of assigninig values 1, 2, and 3 to the variables a, b, and c, respectively.

Last example: In the piece of code below, the value of the expression  c = i  is  255  (not −1, as you were perhaps expecting):

unsigned char c;
int i;
i = -1;
c = i;


Computing the value of a boolean expression

Every boolean (i.e., logic) expression in C has value 0 or 1. For example, the value of the expression

   tt == 32

is 1 if the value of tt is 32 and is 0 otherwise.  Similarly, the value of the expression

   rand () <= RAND_MAX / 2

is 1 if the rand function returns a number smaller than or equal to RAND_MAX / 2 and is 0 if the function returns a number greater than RAND_MAX / 2.

The order of terms in a boolean expression.  The value of every boolean expression is computed from left to right. If the value of the expression becomes well-defined before the expression has been completely examined, the calculation is interrupted. This rule — known as minimal evaluation or short-circuit evaluation — avoids the calculation of subexpressions whose value may be undefined.

In the example below, the two expressions seem equivalent, but the first one is correct while the second may be wrong if the value of v[n] is not defined.

   if (j < n && v[j] < x) ...

   if (v[j] < x && j < n) ...


Integer division

If n is a positive number of type int, the value of the expression  n/2  is  n/2⌋ ,  that is, the floor of the quotient of n by 2.  For example: if n is 9 then n/2 is 4.

If n is a negative number of type int, the value of the expression  n/2  is  −⌊−n/2⌋ .  (Strange, but this is how it works.)  For example: if n is −9 then n/2 is −4 rather than −5.

More generally, if n and m are of type int and have the same sign, then the value of the expression  n/m  is  n/m.  If n and m have opposite signs then the value of the expression  n/m  is  −⌊−n/m.


Casting

An (int)  before an arithmetic expression is a cast.  If x is a variable of type double, the value of the expression

   (int) (x/2)

is the integer part of x/2.  Hence, if x is 9.0 then the value of the expression is 4 (not 4.5).

Another example:  if n is a variable of type int then the expression

   (double) n / 2

means the same as

   ((double) n) / 2

and transforms the value of n into a double before the division by 2.  If the value of n is 9 then the value of the expression will be 4.5 (not 4).

One more example: if h is a variable of type int having value −1 then the value of the expression

   (unsigned) h

is UINT_MAX, that is, 4294967295.

Casting is automatic in situations like

char *ptr;
ptr = malloc (1);

and therefore there is no need to write

ptr = (char *) malloc (1);


C constants

In the following code fragment,  a  is a variable,  999  is an integer constant,  and  'a'  is a character constant (also known as a literal):

   a = 999;
   c = 'a';

Similarly, the expression  "EXAMPLE"  in the following code fragment is a string constant (also known a literal):

   strcpy (s, "EXAMPLE");

The first argument of the functions printf and scanf is usually a string constant:

scanf ("%d", &n);
printf ("The value of n is %d", n);


The syntax of typedef

The syntax of the typedef operator is simple:  first, write the declaration of a variable of some type;  then, write typedef before the declaration.  For example,

   int *ptri;

declares a variable ptri (of the type pointer-to-integer).  Now write typedef before the declaration:

   typedef int *ptri;

This makes ptri into the name of a new type (identical to the type pointer-to-integer).  This type can be used to declare new pointers-to-integer:

   ptri p, q;

Another example:  The following declaration creates a variable pnt (a pair of integers):

   struct {
      int x;
      int y;
   } pnt;

Now write typedef before the declarationx:

   typedef struct {
      int x;
      int y;
   } pnt;

This makes pnt into the name of a new type (a pair of integers).  This type can be used to declare new pairs-of-integers:

   pnt a, b;

(By the way, see typedef versus #define in C in GeeksforGeeks.)


Functions in C and in math

The word function has somewhat different meanings in computing and in mathematics.  In mathematics, a function receives arguments and returns a value.  In computing, besides receiving arguments (= data) and returning a value (= result), a function may also produce side effects (such as changing the values of the arguments, or printing something).


Call by value

In C, when a function is called, all the arguments are passed to the function in value (rather than by reference).  Hence, the function receives the values of variables, not the variables themselves. (We could say, informally, that the function receives copies of the variables.)  This way of passing arguments to a function is known as call by value.

This holds, in particular, for arguments that are arrays.  An array in C is represented by the address of its first element. Therefore, when an array is passed to a function, the function receives this address (rather than some pointer that stores this address).


Global variables

A variable is global if it is defined outside all the functions that constitute the program. Global variables are accessible from any point in the program (even from others modules of the program).  Example:

int glob; // global variable 

int main (void) {
   glob = 0; 
   func ();
   printf ("%d", glob);
   return EXIT_SUCCESS;
}

void func (void) {
   glob += 1;
}

Do not use global variables indiscriminately!  Use them only when they are really needed!


The static declaration

The keyword static before the declaration of a global variable makes the variable private to the module in which it is being declared. In other words, a variable is invisible to other modules of the program.

The keyword static has the same effect on the declarations of functions: the function becomes private to the module and invisible to other modules.

For example, in a module that implements the Mergesort algorithm, the auxiliary function merge cannot be seen outside the module:

static 
void merge (int p, int q, int r, int v[]) {
   . . . 
}

void mergesort (int p, int r, int v[]) {
   if (p < r-1) {
      int q = (p + r);
      mergesort (p, q, v);
      mergesort (q, r, v);
      merge (p, q, r, v);
   }
}

(See static functions and static variables in GeeksforGeeks.)


What does the main function return?

Every C program is a set of functions, one of them being main.  The execution of the program consists of the execution of main (that typically calls other functions of the set). The main function returns an integer to notify the operating system that the execution of the program has ended.  For example,

int main (void) {
   ...
   return 0;
}

or

int main (int argc, char **argv)
   ...
   return 0;
}

The function returns 0 in case the program ended normally and returns a nonzero in case the program ended in some exceptional way.  (See also the exit function.)  We use the constants EXIT_SUCCESS (valued 0) and EXIT_FAILURE (valued 1) as the return values of main.

It is wrong to define the function main to be of type void:

void main (...) {
   ...
}

(See the answer of Terry Lambert to the question Why do most of the people use int as return type for main function and return value as 0 even when there is no use of returning it? on Quora.  See also Is it fine to write “void main()” or “main()” in C/C++? in GeeksforGeeks.)


The exit function

The function exit in the stdlib library interrupts the execution of the program and closes all the files that the program may have opened.  If the argument of the function is 0, the operating system is notified that the program ended successfully; otherwise, the operating system is notified that the program ended in some exceptional manner.

The argument of the function is typically the constant EXIT_FAILURE (equal to 1), or the constant EXIT_SUCCESS (equal to 0).

By the way, the instruction  return XXX;  at the end of the main function is equivalent to  exit (XXX);.


Layout of pointer declarations

The layout of the definition of a pointer is notoriously uncomfortable.  Conceptually, a pointer-to-int is a new data type, which suggests that we should write the * glued to the int:

int* p;

From the technical point of view, however, the * modifies the new variable rather than the int, and this suggests that the * should be glued to the p:

int *p;

The C compiler accepts either of the forms. It also accepts

int * p;

(See the question In C/C++, why do people declare pointers as "int *x" instead of "int* x"? on Quora.)


Dangling pointers

It is a good idea to avoid leaving dangling pointers in your code. Such pointers can hinder the debugging of the program and can be exploited by hackers to attack your machine. Hence, assign NULL to pointers that have been freed:

free (ptr);
ptr = NULL;

Assigning a value to a variable that will not be used is decidedly inelegant. Unfortunately, there is no elegant way to deal with hackers…

For similar reasons, it is recommended to assign some value to a pointer as soon as it is created. For example, write

int *p = NULL;

instead of simply

int *p;

The present site ignores these safety recommendations to avoid tiring the reader with repetitive details.


Newline at the end of text files

It is a good hygienic habit to write a \n character at the end of every text file.  It is also good hygiene to delete the spaces at the end of lines, that is, to avoid having a  \   preceding a \n.


Is C still important?