C programming language - ch2


Table of Contents

1 Compiling the examples

Compile with the standard ./configure ; make

Additionally add the following make options to turn off optimizations and enable debugging symbols


make CXXFLAGS='-g3 -Wall -O0 '  CFLAGS='-g3 -Wall -O0 '

when building C samples, you can also create a separate build directory

mkdir build
cd build

2 Examples

2.1 constants.c

download => constants.c

#include <stdio.h>
#include <stdlib.h>

#define  SIGNED_BITS( x )  ( sizeof( x )*8 -1 )
#define  UNSIGNED_BITS( x )  ( sizeof( x )*8  )
#define max( a, b)   ( (a) > (b) ) ? (a) : (b)

#define   SIGNED_RANGE_OF(v)  power(2, SIGNED_BITS( v ) )
#define   UNSIGNED_RANGE_OF(v)  power(2, UNSIGNED_BITS( v ) )

/* a long constant is written with a terminal l(ell) or L  */
#define MY_LC  123456789L

/* Unsigned constants are written with a terminal u or U, and the suffix ul or UL indicates unsigned long */
#define MY_UL  1234834UL

/* Floating-point constants contain a decimal point (123.4) or an exponent (1e-2) or both; */
/* their type is double, unless suffixed. The suffixes f or F indicate a float constant;
l or L indicate a long double. */
#define MY_LD  1234.56L

/* unsigned long constant with value 15 decimal  */
#define OXFUL   0XFUL

/* Constant characters  */
#define VTAB '\013'   /*  ASCII vertical tab  (decimal 11) */
#define BELL '\007'   /*   ASCII bell character  */

#define VTAB2 '\xb'  /* ASCII vertical tab  */
#define BELL2 '\x7'  /*   ASCII bell character  */

/* The character constant '\0' represents the character with value zero, the  null character.
the numeric value is just 0  */

/* A constant expression is an expression that involves only constants. Such
   expressions may be evaluated at during compilation rather than run-time, and
   accordingly may be used in any place that a constant can occur, as in
#define MAXLINE 1000
char line[MAXLINE+1];

#define LEAP 1 /* in leap years  */
int days[31+28+LEAP+31+30+31+30+31+31+30+31+30+31];

/* A string constant, or string literal, is a sequence of zero or more
   characters surrounded by double quotes, as in  */
char astring[] = "I am a string";

/* String constants can be concatenated at compile time  */

char conc1[] = "hello, "   "world\n";
/* is equivalent to  */
char conc2[] = "hello, world\n";

/* Technically, a string constant is an array of characters. The internal representation of a string has a null character '\0' at the end, so the physical storage required is one more than the number of characters written between the quotes. This representation means that there is no limit to how long a string can be, but programs must scan a a string completely to determine its length.  The standard library function strlen(s) returns the length of its charater string argument s, excluding the terminal '\0'.  */

/* strlen: return length of s  */
int mystrlen(char s[]);

/* 'x' is not the same as "x".  The former is an integer,  the later is an array with char x and \0 */

/* The enumeration constant is a list of constant integer values, as in  */
enum myboolean { NO, YES };
/* The first name in an enum has value 0, the next 1, and so on, unless explicit values are specified.
   Names in different enumerations must be distinct. Values need not be distinct in the same enumeration

enum escapes { BELL3 = '\a', BACKSPACE = '\b', TAB = '\t',
               NEWLINE = '\n', VTAB3 = '\v', RETURN = '\r' };

enum months { JAN = 1, FEB, MAR, APR, MAY, JUN,
              JUL, AUG, SEP, OCT, NOV, DEC };
                /* FEB = 2, MAR = 3, etc.   */

/* Enumerations provide a convenient way to associate constant values with names, an alternative to #define with the advantage that the values can be generated for you.  In addition, a debugger may be able to print values of enumeration variables in their symbolic form */

