0

Writing Generic (Reusable) Code

In C programming language, void pointers and function pointers are used to write generic, reusable code.

These two types of pointers in combination allow to design and implement an algorithm that is independent of the data being manipulated.

For more information about this generic code concept, read the Notes on Programming in C by Rob Pike.

1

Function Pointers Usage

The statement below is from the last post:

void (*func_ptr)(emp *e);

The above variable can be assigned as follows:

func_ptr = read_emp; // Note that no & is required before read_emp

The following two statements are different from each other:

func_ptr = read_emp; // assign the function address of read_emp to a function to pointer variable func_ptr
func_ptr = read_emp();	// runs the function named read_emp() and saves function results to the variable func_ptr

Here is the sample code for the function pointers:

/*
 *
 * funcptrs.c
 *
 * Program to demonstrate the use of function pointers
 *
 * by Mark Virtue, 2001.
 *
 */

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

static void goodbye() {
	while (getchar() != 'n') {}
	printf("nPress ENTER to exit: ");
	fflush(stdin);
	getchar();
}

static int add(int a, int b) {
	return a + b;
}

static int subtract(int a, int b) {
	return a - b;
}

main() {
	int x, y;
	int result;
	int (*func_ptr)(int, int);

	atexit(goodbye);

	printf("Please enter the first number: ");
	scanf("%d", &x);
	printf("Please enter the second number: ");
	scanf("%d", &y);

	while (getchar() != 'n') {}

	printf("Would you like to add or subtract (a/s)? ");
	fflush(stdin);

	if (getchar() == 'a') {
		func_ptr = add;
	}
	else {
		func_ptr = subtract;
	}

	result = func_ptr(x, y);

	printf("The result is %dn", result);

	return 0;
}

Note in the above code, the add and subtract functions need to have the same structure in order for use with the function pointer.

Here is another sample code which uses a function in the standard C library called qsort():

/*
 * qsort.c
 *
 * Program to demonstrate the use of function pointers
 * This program uses the library function qsort():
 *
 * void qsort(void *array, int count, int size, int (*comp_func)(const void *, const void *))
 *
 * by Mark Virtue, 2001.
 */

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

struct employee {

  char name[31];

  char address[101];

  int age;

  float salary;

};

typedef struct employee emp;

int compare_ages(const void *v1, const void *v2) {

    // Create two temp pointers for convenience, to ensure that the void pointers are interpreted correctly

  const emp     *e1 = v1;

  const emp     *e2 = v2;

  return e1->age - e2->age;

    // Note: could have been written: return ((emp *)v1)->age - ((emp *)v2)->age;

}

int compare_names(const void *v1, const void *v2) {

    //Create two temp pointers for convenience, to ensure that the void pointers are interpreted correctly
  const emp *e1 = v1;

  const emp *e2 = v2;

  return strcmp(e1->name, e2->name);

}

static void goodbye() {

  while (getchar() != 'n') {}
  printf("nPress ENTER to exit: ");

  fflush(stdin);

  getchar();

}

main() {

  emp emps[] = {

    {"Bob",  "123 Hope St",   45, 45000.00},

    {"Tony", "124 Hope St",   99, 46000.00},

    {"Mary", "76 Banana Ave", 12, 20000.00},

    {"Sue",  "1000 Road Rd",  45, 90000.00},

    {"Fred", "32 Nowhere Rd", 51, 49000.00}

};

  int i;

  atexit(goodbye);

  qsort(emps, 5, sizeof emps[0], compare_ages);

  for (i = 0; i < 5; i++) {

    printf("%-10s is aged %dn", emps[i].name, emps[i].age);
  }

  qsort(emps, 5, sizeof emps[0], compare_names);

  for (i = 0; i < 5; i++) {

    printf("%-10sn", emps[i].name);

  }
}
0

Function Pointers

A function to pointer is a pointer variable that contains the address of a function. The return type and parameter types of the function must be declared in the declaration of a function pointer.

Here is an example:

	// The following two functions are similar
void read_emp(emp *e);
void print_emp(emp *e);

	// Below is the declaration of the function to pointer which is point to either of these functions
void (*func_ptr)(emp *e);

Note that the first two lines of above code are function prototypes. The last line is a variable declaration.

The statements below are the comparison between a function to pointer variable and a function prototype:

void (*func_ptr)(emp *e);	// function to pointer variable
void *func_ptr(emp *e);	// function prototype

Next post will be talking about the use of function pointers.

0

Pointers to Pointers

It’s possible to create a pointer that holds the address of another pointer, called “a pointer to a pointer”.

