CSM210: Systems Programming in C. Practical Session 1

Solution to strncpy

This is just one solution to the problem. There are many others which are just as correct.
However, it's important to note that this solution is not as portable as other solutions, because in this solution, strncpy expects the first string to be pointed at in a particular way.


/* strncpy(s, t, n) copy n chars from s to t. */ 

#include 
int strncpy(char **,  char *, int);
main(int argc, char *argv[]) {

 char copy[10]; /* declare array copy to hold the first n chars of s */

/* Preform error checking */
 if (argc < 2 || argc > 2)
 { printf("Errors in input\n");
 return 0; /* if there are an invalid number of parameters, terminate */
 }

 strncpy(++argv, copy, 3)); 
 printf("copy is %s\n", copy);
}

int strncpy(char **string, char *copy, int n) { 

 /*we're traversing each array backwards */
 
 *(copy + n ) = '\0';

/* loop through the arrays */
 while (--n >=0) {
	*(copy + n) = *(*string + n);
 }

 return 1;
}


Declaring strncpy

strncpy returns an integer, so that it can be used in conditional expressions.

The function takes three arguments. The third parameter is an integer which will be used to determine how many characters to copy from the first to the second array. The second paramater is defined as a pointer to char, the location of the destination array. The first pointer is defined as a pointer to a pointer to char, which is the location of the source string.

It's important to remember that the source and destination arrays are quite different in structure. The destination array is a straight-forward character array, and each character in the array is immediately accessible as an offset from the beginning of the array. However, the first pointer is defined as a pointer to a pointer to char, because we are simply going to pass the pointer to argv which is an array of pointers, where each pointer points to the beginning of a character array holding a command line argument.


Definition of main()

main takes two arguments, an integer argument count, and a pointer to argv - an array of pointers, where each pointer points to the beginning of a character array containing a command line argument. Remember that the first pointer in argv points to an array containing the program name, and argv[argc] points to an array containing the null character \0.


The copy character array

copy is a character array of size 10 which will contain the first n chars of the string passed down from the command line.

C will reserve space for 10 characters. However, because the array is being updated through pointer arithmetic, rather than through array subscripts, C cannot check that a memory address being updated falls within the bounds of the copy array.

In the strncpy() function,we will dyamically use as much space in memory as we need to hold the first characters of the source string, without checking if the bounds of the copy array have been exceeded. Although the function "works", i.e., if we inspect the copy array by printing it, it contains what we expect, there is no guarantee that C will not use the memory addresses just outside the bounds of the array.

Try declaring copy of size 1 and having it store the first 4 characters of the source string to see what happens.




Error checking

Ensures that the program is called with only two parameters, where the first parameter is the program name and the second is the string to be copied.

Calling strncpy()

See the declaration of strncpy().

The first parameter to strncpy is a pointer to a pointer to char. argv is such a data type, however if we were to pass argv, then we would send the pointer to the beginning of the array. This would point to the program name, rather than the string we want to process, so prior to passing down the pointer we increment it so that it points to the second array element.


Terminating the destination string

In order for a character array to be properly handled as a string by C functions (such as printf()), these arrays must contain the null character \0 in the array location immediately following the last printable character in the string. Remember that we are populating the array in reverse order. The appropriate array element is set to null using the instruction *(copy + n ) = '\0'. copy is a pointer to char. n is the number of characters to copy from the source to the destination array. Arrays count from 0, so the nth array element is just beyond the last printable character of the new string. copy + n returns the address in memory of the nth array element of copy (copy itself points to the first array element). Once we have this address we dereference it (using a single *) to access the contents of that memory address. As the deferencing occurs on the left-hand-side of an assignment, we update the contents of the memory address corresponding to the nth array element of copy.

Traversing the arrays in reverse

Rather than declaring another variable to control a forward loop through the arrays, we loop on n to traverse the arrays backwards, starting from the correct array element (which is why n is decremented prior to entering the loop). Adding an offset to a pointer always returns the correct memory address given the data type of the data the pointer is pointing to.

Accessing the correct character in the input string

To access the correct character in the input string, the expression *(*string + n) is used. If you have not already seen the declaration of the strncpy() prototype and the way strncpy() is called, now's a good time to do so.

We will take the expression apart to understand what's happening. Remember that the first argument to strncpy() is declared as char **string. In other words, string is a pointer to a pointer to char. From the way strncpy() is called, we know that the first argument is the location in memory of the second element of the argv array, i.e., the array element containing the pointer to the character array containing the input string. *string contains the memory address of start of the input string. *string + n evaluates the memory address of the (nth + 1) character in the input string (because arrays count from 0). So *(*string + n) accesses the contents of that memory location, returning the data contained in the nth array element of the input string.