/* 2.4 Declarations.  All variables must be declared before use.
A variable may also be initialized in its declaration.

char esc = '\\';
int limit = MAXLINE+1;

/* If the variable in question is not automatic, the initialization is done once only, conceptionally before the program starts executing, and the initializer must be a constant expression. An explicitly initialized automatic variable is initialized each time the function or block it is i is entered; the initilizer may be any expression. External and static vairables are initialized to zero by default. Automatic variables for which is no explicit initializer have undefined (i.e., garbage) values.

/* The qualifier const can be applied to the declaration of any variable to specify that its value will not be changed. For an array, the const qualifier says that the elements will not be altered.  */

const double e = 2.71828182845905;
const char msg[] = "warning: ";

/* The const declaration can also be used with array arguments, to indicate that the function does not change that aray:  */
int strlen3(const char[]);

/* The result is implementation-defined if an attempt is made to change a const  */

/* 2.5 Aritmetic operators  */

int is_leap_year( int year );

/* 2.6 Relational and logical operators.  Relational operators have lower precedence than arithmetic operators
so an expression like i < lim-1 is taken as i < (lim-1), as would be expected.  */

/* The precedence of && is higher than that of ||, and both are lower than relational and equality operators,
so the following expression need no extra parantheses:

i < lim-1 && (c=getchar()) != '\n' && c != EOF

but since the precedence of != is higher than assignment, parentheses are needed in

(c=getchar()) != '\n'

By definition, the numeric value of a relational or logical expression is 1 if the relation is true,
and 0 if the relation is false.

The unary negation operator ! converts a non-zero operand into 0, and a zero operand in 1.
A common use of ! is in constructions like

if (!valid)


/* 2.7 Type conversions.   When an operator has operands of different types, they are converted to a common type according to a small number of rules.  In general, the only automatic conversions are those that convert a "narrower" operand into a "wider" one without losing information, such as converting an integer into floating point in an expression like f + i.  Expressions that might lose information, like assigning a longer integer type to a shorter, or a floating-point type to an integer, may draw a warning, but they are not illegal.
A char is just a small integer, so chars may be freely used in arithmetic expressions. This permits considerable flexibility in certain kinds of character transformations. One is exemplified by this naive implementation of the function atio, which converts a string of digits into its numeric equivalent.

/* atoi: convert s to integer  */
int atoi2(char s[]);

/* lower: convert c to lower case; ASCII only  */
int lower2(int c);

/* Relational expressions like i > j and logical expresions connected by && and || are defined to have value 1 if true, and 0 if false. This the assignment

d = c >= '0' && c <= '9'

sets d to 1 if c is a digit, and 0 if not. However, functions like isdigit may return any non-zero value for true. In the rest part of if, while, for, etc, "true" just means "non-zero", so this makes no difference.

Notice that floats in an expression are not automatically converted to double; this is a change from the original definition. In general, mathematical functions like those in <math.h> will use double precision. The main reason for using float is to save storage in large arrays, or less, often, to save time on machines where double-precision airthmetic is particularly expensive.


int main()

    long lc = 123456789L;

    printf("unsigned char range \n"  );
    printf ("%s\n", conc1);
    printf ("%s\n", conc2);
    printf ("length= %d\n", mystrlen(conc1) );

    printf("Leap Years:\n");
    int i;
    for ( i = 1972; i < 2020; i++ ) {
        if (is_leap_year( i ) ) {
            printf("%d\n", i);

    printf("TEST ATOI: %d \n", atoi2("3456"));
    printf ("TEST LOWER: %c \n", lower2('A') );

    return 0;

int mystrlen(char s[])
    int i = 0;
    while (s[i] != '\0' )
    return i;

/* Returns 1 if YEAR is leap,  else  return 0
A year is leap if it is divisible by 4 but not by 100, except that years divisible by 400 are leap years.
the % operator can not be applied to a float or double.
int is_leap_year( int year )
    return ( (year % 4 == 0 && year % 100 !=0) || year % 400 == 0);

/* atoi: convert s to integer  */
int atoi2(char s[])
    int i, n;
    n = 0;
    /* the test in the for can be replaced with  isdigit(s[i])  from <ctype.h> header  */
    for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
        n = 10 * n + (s[i] - '0');
    return n;