Here is an example of a pointer to a pointer:

char **ptr;		// Declaration of a pointer to a pointer
char *ptr;	// Declaration of a normal pointer
char c;	// Normal variable declaration

	// Below is the usage of a pointer to a pointer
char c = 'a';
char *p_c = &c;
char **p_p_c = &p_c;

The usage of pointers to pointers is mostly on a function that takes a pointer parameter which needs to use that pointer to point to something else.

It’s also useful for working with multi-dimensional arrays. For example,

char *argv[];	// is the same as
char **argv;

It can also form an indefinite pointer to pointer, for example, a pointer to a pointer to a pointer to a pointer… However, this indefinite form is rarely used.

The following is the same code for pointers to pointers:

/*
 *
 * ptr2ptr.c
 *
 * Program to demonstrate the use of pointers to pointers
 *
 * by Mark Virtue, 2001.
 *
 */

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

struct employee {
	char name[31];
	char address[101];
	int age;
	float salary;
};

typedef struct employee emp;

/*
 *
 * Function to find the oldest of 2 emp structures
 * the pointer to a pointer to an emp "e_ptr" has
 * its contents (a pointer to an emp) assigned to the appropriate one
 *
 */
static void oldest(emp emps[], emp **e_ptr) {
	if (emps[0].age > emps[1].age) {
		*e_ptr = emps + 0;	// Note: it's *e_ptr
	}
	else {
		*e_ptr = &(emps[1]);
	}
}

static void goodbye() {
	printf("nPress ENTER to exit: ");
	fflush(stdin);
	getchar();
}

main() {
	emp emps[2] = {
		{"Sob", "123 Hope St", 45, 4500.00},
		{"Fred", "32 Nowhere Rd", 51, 49000.00}
	};
	emp *elder;

	atexit(goodbye);

	oldest(emps, &elder);

	printf("The oldest employee is aged %dn", elder->age);
}
0

The C Pointer Arithmetic

An integer can be added to a pointer to get another address. For example,

int array[100];
int *ptr1 = array;
int *ptr2;

ptr2 = ptr1 + 5;

The integer 5 is depend on the variable type of ptr1. According to the above statements, ptr1 is an integer, the byte size of an integer is 4. Therefore 5 time 4 equals 20. The address of ptr1 plus 20 is the address for ptr2.

As the last post stated, a pointer is the address of first element of an array. This means that ptr1 is the address of first element of array and ptr2 is the 6th element of array:

ptr2 = &(ptr1[5]);	// this is the same as below
ptr2 = ptr1 + 5;

Here are the sample statements based on these two similar statements:

int x[10]; // or
int *x;

	// The following two statements are completely interchangeable
x[10];
*(x + 10);

	// or (these two are also interchangeable)
&(x[10]);
x + 10;

Note that the subtraction of an integer from a pointer is exactly the same as the addition which we talked about above.

Here is the sample code for the pointer arithmetic:

/*
 *
 * arithmetic.c
 *
 * Program to demonstrate the use of pointer arithmetic
 *
 * by Mark Virtue, 2001.
 *
 */

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

static void goodbye() {
	while (getchar() != 'n') {}
	printf("nPress ENTER to exit: ");
	fflush(stdin);
	getchar();
}

main() {
	int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	int *ptr = array;
	int element;

	atexit(goodbye);

	printf("Please enter which element you would like: ");
	scanf("%d", &element);

		// The output for the following statements are the same, all print out the value for the specified element of the array
	printf("narray[%d] is %dn", element, array[element]);
	printf("*(array + %d) is %dn", element, *(array + element));
	printf("*ptr[%d] is %dn", element, ptr[element]);
	printf("*(ptr + %d) is %dn", element, *(ptr + element));

		// The following statements print out the memory address for the specified element of the array
	printf("n&(ptr[%d]) is %dn", element, &(ptr[element]));
	printf("ptr + %d is %dn", element, ptr + element);

		// This prints out the address difference between two elements of the array through subtraction
	printf("nThe ADDRESS difference between element 9 and element 1 is %dn", &(ptr[9]) - &(ptr[1]));
}
1

The Similarities and Differences between Pointers and Arrays

There are small number of similarities between pointers and arrays:

  • The value of an array is the address of its first element (Note that an address is a pointer). For example,
    int array[40];
    int *ptr;
    
    ptr = array;

