Pointers, Constants, and Multiple Indirection in C

Pointers can use different levels of indirection; in fact, we often have pointer variables that are pointing to another pointer.

#include <stdio.h>

int main(void){
    
    char *bands[] = {"The Talking Heads", "Elvis Costello", "The Pixies", "Prince"};
    char *albums[] = {"Remain in Light", "This Year's Model", "Doolittle", "1999"};
    
    char **bandsAlbums[2];
    
    bandsAlbums[0] = bands;
    bandsAlbums[1] = albums; 
    
    //loops through first array
    for(int i = 0; i < 4; i++){
        printf("%s\t", *(*bandsAlbums+i));
    }
    
    //first element of second array
    //Remain In Light
    printf("\n%s\t", **(bandsAlbums+1));
    
    //second element of second array
    //This Year's Model
    printf("%s\t", *(*(bandsAlbums+1)+1));
    
    return 0;
    
}


Multiple indirection is most commonly used with strings, since a string is itself an array. However, we can use multiple indirection with any sort of array.

#include <stdio.h>

int main(void){
    
    int values[10];
    int tenValues[10];
    
    for(int i = 0; i < 10; i++){
        values[i] = i+1;
    }
    
    for(int i = 0; i < 10; i++){
        tenValues[i] = (i+1)*10;
    }
    
    int *iPtr[2];
    
    iPtr[0] = values;
    iPtr[1] = tenValues;
    
    //prints 1
    printf("%d\t", **iPtr);
    //prints 10
    printf("%d\t", **(iPtr+1));
    
    //prints 2
    printf("%d\t", *(*iPtr+1));
    //printfs 20
    printf("%d\t", *(*(iPtr+1)+1));
    
    return 0;
    
}

Note that there is not an inherent limit on the number of levels of indirection possible.

Pointers and the const keyword go hand-in-hand in C. A pointer can be defined as a pointer to a constant, which means that the pointer cannot be used to modify the value it is referencing.

#include <stdio.h>

int main(void){
    
    double d01 = 22.41;
    double d02 = 169.254;
    
    double *dPtr;
    const double *const_dPtr;
    
    dPtr = &d01;
    const_dPtr = &d02;
    
    printf("%f\t", *dPtr);
    //can modify value via 
    //regular pointer
    (*dPtr)++;
    printf("%f\t", *dPtr);
            
    printf("\n%f\t", *const_dPtr);
    //we cannot modify value via
    //const pointer
    d02++;
    printf("%f\n", *const_dPtr);
            
    return 0;
    
}

We can dereference a constant pointer, but we cannot change the value to which it is pointing. We can, however, change what the value the pointer is pointing at; the pointer value itself is not constant. The pointer can be changed to point to a different value.

#include <stdio.h>

int main(void){
    

    int a = 42;
    int b = 47;
    
    const int *const_ptr = &a;
    
    
    printf("%d\n", *const_ptr);
    
    const_ptr = &b;
    
    printf("%d\n", *const_ptr);
    
    return 0;
    
}

We can declare a constant pointer to a nonconstant. To do so , we switch the data type and the const keyword. When we do this, it is possible to change the value stored in the variable to which the pointer is pointing; however, we cannot direct the pointer to point to a different variable.

#include <stdio.h>

int main(void){
    
    char a = 'a';
    char b = 'b';
    
    char *const const_ptr1 = &a;
    char *const const_ptr2 = &b;
    
    *const_ptr1+=2;
    *const_ptr2+=2;
    
    //prints c and d, respectively
    printf("%c\n", *const_ptr1);
    printf("%c\n", *const_ptr2);
    
    return 0;
    
    
}

If you can, please purchase a copy of my book. It can be read on your Amazon Kindle, or on an Android tablet or iPad using the Amazon app: http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

 

 

 

Advertisements

Addition and Subtraction of C Pointers

When we add an integer value to a pointer, the amount added is actually the number multiplied by the number of bytes in the underlying data type. As the compiler is aware of the data type size, pointer values can be automatically adjusted in a portable fashion.

#include <stdio.h>

int main(void){
    
    int nums[] = {16, 42, 69, 97, 126};
    
    int *ptr = nums;
    
    printf("nums[0] %p\t%d\n", ptr, *ptr);
    printf("nums[1] %p\t%d\n", ptr + 1, *(ptr + 1));
    printf("nums[2] %p\t%d\n", ++ptr + 1, *(ptr + 1));
    printf("nums[3] %p\t%d\n", ptr + 2, *(ptr + 2));
    printf("nums[4] %p\t%d\n", ++ptr + 2, *(ptr + 2));
    
    return 0;
}

When an array name is used without an index, it devolves to the address of the first element of the array. It follows that array elements are stored in consecutive memory locations.

#include <stdio.h>

int main(void){

char b;
int c;

//be sure
//pointer types are compatible
char *bPtr = &b;
int *cPtr = &c;

//increments by one
printf("\nchar:\n");

printf("%p\n", bPtr);
bPtr++;
printf("%p\n", bPtr);
bPtr = bPtr + 1;
printf("%p\n", bPtr);

//increments by four
printf("\nint:\n");

printf("%p\n", cPtr);
cPtr += 1;
printf("%p\n", cPtr);
printf("%p\n", ++cPtr);

return 0;
}

The int pointer in the example above is incremented by four, because the size of an int is (typically) four bytes. Likewise, the char pointer is incremented by one, because the size of a char is (almost always) one byte.

When one pointer is subtracted from another, we get the offset in units between the two pointers, not the difference in bytes.

#include <stdio.h>

