Pointers and Arrays



  • A pointer is a variable that contains the address of a variable.


  • A typical machine has an array of consecutively numbered or addressed memory cells that may be manipulated individually or in contiguous groups.


  • One common situation is that any byte can be a char, a pair of one-byte cells can be treated as a short integer, and four adjacent bytes form a long. A pointer is a group of cells (often two or four) that can hold an address.


  • if c is a char and p is a pointer that points to it, we could represent the situation this way: p = &c;. The unary operator & gives the address of an object.


  • The above code assigns the address of c to the variable p, and p is said to ``point to" c.


  • The & operator only applies to objects in memory: variables and array elements. It cannot be applied to expressions, constants, or register variables.


  • The unary operator * is the indirection or dereferencing operator; when applied to a pointer, it accesses the object the pointer points to.


  •    int x = 1, y = 2, z[10];
       int *ip;          /* ip is a pointer to int */
       ip = &x;          /* ip now points to x */
       y = *ip;          /* y is now 1 */
       *ip = 0;          /* x is now 0 */
       ip = &z[0];       /* ip now points to z[0] */
    

  • The declaration of the pointer ip, int *ip; is intended as a mnemonic; it says that the expression *ip is an int.


  • This reasoning applies to function declarations as well. For example, double *dp, atof(char *); says that in an expression *dp and atof(s) have values of double, and that the argument of atof is a pointer to char.


  • Every pointer points to a specific data type. (There is one exception: a ``pointer to void'' is used to hold any type of pointer but cannot be dereferenced itself.



Q. Pointers and examples of Pointers


  • If ip points to the integer x, then *ip can occur in any context where x


  • *ip = *ip + 10; increments *ip by 10.


  • The unary operators * and & bind more tightly than arithmetic operators, so the assignment y = *ip + 1 takes whatever ip points at, adds 1, and assigns the result to y.


  • *ip += 1 , ++*ip and (*ip)++ increments what ip points to, as do The parentheses are necessary in this last example; without them, the expression would increment ip instead of what it points to, because unary operators like * and ++ associate right to left.


  • Since pointers are variables, they can be used without dereferencing. For example, if iq is another pointer to int, iq = ip copies the contents of ip into iq, thus making iq point to whatever ip pointed to.




Pointers and Function Arguments



  • Since C passes arguments to functions by value, there is no direct way for the called function to alter a variable in the calling function.


  • For instance, a sorting routine might exchange two out-of-order arguments with a function called swap. i.e. swap(a, b);


  • void swap(int x, int y)  
       {
           int temp;
           temp = x;
           x = y;
           y = temp;
       }

  • Because of call by value, swap can't affect the arguments a and b in the routine that called it. The function above swaps copies of a and b.


  • The way to obtain the desired effect is for the calling program to pass pointers to the values to be changed. swap(&a, &b);


  • Since the operator & produces the address of a variable, &a is a pointer to a. In swap itself, the parameters are declared as pointers, and the operands are accessed indirectly through them.


  • void swap(int *px, int *py)  /* interchange *px and *py */
       {
           int temp;
           temp = *px;
           *px = *py;
           *py = temp;
       }

  • calling program to pass pointers to the values



Pointers and Arrays



  • The declaration int a[10]; defines an array of size 10, that is, a block of 10 consecutive objects named a[0], a[1], ...,a[9].


  • The notation a[i] refers to the i-th element of the array. If pa is a pointer to an integer, declared as int *pa; then the assignment pa = &a[0]; sets pa to point to element zero of a; that is, pa contains the address of a[0].


  • Now the assignment x = *pa; will copy the contents of a[0] into x. If pa points to a particular element of an array, then by definition pa+1 points to the next element, pa+i points i elements after pa, and pa-i points i elements before. Thus, if pa points to a[0], *(pa+1) refers to the contents of a[1], pa+i is the address of a[i], and *(pa+i) is the contents of a[i].


  • Pointers and Arrays
  • The meaning of ``adding 1 to a pointer" and by extension, all pointer arithmetic, is that pa+1 points to the next object, and pa+i points to the i-th object beyond pa.


  • Since the name of an array is a synonym for the location of the initial element, the assignment pa = &a[0] can also be written as pa = a;


  • Applying the operator & to both parts of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the address of the i-th element beyond a. As the other side of this coin, if pa is a pointer, expressions might use it with a subscript; pa[i] is identical to *(pa+i). In short, an array-and-index expression is equivalent to one written as a pointer and offset.


  • There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable, so pa = a and pa++ are legal. But an array name is not a variable; constructions like a = pa and a++ are illegal.


  • If one is sure that the elements exist, it is also possible to index backwards in an array; p[-1], p[-2], and so on are syntactically legal, and refer to the elements that immediately precede p[0]. Of course, it is illegal to refer to objects that are not within the array bounds.



Q. Demonstrate Pointers and Arrays


  • When an array name is passed to a function, what is passed is the location of the initial element. Within the called function, this argument is a local variable, and so an array name parameter is a pointer, that is, a variable containing an address.


  •    /* strlen:  return length of string s */
       int strlen(char *s)
       {
           int n;
           for (n = 0; *s != '\0', s++)
               n++;
           return n;
       }
    

  • Since s is a pointer, incrementing it is perfectly legal; s++ has no effect on the character string in the function that called strlen, but merely increments strlen's private copy of the pointer. That means that calls like those given below all work.


  •    strlen("hello, world");   /* string constant */
       strlen(array);            /* char array[100]; */
       strlen(ptr);              /* char *ptr; */
    




Address Arithmetic



  • If p is a pointer to some element of an array, then p++ increments p to point to the next element, and p+=i increments it to point i elements beyond where it currently does.


  • In general a pointer can be initialized just as any other variable can, though normally the only meaningful values are zero or an expression involving the address of previously defined data of appropriate type.


  • Pointers and integers are not interchangeable. Zero is the sole exception: the constant zero may be assigned to a pointer, and a pointer may be compared with the constant zero. The symbolic constant NULL is often used in place of zero, as a mnemonic to indicate more clearly that this is a special value for a pointer.


  • Pointers may be compared under certain circumstances. If p and q point to members of the same array, then relations like ==, !=, <, >=, etc., work properly.


  • Thus p < q is true if p points to an earlier element of the array than q does. Any pointer can be meaningfully compared for equality or inequality with zero.


  • The construction p + n means the address of the n-th object beyond the one p currently points to. This is true regardless of the kind of object p points to; n is scaled according to the size of the objects p points to, which is determined by the declaration of p. If an int is four bytes, for example, the int will be scaled by four.


  • Pointer subtraction is also valid: if p and q point to elements of the same array, and pq-p+1 is the number of elements from p to q inclusive.


  • It is not legal to add two pointers, or to multiply or divide or shift or mask them, or to add float or double to them, or even, except for void *, to assign a pointer of one type to a pointer of another type without a cast.



Q. Demonstrate Pointer Arithmetic for function strlen: return length of string s


  • /* strlen:  return length of string s */
       int strlen(char *s)
       {
           char *p = s;
           while (*p != '\0')
               p++;
           return p - s;
       }
    

  • In its declaration, p is initialized to s, that is, to point to the first character of the string. In the while loop, each character in turn is examined until the '\0' at the end is seen.


  • Because p points to characters, p++ advances p to the next character each time, and p-s gives the number of characters advanced over, that is, the string length.





Character Pointers and Functions



  • A string constant, written as "I am a string" is an array of characters. In the internal representation, the array is terminated with the null character '\0' so that programs can find the end. The length in storage is thus one more than the number of characters between the double quotes.


  • If pmessage is declared as char *pmessage; then the statement pmessage = "hello world"; assigns to pmessage a pointer to the character array.


  • There is an important difference between these definitions: 
       char amessage[] = "now is the time"; /* an array */
       char *pmessage = "now is the time"; /* a pointer */

  • amessage is an array, just big enough to hold the sequence of characters and '\0' that initializes it. Individual characters within the array may be changed but amessage will always refer to the same storage. On the other hand, pmessage is a pointer, initialized to point to a string constant; the pointer may subsequently be modified to point elsewhere, but the result is undefined if you try to modify the string contents.



Q. Demonstrate character pointers and arrays


  •    /* strcpy:  copy t to s; array subscript version */
       void strcpy(char *s, char *t)
       {
           int i;
           i = 0;
           while ((s[i] = t[i]) != '\0')
               i++;
       }
    

  •    /* strcpy:  copy t to s; pointer version */
       void strcpy(char *s, char *t)
       {
           int i;
           i = 0;
           while ((*s = *t) != '\0') {
               s++;
               t++;
           }
       }
    



Multi-dimensional Arrays



  • C provides rectangular multi-dimensional arrays, although in practice they are much less used than arrays of pointers.


  • static char daytab[2][13] = {
           {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
           {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
       };
       /* day_of_year:  set day of year from month & day */
       int day_of_year(int year, int month, int day)
       {
           int i, leap;
           leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
           for (i = 1; i < month; i++)
               day += daytab[leap][i];
           return day;
       }
       /* month_day:  set month, day from day of year */
       void month_day(int year, int yearday, int *pmonth, int *pday)
       {
           int i, leap;
           leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
           for (i = 1; yearday > daytab[leap][i]; i++)
               yearday -= daytab[leap][i];
           *pmonth = i;
           *pday = yearday;
       }
    

  • The arithmetic value of a logical expression, such as the one for leap, is either zero (false) or one (true), so it can be used as a subscript of the array daytab.


  • The array daytab has to be external to both day_of_year and month_day, so they can both use it.It is char to illustrate a legitimate use of char for storing small non-character integers.


  • If a two-dimensional array is to be passed to a function, the parameter declaration in the function must include the number of columns; the number of rows is irrelevant, since what is passed is, as before, a pointer to an array of rows.


  • Thus if the array daytab is to be passed to a function f, the declaration of f would be: 
       f(int daytab[2][13]) { ... }
    It could also be 
       f(int daytab[][13]) { ... }
    since the number of rows is irrelevant, or it could be 
       f(int (*daytab)[13]) { ... }
    which says that the parameter is a pointer to an array of 13 integers. 




Initialization of Pointer Arrays



  • Consider the problem of writing a function month_name(n), which returns a pointer to a character string containing the name of the n-th month. This is an ideal application for an internal static array.


  •    /* month_name:  return name of n-th month */
       char *month_name(int n)
       {
           static char *name[] = {
               "Illegal month",
               "January", "February", "March",
               "April", "May", "June",
               "July", "August", "September",
               "October", "November", "December"
           };
           return (n < 1 || n > 12) ? name[0] : name[n];
       }
    

  • The initializer is a list of character strings; each is assigned to the corresponding position in the array. The characters of the i-th string are placed somewhere, and a pointer to them is stored in name[i].


  • Since the size of the array name is not specified, the compiler counts the initializers and fills in the correct number.