Pointers and arrays are almost the same. However, there are four small differences between them:

  1. The memory used by an array doesn’t need to be initialized, whereas a pointer needs to be initialized to point to a valid piece of memory.
  2. sizeof ptr always equals 4 which is an integer (on a 32-bit PC). The size of an array is equal to the number of elements in the array multiply by the size of each element.
  3. The array variables cannot be located on the left side of the assignment statements (such as an equal sign), because the array value (address) cannot be changed.
  4. If arr is an array, then the following statement is true:
    &arr = arr;

Below is the sample code that shows the similarities and differences between arrays and pointers:

	// below are some declarations
void func(int *x, int y[]);
int a[40];
int *p;

	// all the following statements which are based on the above declarations are valid
p = a;	// a = p is invalid
p[0] = a[0];
*p = *a;
*a = *p;
func(a, p);
func(p, a);
0

The C NULL Pointer

NULL is a #define statement that is used by functions in the standard C library (stdlib.h) such as malloc and fopen. It’s used to define an invalid pointer or an error condition.

Here is NULL definition statement inside stdlib.h:

#define NULL ((void *)0)

The above code means that NULL is the zero memory address.

Note that unlike PHP, the following is bad practice in C language:

if (!malloc(10))

You need to write the above statement as follows:

if (malloc(10) == NULL)

Below is the sample code for the NULL pointer:

/*
 *
 * null.c
 *
 * Program to demonstrate the use of a NULL pointer
 *
 * by Mark Virtue, 2001.
 *
 */

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

static void goodbye() {
	while (getchar() != 'n') {}
	printf("nPress ENTER to exit: ");
	fflush(stdin);
	getchar();
}

/*
 *
 * memdup()
 *
 * A function that takes a pointer to any type of memory,
 * then creates a duplicate amount of memory (using malloc)
 * and copies the original memory into the new memory
 *
 */

void *memdup(void *mem, int size) {
	void *newptr;

	newptr = malloc(size);
	if (newptr == NULL) {
		return NULL;
	}
	memcpy(newptr, mem, size);
	return newptr;
}

main() {
	float float_array1[] = {1.2, 2.3, 3.4};
	int int_array1[] = {1,2,3,4,5,6};
	char char_array1[] = "Hello Worldn";
	float *float_array2;
	int *int_array2;
	char *char_array2;

	atexit(goodbye);

	float_array2 = memdup(float_array1, sizeof float_array1);
	int_array2 = memdup(int_array1, sizeof int_array1);
	char_array2 = memdup(char_array1, sizeof char_array1);

	if (float_array2 == NULL) {
		printf("The float failedn");
	}
	else {
		free(float_array2);
	}

	int_array2 == NULL ? printf("The int failedn") : free(int_array2);

	char_array2 == NULL && printf("The char failedn");

	return 0;
}
1

C void Pointers

A void pointer is a generic pointer which has no variable type. It’s primary used for declaring an “universal” pointer (“pointer to anything”).

For example in the following code, free_it function is not defined:

int *int_ptr = malloc(10 * sizeof(int));
float *float_ptr = malloc(10 * sizeof(float));
free_it(int_ptr);    // int_ptr is an integer
free_it(float_ptr);    // float_ptr is a float

Notice in the above code, although the free_it function is not defined, it can take different variable types as its parameter. It is able to define a parameter that can accept all kinds of variable types by declaring the parameter as void:

void *ptr;

Void pointers can be used by these functions: malloc, free, memcpy, and memset, etc.

The void variable type does not have size. Therefore, the following statements are undefined:

// void *ptr;
sizeof *ptr;    // undefined
ptr[0];    // undefined

To use a void pointer, assign its address to another non-void pointer before using it, for instance:

emp *el = ptr;    // or
int *el = ptr;

Below is the sample code for the void pointers:

/*
 *
 * void.c
 *
 * Program to demonstrate the use of a void pointer
 *
 * by Mark Virtue, 2001.
 *
 */

#include <stdlib.h>
#include <memory.h>

/*
 *
 * memdup()
 *
 * A function that takes a pointer to any type of memory,
 * then creates a duplicate amount of memory (using malloc)
 * and copies the original memory into the new memory
 *
 */

void *memdup (void *mem, int size) {
	void *newptr;

	newptr = malloc(size);
	memcpy(newptr, mem, size);
	return newptr;

	// could be written: return memcpy(malloc(size), mem, size);
}

main() {
	float float_array1[] = {1.2, 2.3, 3.4};
	int int_array1[] = {1, 2, 3, 4, 5, 6};
	char char_array1[] = "Hello Worldn";
	float *float_array2;
	int *int_array2;
	char *char_array2;

	float_array2 = memdup(float_array1, sizeof float_array1);
	int_array2 = memdup(int_array1, sizeof int_array1);
	char_array2 = memdup(char_array1, sizeof char_array1);

	free(float_array2);
	free(int_array2);
	free(char_array2);
}