int main(void){
    
    int ints[] = {15, 45, 60, 105};
    long long llInts[] = {1024, 16384, 65536};
    
    int *iP1 = ints;
    int *iP2 = ints + 1;
    
    long long *llPtr1 = &llInts[0];
    long long *llPtr2 = &llInts[1];
    
    
    printf("%p = %d\n", iP1, *iP1);
    printf("%p = %d\n", iP2, *iP2);
    
    printf("difference between two elements = %ld\n", iP2 - iP1);
    
    printf("\n%p = %lld\n", llPtr1, *llPtr1);
    printf("%p = %lld\n", llPtr2, *llPtr2);
    
    printf("difference between two elements = %ld\n", llPtr2 - llPtr1);
    
    return 0;
    
}

If you get the chance, take a look at my authors page at Amazon http://www.amazon.com/Al-Jensen/e/B008MN382O

 

 

 

 

String Functions in

The strcat() function joins two strings together by overwriting the null character in the first string parameter with the second string, which retains its null terminating character. One conclusion we can draw from this operation is that we must be sure that the first string has a large enough space in memory to contain both strings, as there is no bound checking.

#include <stdio.h>
#include <string.h>

int main(void){
    
    char string1[200] = "Making assumptions is part of any investigation.";
    char *string2 = " If we're wrong, all it'll cost is an apology.";
    
    //strcat returns the first string
    printf("%s", strcat(string1, string2));
            
    //first string remains changed
    printf("\n%s\n", string1);
    
    return 0;
    
}

Our next program will append the second string read from stdin to the first, and then output the string to stdout.

#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 200

int main(void){
    
    char string1[BUFFER_SIZE];
    char string2[BUFFER_SIZE];
    
    printf("Please enter a string:\t");
    
    //standard input (stdin)
    //is treated as a file
    fgets(string1, BUFFER_SIZE, stdin);
    
    printf("Please enter another string:\t");
    
    fgets(string2, BUFFER_SIZE, stdin);

    //note how fgets includes the trailing newline
    //in the strings
    printf("\n%s\n", strcat(string1, string2));
    
    
    return 0;
    
}

The strchr() function returns a pointer to the first occurrence of a byte in a string provided to it. If no match is found, a null pointer is returned.

#include <stdio.h>
#include <string.h>

int main(void){
    
    char *string1 = "The morning sun has vanquished the horrible night";
    char string2[100] = "What a terrible night for a curse.";
    
    char *string3 = strchr(string1, 'v');
    char *string4 = strchr(string2, ' ');
    
    printf("\n%s\n", string3);
    printf("\n%s\n", string4);
    
    return 0;
    
}

The strcmp() function compares two strings and returns an integer based on the outcome. A value less than zero is returned if the first string parameter is less than the second string parameter. A value of zero is returned if the two strings are equal to each other. Finally, a value of greater than zero is returned if the first string is greater than the second string.

#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 200

int main(void){
    
    char string1[BUFFER_SIZE] = "Three pounds of flax";
    char string2[BUFFER_SIZE] = "Three pounds of flax";
    char string3[BUFFER_SIZE] = "three pounds of flax";
    
    if(strcmp(string1, string3)){
        printf("<%s> and <%s> are not the same\n", string1, string3);
    }
    
    if((strcmp(string1, string2))==0){
        printf("<%s> and <%s> are the same\n", string1, string2);
    }
    
    //result might seem counterintuitive
    if((strcmp(string1, string3))<0){
        printf("<%s> is less than <%s>\n", string1, string3);
    } else if((strcmp(string1, string3))>0){
        printf("<%s> is greater than <%s>\n", string1, string3);
    } else {
        printf("<%s> and <%s> are equal\n", string1, string3);
    }
    
    return 0;
    
}

The strcpy() function copies the contents of the second string parameter into the first.

It bears mentioning here than in certain situations that call for a pointer, an array name devolves to a pointer to its first element. Remember, a string is a char array with a standardized sentinel, in this case the null character, defining its boundary.

#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 200

int main(void){
    
    char string1[BUFFER_SIZE];
    char string2[BUFFER_SIZE] = "Han shot first.";
    char string3[BUFFER_SIZE] = "No one knows what he can do till he tries.";
    
    strcpy(string1, string2);
    
    printf("\n%s\n", string1);
    
    printf("\n%s\n", strcpy(string2, string3));
    
    char *string4 = strcpy(string3, string1);
    
    printf("\n%s\n", string4);
    
    return 0;
    
}

Don’t forget that there are no standard operators for assigning and comparing strings. To assign a string value to another string, we must use strcpy(). To compare a string, we must use strcmp().

The strlen() function returns the length as an unsigned long integer of the null-terminated string pointed to by the function’s sole parameter. Note that the null terminator itself is not counted.

#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 200

int main(void){
    
    char *str1 = "Don't Panic";
    
    printf("The length of the string: %lu\n", strlen("You rush a Miracle Man, you get rotten miracles"));
    
    printf("The length of %s is %lu", str1, strlen(str1));
    
    return 0;
}

The strncat() function joins a two strings together, with the second string parameter being limited in length by a certain number of characters specified in the third and final function parameter, which is an integer number.

#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 30
#define LARGER_BUFFER_SIZE 60

int main(void){
    
    char string1[BUFFER_SIZE]="";
    char string2[LARGER_BUFFER_SIZE] = "As Thucydides has said, ";
    char *string3 = "The secret of happiness is freedom. The secret of freedom is courage.";
    
    strncat(string1, string3, BUFFER_SIZE);
    
    printf("\n%s\n", string1);
    
    //remember, strncat and strcat append second string
    //to the end of the first
    strncat(string2, string3, LARGER_BUFFER_SIZE - strlen(string2));
    
    printf("\n%s\n", string2);
    
    return 0;
}

If you’re interested in learning more about the C language, please buy a copy of my book at http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M