0

I've been told that the name array is basically just a reference to the first element of the array but I fail to fully wrap my head around this. For example from the following snippet

char str1[] = "Hello";
printf("%d %d %s %c\n", &str1, &str1[0], str1, str1[0]);
// => -476114470 -476114470 Hello H

we can see that both str1 and str1[0] share the same memory address (-476114470) which suggests, at least according to my poor understanding of this, that they are the "same thing". But their actual printed value differ being "Hello" and "H" accordingly. How is this possible?

La Murga
  • 303
  • 4
  • 15
  • If you have `struct {int x, y;} s;`, then `s` and `s.x` have the same address as well, but they're clearly different. – HolyBlackCat May 13 '18 at 21:47
  • 3
    Any explanation involving "the name of an array" is wrong. Throw your tutorial away. The issue has nothing to do with names. – melpomene May 13 '18 at 21:47
  • 1
    Look at people waiting in line. You'll notice that the queue starts exactly where the first person is standing. This does not mean that the queue and that person are the same thing. – molbdnilo May 13 '18 at 21:54
  • @molbdnilo correct. I guess what I fail to understand is why would they have the same memory address (if that's what &str1 == &str1[0] implies) if they are different. – La Murga May 13 '18 at 22:14
  • 2
    Read section 6 of the [comp.lang.c FAQ](http://www.c-faq.com). The name of an array is the name of the array. When used in an expression, it is an expression of array type. In most but not all contexts, an expression of array type is implicitly converted to a pointer expression, yielding the address of the array object's initial element. But, for example, `sizeof name_of_array` yields the size of the array object, not the size of a pointer (the conversion does not occur in this case). – Keith Thompson May 13 '18 at 22:20
  • @LaMurga They're the same for the same reason that the location (i.e. address) of the first person in line is the same as the location (address) of the entire line of people, although the entire line occupies space beyond that location. – molbdnilo May 14 '18 at 00:26
  • note that you're also invoking UB because [pointers must be printed with `%p`](https://stackoverflow.com/q/9053658/995714) – phuclv May 14 '18 at 00:52

4 Answers4

3
// `str1` is an _array of char_
char str1[] = "Hello";

What's the difference between a name array and the (address of the) first element of the array in C?

An array is several elements of the same type. The (address of the) first element is an address.


I've been told that the name array is basically just a reference to the first element of the array ...

In some contexts, they appear the same, yet that is a result of a conversion. 1st line: the array str1 is passed to printf() and in this context, the array is converted. str1 is converted from an array to the address and type of the first element.

printf("%s\n", str1); // Prints "Hello\n"

2nd line: The address of the first array element is passed.

printf("%s\n", &str1[0]); // Prints "Hello\n"

Unsurprisingly, they print the same.

In this context, you will certainly see a difference.

printf("size: %zu\n", sizeof str1);    // Prints 6
printf("size: %zu\n", sizeof &str1[0]);// Size of a char * pointer

we can see that both str1 and str1[0] share the same memory address

printf("%d %d\n", &str1, &str1[0]);

This line of exploration can mis-lead. &str1 and &str1[0] are both addresses to the same memory location but have different types: address of a array 6 of char vs address of a char.

As very different types they may even print different address encodings as a addresses may have many representations. (This is uncommon though).

Note: using "%d" to print object pointers is not well defined, use "%p" after a (void *) cast.

printf("%p %p\n", (void *) &str1, (void *) &str1[0]);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
2

This is how it looks like in memory:

+-----+-----+-----+-----+-----+-----+
| 'H' | 'e' | 'l' | 'l' | 'o' | \0  |
+-----+-----+-----+-----+-----+-----+
^
+-- pointer to first element
|
+-- also: pointer to array

Note in particular that %c and %s do not deal with the same type of arguments! %c deals with a char (that is, a single character), whereas %s deals with a char * (that is, a pointer to an array of characters, hopefully null-terminated).

If you just have a char x;, printing it with printf("%s", &x); - you'd have to provide the address, since %s expects a char* - would yield unexpected results, as &x + 1 might not be 0.

Moreover,

int printf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);     // Initialize stdargs

    const char *p = fmt;   // Pointer into the format string
    while (*p != '\0') {
        char ch = *p++;    // Get the next character of the format string

        if (ch != '%') {   // Not a format character? Just print it
            putchar(ch);
            continue;
        }

        char fmt = *p++;   // Get the character following the %

        if (fmt == 'd') {
            int n = va_arg(ap, int);
            print_number(n);

        if (fmt == 'c') {
            char c = va_arg(ap, char);
            putchar(c);

        } else if (fmt == 's') {
            char *str = va_arg(ap, char *);
            print_string(str);

        } else {
            // Unrecognized format character
        }
    }

    va_end(ap);

    return n;
}

After your examination, let's look at pointer arithmetic,

#include <stdio.h>

int test() {
    char str1[] = "Hello";
    char const * ptr = str1;

    while(*ptr != '\0') {
        printf("%p  -->  %c\n", (void*)ptr, *(ptr++));
    }

    return 0;
}

int main(int argc, char * argv[]) {
    test();
    return 0;
}

Output:

0x7ffedff6132a  -->  H
0x7ffedff6132b  -->  e
0x7ffedff6132c  -->  l
0x7ffedff6132d  -->  l
0x7ffedff6132e  -->  o
0

In your print statement, try to replace str1[0] with its memory address &str1[0] and use %s instead of %c. You'll notice the entire string will be printed as well.

As suggested by Ivan:

You can use printf("%s\n", &str1[0]); without any problem, since str1 is exactly the same address as the ADDRESS of the 1st element (&str1[0]) and "%s" takes the address that you want it to start printing the string and it prints until it finds '\0'. In the same manner you can also use printf("%s\n", &str1[3]); which will output 'lo' on the screen.

Now to explain why both str1 and &str1[0] are not always the same thing:

Using sizeof on an array is an example of situation when the array is not changed into a pointer to its first element. sizeof str1 is not the same as sizeof &str1[0].

Less importantly: use %p instead of %d when displaying pointer addresses.

  • 4
    You can't print `str1[0]` with `%s`, that's undefined behavior and likely to crash. – interjay May 13 '18 at 21:52
  • 1
    You can use `printf("%s\n", &str[0]);` without any problem, since `str` is exactly the same address as the ADDRESS of the 1st element (`&str[0]`) and "%s" takes the address that you want it to start printing the string and it prints until it finds '\0'. In the same manner you can also use `printf("%s\n", &str[3]);` which will output 'lo' on the screen. – Ivan Angelov May 13 '18 at 22:06
  • @IvanAngelov I quoted it in my answer did I do it the right way ? Thanks for suggesting that. –  May 13 '18 at 22:10
  • @TanguyAndreani I don't know what the `sizeof` operator has to do with that. When using `sizeof(str);` it will return the size in bytes of the array "Hello"(5) + '\0'(1) = 6. On the other hand `sizeof(&str[0]);` will return the size of the pointer, which is platform dependant. – Ivan Angelov May 13 '18 at 22:23
  • Yes this is what I wrote it is a sample case when the two are not equivalent –  May 13 '18 at 22:24
0

array name is a constant pointer and points to starting address of an array.

str[i] = [i]str = *(str + i)

so, &str[0] = &(*(str + 0)) = &*str = str;

hence it is clear that address of first element is same as array name (base address of array).