Skip to content
Physics and Astronomy
Home Our Teaching Resources C programming PHY3134 stdlib.html
Back to top

Computational Physics
The standard library

As well as the actual language itself, C also provides a standard library of functions. We won't try to give an exhaustive list, that's what K&R and Google are for, just a few examples.

The library functions are split into groups, each with its own include file.

Standard input: <stdio.h>

You've met these before, there are a few main classes:

Formatted input and output

Most of these come in three versions, eg
#include<stdio.h>
int main(void) {
  char string[256];

  printf("Hello, world\n");
  fprintf(stdout, "Hello, world\n");
  sprintf(string, "Hello, world\n");
  return 0;
}
The second call is identical to the first since stdout is one of three file descripters that is automatically opened when a program is run (the others are stdin and stderr which is used for error messages).

sprintf takes a character array (string) as its first argument.

<stdio.h> is also the place where NULL is defined, so you will need to include it on the rare occasions you want to use NULL without any IO.

Character input and output

These just read or write one or more characters. Mixing these with the formatted input and output routines can be tricky.

Character tests: <ctype.h>

Wnat to know if a particular character is a letter(or a number, or a space, or..)? <ctype.h> is your friend.

In the example below we demonstrate the use of fgets to read in a string with spaces and putchar to print a single character.

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

/*
 * Print out letters from the input string and 
 * print out all spaces as tabs
 * Note use of fgets to handle spaces
 */
int main(void) {
  char string[256], *p;

  printf("String?\n");
  fgets(string, 256, stdin);
  
  /* Get rid of the '\n' at the end */
  p = string + strlen(string) - 1;
  if (*p == '\n')
    *p = '\0'; 

  for(p = string; *p; ++p)
    if (isalpha(*p))
	putchar(*p);
    else if(isspace(*p))
      putchar('\t');

  putchar('\n');
}
Result:
./ctype
String?
abc def
abc     def

./ctype
String?
ab2cdef ;lo
abcdef  lo
Ie, all the letters have been printed out, the spaces changed to tabs and everything else ignored. Similarly, the isprint(c) function returns non-zero if c is a printable character.

String functions: <string.h>

Again, you've all used them but you should normally try to use the 'n' versions when possible:
#include <stdio.h>
#include <string.h>

#define BUFLEN 4
void killme(void) {
  char buffer[BUFLEN+1] = "";

  strcpy(buffer, "This would be very bad news indeed");
}

void dontkillme(void) {
  char buffer[BUFLEN+1] = "";

  strncpy(buffer, "This would be truncated", BUFLEN);
}

Strlen and strcmp are also often used.

A minor warning

You may have noticed that the buffer we passed to strncpy had a length one greater than it apparently needed to have and that we carefully initialised it to all zeros. This is because strncpy fails in a rather unhelpful way if the string to be copied is longer than the length provided by the third argument : it copies all N bytes leaving a string without the zero at the end. Thus, whilst it doesn't over-write the buffer it does leave an invalid string. We've got round this by making buffer one byte larger than it needs to be and ensuring that buffer[BUFLEN] is zero. Another approach would be to write a wrapper function:
#include <string.h>
char *mystrncpy(char *to, const char *from, size_t n) {
  strncpy(to, from, n);
  to[n-1] - '\0';
  return to;
}

The const qualifier

The const char *from syntax above means that we are promising that *from is a pointer to a constant string, ie one that mystrncpy won't try to change. Had we tried to write to *foo that would have been an error. Finally we note that the const could have come after the char * meaning that the variable itself is a constant, not what what it points to. Here, for example, we take constant copies of the two arguments to and from. The four commented-out statements are all errors: the first two try to change a constant variable, the second try to change a constant string.
#include <string.h>

char *mystrncpy(char *to, const char *from, size_t n) {
  char * const tocopy = to;
  const char * const fromcopy = from;

  strncpy(tocopy, from, n);
  ++to;
  // ++tocopy;
  // ++fromcopy;

  tocopy[n-1] = '\0';
  // fromcopy[n-1] = '\0';
  // from[n-1] = '\0';
  return tocopy;
}

Mathematical functions: <math.h>

You've all used them (linux users: you may need to specify -lm when you link), here's an example that answers the age-old question: "How do I find the angle whose cosine is y/sqrt(x*x+y*y) and sine is x/sqrt(x*x+y*y)?"
#include <stdio.h>
#include <math.h>

#define PI 3.14159265358979

int main(void) {
  double angle, sinbit, cosbit;

  /* NB, sinbit and cosbit don't need to be normalised */
  printf("sin and cos?\n");
  scanf("%lf %lf", &sinbit, &cosbit);
  
  printf("angle is %4f pi\n", atan2(sinbit, cosbit)/PI);
}

Sorting and <stdlib.h>

As well as malloc, etc. <stdlib.h> includes a function, qsort, to sort array elements. These can be arrays of any type (float, int, etc.) or arrays of structures.

Since this can be a little confusing, we'll provide a working example.

Sorting an array of structures poses two problems:

  • Given two structures, how does qsort know which should come first?
    Answer: we have to right a function to tell it.

  • More subtly; if we pass qsort a pointer to the first element of the array, how does it know where the second, third, etc elements are?
    Answer: we tell it.