The above code does not print out anything, just to show you how to use the void pointers.

If you have any questions about the above code, leave a comment below and I will respond as quickly as possible.

0

The C Bit-Sized Structure Fields

Fields of structures can be sized in multiples of 1 bit. For instance,

struct employee {
 char name[31];
 int married :1;
};

Note that in the above structure, :1 represents the single bit for married integer variable.

These type of fields are called small integer types.

It’s a good practice to place these bit-sized fields continuously in a structure definition for the minimal waste of space.

Here is the sample code for bit-sized structure fields rewrote from the last post:

/*
 *
 * bitfield.c
 *
 * Program to demonstrate the use of bit-sized structure fields
 *
 * by Mark Virtue, 2001.
 *
 */

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

struct employee {
 char name[31];
 char address[101];
 int age;
 float salary;
 int married :1;
 int manager :1;
 int male :1;
 int num_kids :4; // limit of 15 kids
};

typedef struct employee emp;

static read_emp(emp *e) {
 char answer;

 printf("Please enter the age: ");
 scanf("%d", &e->age);

 while (getchar() != 'n') {}

 printf("Married (Y/N)? ");
 fflush(stdin);
 answer = getchar();
 e->married = toupper(answer) == 'Y';
}

static void print_emp(emp *e) {
 printf("Age is %dn", e->age);
 puts(e->married ? "Married" : "Not married");
}

static void goodbye() {
 while (getchar() != 'n') {}
 printf("nPress ENTER to exit: ");
 fflush(stdin);
 getchar();
}

main() {
 emp e;

 read_emp(&e);
 print_emp(&e);

 fflush(stdin);
}
0

The C & and | Operators

These operators are bitwise operators, different from the logical operators (&& and ||).

The & (Bitwise and) operator can produce the following results:

  • If a bit is on in both operands, it’s on in the result
  • If a bit is off in either operand, it’s off in the result

Notice the difference between single (&, |) operator and double (&&, ||) operator. The double operator works on the entire number, whereas single operator works on each bit in the number.

The | (Bitwise or) operator is almost the same as & operator:

  • If a bit is on in either operand, it’s on in the result
  • If a bit is off in both operands, it’s off in the result

Here are some examples:

104 & 3 = 8
104 (01101000)
 13 (00001101)
  8 (00001000)
  // There is only one bit that is on on both operands, therefore the result is only on for this particular bit.

52 | 12 = 60
52 (00110100)
12 (00001100)
60 (00111100)
  // There needs to be at least one bit on on either operand in order for the result to be on, therefore the result is on for the bits which are on for either operands.

The usage of these two operator is just turning a bit on or off.

Below is the code for turning a bit on:

#define MARRIED (1 << 3)
status |= MARRIED;    // is the same as status = status | MARRIED

And turning a bit off:

status &= ~MARRIED;

For testing whether a bit is on or off:

if (status & MARRIED)
if (!(status & MARRIED))

It will return a zero (off/false) or a non-zero (on/true).

Note that these two operators have lower precedence than –, !-, etc. and the follwing wil not work:

if (status & MARRIED == 0)    // Its first step will be to process MARRIED == 0 which will always produce the unexpected answer.

Here is the sample code for the two operators:

/*
 *
 * and_or.c
 *
 * Program to demonstrate the use of bit-sized & and | operators
 *
 * by Mark Virtue, 2001.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define MARRIED (1 << 0)
#define MANAGER (1 << 1)
#define MALE (1 << 2)

struct employee {
	char name[31];
	char address[101];
	int age;
	float salary;
	int flags;
};

typedef struct employee emp;

static read_emp(emp *e) {
	char answer;

	e->flags = 0;

	printf("Please enter the age: ");
	scanf("%d", &e->age);

	while (getchar() != 'n') {}

	printf("Married (Y/N)? ");
	fflush(stdin);
	answer = getchar();
	if (toupper(answer) == 'Y') {
		e->flags |= MARRIED;
	}
	else {
		e->flags &= ~MARRIED;
	}
}

static void print_emp(emp *e) {
	printf("Age is %dn", e->age);
	puts((e->flags & MARRIED) ? "Married" : "Not married");
}

static void goodbye() {
	while (getchar() != 'n') {}
	printf("nPress ENTER to exit: ");
	fflush(stdin);
	getchar();
}

main() {
	emp e;

	read_emp(&e);
	print_emp(&e);

	fflush(stdin);
}
Pages ... 1 2 3 4 5 6 7 8 9 10 11 12