/* lower: convert c to lower case; ASCII only.    The header <ctype.h> provides better alternative tolower  */
int lower2(int c)
    if (c >= 'A' && c <= 'Z')
        return c + 'a' - 'A';
        return c;


2.2 ch2_9.c

#include <stdio.h>

/* strcat2: concatenate t to end of s; s must be big enough  */
void strcat2(char s[], char t[]);

/* getbits: get n bits from position p  */
unsigned getbits(unsigned x, int p, int n );

/* bitcount: count 1 bits in x  */
int bitcount(unsigned x);

int main()
    char mystring[] = "this is string one";
    char mystring2[] = "my string two";
    char result[200] = "";

    int x, y;

    strcat2( result, mystring );
    strcat2( result, mystring2 );

    printf ("%s\n", result);

    x = ~077;
    printf ("%d\n", x );

    x = ~0;
    printf ("%d\n", x );

    x = 255;
    y = getbits(x, 4, 3);

    printf ("getbits=%d\n", y);

    y = bitcount( x );
    printf ("bitcounts=%d\n", y);


unsigned getbits(unsigned x, int p, int n )
    return (x >> (p+1-n)) & ~(~0 << n);

/* strcat2: concatenate t to end of s; s must be big enough  */
void strcat2(char s[], char t[])
    int i, j;
    i = j = 0;

    while (s[i] != '\0')  /* find end of s  */
    while ((s[i++] = t[j++]) != '\0')  /* copy t  */


/* bitcount: count 1 bits in x  */
int bitcount(unsigned x)
    /* declaring the argument x to be an unsigned ensures that when it is right shifted,
     vacated bits will be filled with zeros, not sign bits, regardless of the machine the program is run on. */

    int b;
    for (b = 0; x!=0; x >>= 1)
        if (x & 01)
    return b;

2.3 exercise2_1.c

/* Exercise 2.1  Write a program to determine the ranges of char, short, int, and long variables,
both signed and unsiged, by printing appropirate values from standard headers and by direct computation.

#include <limits.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>

#define  SIGNED_BITS( x )  ( sizeof( x )*8 -1 )
#define  UNSIGNED_BITS( x )  ( sizeof( x )*8  )
#define max( a, b)   ( (a) > (b) ) ? (a) : (b)

#define   SIGNED_RANGE_OF(v)  power(2, SIGNED_BITS( v ) )
#define   UNSIGNED_RANGE_OF(v)  power(2, UNSIGNED_BITS( v ) )

long double power(int x, int n);

int main()
    int i;
    long l;
    char c;
    float f;
    double d;
    long double ld;

    printf("int size = %ld  bytes\n", sizeof( i ) );
    printf("long size = %ld bytes\n", sizeof( l ) );
    printf("char size = %ld bytes\n", sizeof( c ) );
    printf("float size = %ld bytes\n", sizeof( f ) );
    printf("double size = %ld bytes\n", sizeof( d ) );
    printf("long double size = %ld bytes\n", sizeof( ld ) );

    printf("2^32 = %0.LF \n", power(2,32) );

    printf("unsigned int range  = [0 .. %0.LF ]\n",  UNSIGNED_RANGE_OF(i)  );
    printf("signed int range  = [- %0.LF .. %0.LF ]\n",
           SIGNED_RANGE_OF( i ),
           SIGNED_RANGE_OF( i )  );

    printf("unsigned char range = [0 .. %0.LF ] \n", UNSIGNED_RANGE_OF( c )  );

    return 0;

/* raises X to N, for example 2 elevated to 5 = 32 */
long double power(int x, int n)
    long double result = 1;
    int i;
    for ( i = 0; i < n; i++ ) {
        result *= x;
    return result;

Author: Sebastian Emilio Narvaez

Created: 2019-05-22 Wed 17:54

Emacs 25.2.2 (Org mode 8.2.10)