Sorting example

NB the boxes of code in this section are a single C file that has been split into sections.

Let's imagine we have a list of foods and we want them sorted by calories, with foods with identical calories listed alphabetically. Our data structure looks like this

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

#define NAMELEN 256
typedef struct food {
  char name[NAMELEN];
  float calories;
} Food;
Now we declare the function that qsort will call whenever it needs to know which order two items should be in. The prototype is:
int calories_or_alphabetical(const void *a, const void *b);
It must return an int which is negative or positive according to whether the thing a points to should be before or after in the list than the thing b points to. If it turns out they should both occupy the same place ("second equal") it returns zero.

The arguments are both void pointers because qsort neiether knows or cares what they point to and the word const in front of them says that calories_or_alphabetical is not allowed to modify the thing they point to.

OK, let;s read in the data values.

int main(void) {
  Food *foods = NULL;
  int howmany, todo;

  do {
    printf("How many foods (>= 1)?\n");
    scanf("%d", &howmany);
  } while (howmany < 1);

  if ((foods = malloc(howmany * sizeof *foods)) == NULL) {
    fprintf(stderr, "Sorry you're too hungry for this Mac!\n");
    exit(-1);
  }

  for (todo = howmany -1; todo >= 0; --todo) {
    int readin;

    printf("Name and calories?\n");
    readin = scanf("%s %f", foods[todo].name, &foods[todo].calories);
    if (readin != 2) {
      fprintf(stderr, "Sorry, I couldn't understand that\n"
	      "Please make sure the food name has no spaces.\n");
      ++todo;
    }
  }
We use a do..while loop to check that we read in a positive number of foods and we also test to check we've read the (space-free) name and calories properly.

Now we sort the array and print out the foods in calorie/alphabetical order:

  qsort(foods, howmany, sizeof *foods, calories_or_alphabetical);

  for (todo = howmany -1; todo >= 0; --todo) 
    printf("%s %f\n", foods[todo].name, foods[todo].calories);
  return 0;
}
The first two arguments to qsort are quite simple: a pointer to the start of the array (note it must be an array not a linked list) and the number of elements to be sorted. The third argument is the answer to our second question above: it's the 'distance' (in bytes) between the Nth and (N+1)th elements of the array which tells qsort where the second, third, fourth, etc. array elements are. Finally, calories_or_alphabetical is just the name of our function. yes, you can pass the name of a function to another function.

The function itself looks like this:

/*
 * Return positive or negative or zero according to which food
 * should come first in the sorted list
 */
int calories_or_alphabetical(const void *a, const void *b) {
  const Food *fooda = a, *foodb = b;

  if ( fooda->calories != foodb->calories)
    return foodb->calories - fooda->calories;
  return strcmp(foodb->name, fooda->name);
}
Notice that the very first thing the function does is to turn the two void * pointers into something it can use (which must be the same type as the array qsort was called with of course). Then it checks to see if one food has more calories than the other. If they have the same calories it checks for which comes first in the alphabet. Note that our function calories_or_alphabetical can return any positive or negative number it likes. All that counts is whether the value is positive, negative or zero.

The results

INPUT:

How many foods (>= 1)?
5
Name and calories?
cream 300
Name and calories?
bigmac 300
Name and calories?
kentucky 500
Name and calories?
yoghurt 100
Name and calories?
apple 100
OUTPUT:
apple 100.000000
yoghurt 100.000000
bigmac 300.000000
cream 300.000000
kentucky 500.000000

Sorting and pointers

The sort function always introduces another layer of pointing. In the above example, we have an array of structures but each argument to calories_or_alphabetical is a pointer to a structure (Food *). Had we had an array of pointers to structures each argument would be a pointer to a pointer (Food **) and the start of calories_or_alphabetical would have been:
int calories_or_alphabetical(const void *a, const void *b) {
  const Food *fooda = * (Food **) a, *foodb = * (Food **) b;
Sorting is important in its own right, it has also introduced two new concepts: passing a function name as an argument to another function so that second function can call it and using a void * pointer to pass a pointer to a structure of our own devising through a library function to another function of ours. We conclude with a small example of this:
typedef struct food {
  float val;
  char name[32];
} Food;


int alphabetical(const void *a, const void *b) {
  const Food *fooda = a, *foodb = b;

  fprintf(stderr, "%s %s\n", fooda->name, foodb->name);
  return strcmp(fooda->name, foodb->name);
}


void tryit(int fun(const void *, const void *), void *a, void *b) {
  printf("%d", fun(a, b));
}

int main(void) {
  Food a, b;

  strcpy(a.name, "Potato");
  strcpy(b.name, "Apple");

  tryit(alphabetical, &a, &b);
  return(0);
}
Notice my diagnostic inside alphabetical. This to check I've got my levels of pointers right. You would be very wise to do the same!

exit

Sometimes your program just needs to quit (for example if malloc fails, meaning you've run out of memory). Calling exit(integer_value) will do this for you, the convention is that a value of zero means success, anything alse means failure. exit is also defined in stdlib.h.
                                                                                                                                                                                                                                                                       

Validate   Link-check © Copyright & disclaimer Privacy & cookies Share
Back to top