Thread: Pointer to an array : string

  1. #1
    Registered User
    Join Date
    Jun 2018
    Posts
    26

    Pointer to an array : string

    Hello,

    I only have intermediate knowledge in c.
    I'm learning some advanced topics about pointers in c,

    Below is the program that confuses me
    Code:
    #include<stdio.h>
    
    int main(void){
    
        //section 1
    
        char str1[]="Hello";
        printf("%d, %d, %d, %s\n",&str1,&str1[0], str1, str1);
        printf("\n");
        
        //section 2
    
        char*str2;
        str2 ="Goodbye";
        printf("%d, %d, %s\n",&str2, str2, str2);
        printf("\n");
    
        //section 3
    
        char*string[3];
    
        string[0]="zero";
        string[1]="one";
        string[2]="two";
    
        printf("%d, %d, %d, %d, %s\n",&string, string,&string[0], string[0], string[0]);
        printf("%d, %d, %d, %d, %s\n",&string, string,&string[1], string[1], string[1]);
        printf("%d, %d, %d, %d, %s\n",&string, string,&string[2], string[2], string[2]);
        printf("\n");
        system("pause");
    }
    OUTPUT : Pointer to an array : string-c2-png

    In section 1

    Code:
    char str1[]="Hello";
        printf("%d, %d, %d, %s\n",&str1,&str1[0], str1, str1);
        printf("\n");
    //output : 15989136, 15989136, 15989133, Hello
    Pointer to an array : string-c3-jpg

    I know address of an array is the address of first element of that array, which is same as the array name

    In section 2

    Code:
    char*str2;
        str2 ="Goodbye";
        printf("%d, %d, %s\n",&str2, str2, str2);
        printf("\n");
    //output : 15989124, 2390488, Goodbye
    Pointer to an array : string-c-jpg

    Here str2 is a pointer, which is stored at some place in memory
    and &str2 will display that address. Value of str2 is the address at which the string "Goodbye" is stored

    I understand up to this part. But third section confuses me,

    Code:
    char*string[3];
    
        string[0]="zero";
        string[1]="one";
        string[2]="two";
    
        printf("%d, %d, %d, %d, %s\n",&string, string,&string[0], string[0], string[0]);
        printf("%d, %d, %d, %d, %s\n",&string, string,&string[1], string[1], string[1]);
        printf("%d, %d, %d, %d, %s\n",&string, string,&string[2], string[2], string[2]);
        printf("\n");
    
    //output:
    //15989104, 15989104, 15989104, 2390852, zero
    //15989104, 15989104, 15989108, 2391020, one
    //15989104, 15989104, 15989112, 2390840, two
    
    
    which shows the address of zero, one, two??
    What each value in output represents??


    Thanks
    Last edited by Athul; 06-25-2018 at 10:19 AM.

  2. #2
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    You should use %p for printing the value of a pointer. You should also cast the pointer values you're printing to (void *). So your code should look like
    Code:
    printf("%p, %p, %p, %p, %s\n", (void *) &string, (void *) string, (void *) &string[0], (void *) string[0], string[0]);
    That aside, here's what's happening

    char *string[3];
    This declares an array of length 3, where each element is a pointer to char. Note, the elements are just pointers, so there is memory to store a pointer only, there is no memory there to store an actual string.

    string[0] = "zero";
    string[1] = "one";
    string[2] = "two";

    This makes the pointer for each element of the array point to a specific string. There is still no storage space in the array itself for the strings. The strings "zero", "one" and "two" are stored elsewhere in memory, and are pointed to by the elements of the array.

    &string (column 1)
    This is the address of the start of the string array. It also happens to be the address of the first element.

    string (column 2)
    This is also the address of the start of the string array, so it always matches column 1. This is the way the C language was designed. If you use the name of an array without any [ ] brackets to index, you get the address of the beginning (first element) of the array.

    &string[n] (column 3)
    This is the address of the n-th element of the array. You can see that &string[0] is the same as &string and string. That is because the first (0th) element of the array is the same as the beginning of the array (there's nothing extra stored at the front, like the length of the array or other metadata). &string[1] is 4 bytes past that, and &string[2] another 4 bytes past. The elements are stored adjacently in memory, there's no unnecessary space in between them (in some cases, there may be padding for alignment reasons, but that's a discussion for later and doesn't apply here).

    string[n] as a pointer (column 4)
    This is the data in the n-th element of the array, i.e. the addresses of the literal strings "zero", "one" and "two". You'll notice these values are significantly different from the previous ones. Those string literals live in a different place in memory from the array you declared.

    string[n] as a string (column 4)
    Just like column 4, this is the data in the n-th element of the array. It's the same address of the literal strings "zero", "one" and "two" that you see in column 4, just interpreted differently by printf. The %p says "write out the pointer value like a memory address". The %s says "interpret and print this address as a string".

  3. #3
    Registered User
    Join Date
    Jun 2018
    Posts
    26
    Thanks for the reply.

    Reason for I used %d instead of %p;

    I'm learning some advanced topics of c from a Udemy course "Advanced C programming : pointers". In which instructor uses %d. The reason he says is,

    In the course, however, I do have a good reason for (sometimes) using %d. Let me explain. A pointer value is a number. It is a number that represents an address. That number can be displayed in any numeric format. The %p specifier displays it in hexadecimal format. Now, some experienced C programmers are so familiar with hexadecimal that they can count in hex numbers as easily as in decimal numbers.

    if you are sufficiently comfortable with hexadecimal to understand that 00000069 is the same value as 105, there is no problem at all here. Just show values in hex. However many (most!) people cannot count easily in hexadecimal. But in many of my lessons we need to be able to count pointer values. This is especially true later in the course when I explain data-type alignment and pointer arithmetic.

    I also tried %p, but never used (void *). I tried with and with out (void *), and got the same result; So whats's the use of (void *) ????
    I only know type cast like this;

    Code:
    int main(void){
    
    int a;
    char b = 'c';
    a = (int)b;
    
    }

    Now coming back tot main question

    &string (column 1)
    This is the address of the start of the string array. It also happens to be the address of the first element.

    The string you mentioned is char *string[3], right??? not "zero", "one" or "two"

    I think I understand your explanation. SO I made a image connecting all data and address. please take a a look at it and correct me if'm wrong

    Pointer to an array : string-c-jpg

    &string[n] (column 3)
    This is the address of the n-th element of the array. You can see that &string[0] is the same as &string and string. That is because the first (0th) element of the array is the same as the beginning of the array (there's nothing extra stored at the front, like the length of the array or other metadata). &string[1] is 4 bytes past that, and &string[2] another 4 bytes past. The elements are stored adjacently in memory, there's no unnecessary space in between them (in some cases, there may be padding for alignment reasons, but that's a discussion for later and doesn't apply here).
    Why 4 bytes for each array element?? does 4 byte required to store an address?? char is 1 byte, right??

  4. #4
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    First, read up on implementation-defined, unspecified and undefined behavior. I find the following a good summary: Question 11.33.

    The use of (void *) is a type cast, just like (int). It changes the type of the pointer.

    The short answer for using %p and (void *) is because it's what the C standard says is the right way to do it. The details on printf are discussed in sections 7.21.6.3 of the C11 standard. There is other relevant info in sections 6.3.2.3, annex J

    Per the C standard, an int and a pointer are not required to be the same size, so when printf tries to interpret the data to fill in for a %d it uses the size of an int, and when it interprets the data to fill in for a %p it uses the size of a void pointer. Imagine a system with 16-bit ints and 32-bit pointers. %d will only look at 16 of the 32 bits in the pointer to display, so you're missing half the data. Now, flip that around to 32-bit ints and 16-bit pointers. %d reads 32 bits of data, but only 16 are the pointer. What are the other 16? Who knows, maybe some other variable, maybe garbage. You're asking printf to read invalid memory, and that's undefined behavior.

    Also, the C standard does not require all pointers to be the same size, e.g. an int pointer might be one size, and a char * another, and it's not safe to convert from one pointer type to another indiscriminately. Since not all conversions are safe, the only way printf can safely take a pointer type to print an address is if it takes a void pointer. You need the cast to ensure the pointer is the right type and printf can interpret the pointer correctly. Failing to cast could result in similar issues (undefined behavior) that using the %d would when you should use %p.

    Having said that, it's quite common on most modern systems that all pointers are the same size, and that ints and pointers are the same size. As is the case with the code you & your professor are using, it seems to work just fine. However, this is not guaranteed, and misusing it can lead to undefined behavior. Maybe you get away with it, or maybe your program crashes 30 minutes after the bad printf happened, and you spend hours or days trying to reproduce and debug it. It's better to just do it right the first time. But if your professor insists on you using %d, then use it; but it is important that you understand why it's wrong and what the correct way is.



    Your image looks correct, with one small exception. I think this is a typo, not a true misunderstanding on your part, but for clarity: You marked the 2nd element of char *string[3]; as being printf("%d", &string[1]);. It should be &string[2].

    And yes, it seems that on your system/implementation that 4 bytes is the amount of memory used to store a char *. A char is usually 1 byte, but a char * is not a char. It's a pointer to char. char and char * are different types, hence different sizes.

  5. #5
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    948
    Quote Originally Posted by anduril462 View Post
    Having said that, it's quite common on most modern systems that all pointers are the same size, and that ints and pointers are the same size.
    On most modern systems, int is 32 bits and a pointer is 64 bits. So using %d is almost guaranteed to give the wrong results on a modern system.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. array of pointer to string
    By lovelycse in forum C Programming
    Replies: 4
    Last Post: 08-31-2012, 06:55 AM
  2. is a string in C = pointer = array?
    By winggx in forum C Programming
    Replies: 1
    Last Post: 03-21-2010, 11:54 PM
  3. qsort() in Array of Pointer to String
    By vb.bajpai in forum C Programming
    Replies: 8
    Last Post: 06-16-2007, 04:18 PM
  4. Pointer to array of string and Array of Pointer to String
    By vb.bajpai in forum C Programming
    Replies: 2
    Last Post: 06-15-2007, 06:04 AM
  5. string to pointer array
    By pankleks in forum C Programming
    Replies: 7
    Last Post: 02-09-2005, 06:45 AM

Tags for this Thread