
Are pointers and arrays equivalent in C? (2009) - deanstag
http://eli.thegreenplace.net/2009/10/21/are-pointers-and-arrays-equivalent-in-c
======
Animats
You can actually pass an array as an array in C99:

    
    
        #include <stdlib.h>
    
        void f(size_t m, size_t n, float a[m][n])
        {   for (size_t i = 0; i < m; i++) {
                for (size_t j=0; j < n; j++) {
                    a[i][j] = 0;
                }
            }
        }
    
        int main(int argc, char* argv[]) {
            float a[10][20];
            f(10,20,a);
        }
    
    

This "conformant array" feature was supported in C99. GCC supports it.
Microsoft didn't like it, though, and didn't implement it, so nobody uses it,
and it was made optional in later versions of the C standard.

It doesn't do what it looks like it does. The last subscript is totally
ignored by the compiler. For 1D arrays, it's not only ignored, the object is
still passed as a pointer to the element type.

I once proposed making arrays in C size-safe by taking conformant arrays
seriously and adding some features to make them more useful.[1] After much
discussion, the idea was considered sound, but not popular enough. So buffer
overflows in C continue.

I have great hopes for Rust.

[1]
[http://animats.com/papers/languages/safearraysforc43.pdf](http://animats.com/papers/languages/safearraysforc43.pdf)

~~~
Peaker
That's ordinary C89 behavior, not a C99 feature, and I'm pretty sure Microsoft
supports it too. The C99 feature here is VLA (variable-length array), arrays
whose length is a runtime value.

If you change m and n to be constants, it works with C89.

The rule is simple (yet confusing!): In a function prototype, if the parameter
(top-level) type is an array, it is de-sugared to be an element-pointer
instead.

Thus, the code you wrote with constants instead:

    
    
        #define M 10
        #define N 20
        void f(size_t m, size_t n, float a[M][N])
    

De-sugars, as per C89 ordinary rules, to:

    
    
        void f(size_t m, size_t n, float (*a)[N])
    

And it is an ordinary array degradation to a pointer, except instead of
pointing to a float, it happens to point at a float[N].

~~~
Animats
Variable length arrays are a different feature. They are local temporary
arrays of variable size:

    
    
        int f(size_t n) {
            int temp[n]; /* local array of size n */
        ...
        }
    

Someone once used that feature in the Linux kernel to get temp space for a
string. It was taken out, because those arrays went on the stack and thus
allowed large kernel stack growth based on data from outside the kernel.

Nobody uses that feature either.

~~~
Peaker
EDIT: I now see what you mean, sorry for misunderstanding.

The feature sounds useful, but the reason I misunderstood you is that you used
the same syntax that represents array-to-ptr degradation, which made me think
you were confused :-) I'm not sure how you would make this properly backwards
compatible with C. You'd need to use a different syntax for such a major new
feature.

Kept here for posterity:

You used variable length arrays in conjunction with ordinary array-of-array
notation that (in function parameter list context) degrades to ptr-to-array.

Here is the output of gcc on your code, with -Wvla (which warns about use of
vla):

    
    
      gcc -c testvla.c -o /dev/null -Wvla -std=c99
      testvla.c:3:1: warning: variable length array ‘a’ is used [-Wvla]
       void f(size_t m, size_t n, float a[m][n])
       ^
      testvla.c:3:1: warning: variable length array ‘a’ is used [-Wvla]
    

The VLA is why your code is C99. If you use a[10][20] in the function
prototype, it is ordinary C89, and if Microsoft wrote a C89 conformant
compiler, it works on Microsoft's compiler as well.

------
krylon
When I was first learning C, pointers scared the crap out of me, and it took
me longer to understand them than I am comfortable admitting to.

Then I got pointers and this big "NOW I get it"-moment and felt really clever.

And _then_ came the moment I had a problem with pointers and arrays being
interchangeable _most_ of the time and became very confused about when they
were not equivalent.

People who like C (which I do, too) will often point out C's perceived
simplicity when compared with, say, C++, but C definitely does have its murky
corners, and the need for backward compatibility with existing code will most
likely prevent them from getting cleaned up.

~~~
lfowles
Much of my initial confusion with C style pointers was using & to show pass-
by-reference and to get the address of a variable. It still doesn't make
sense, but at least I've got it deeply ingrained now :\

~~~
pikachu_is_cool
C doesn't have pass by reference.

~~~
ArkyBeagle
It has "pass a pointer by value", which works out conceptually to the same
thing, only with slightly different syntax.

~~~
pikachu_is_cool
Yeah I was talking about this:

> was using & to show pass-by-reference

------
wsxcde
One other difference on modern compilers relating specifically to character
arrays:

This will work fine:

    
    
        char arr[] = "stuff";
    
        printf("arr:%s\n", arr);
        arr[0] = 'b'; arr[1] = 'l';
        printf("arr:%s\n", arr);
    

arr is allocated on the stack (not heap, sorry about the mistake) and is
mutable.

But the following will segfault:

    
    
        char* ptr = "stuff";
    
        printf("ptr:%s\n", ptr);
        ptr[0] = 'b'; ptr[1] = '1';
        printf("ptr:%s\n", ptr);
    

Strings are allocated in the read-only data segment. Technically you can only
write const char* ptr = "stuff", but due to wanting to remain backward
compatibile, gcc will let you write the above and not even warn you unless you
use -Wall or similar.

~~~
deanstag
Yeah. I was exploring exactly this problem when i came across this original
article. Later i saw that "char arr[] = "stuff" " actually inserts assembly
code that puts the value stuff into the stack. That was interesting news to
me. stackoverflow.com/questions/29184100/char-array-and-pointer-
initialization-semantics/29184190#29184190

------
halayli
This is straight from Expert C Programming book, including the diagrams. It's
dishonest to recycle and present it as if it's yours with a reference mention
at the very bottom. This is not a reference, it's a copy.

~~~
logicallee
I agree with the other poster. I was going to upvote you earlier, and
torrented the book you refer to for verification (ironically because I don't
believe material should be stolen like this), but apparently I never got
around to it because I was able to downvote you just now. The reason I never
finished upvoting you was that I didn't think I found the right book: Expert C
Programming doesn't match your description (of not being a reference, but
rather the article being a copy of it). I didn't find the diagrams, and the
text was very different.

So it sounds like your accusation is way off-base, and you should specify that
you were wrong. Maybe you were just reminded of that other book but didn't
verify that the article was a copy of it?

------
mpweiher
Also sizeof is different.

    
    
       char a[20];
       char *b;
    

sizeof a -> the size of the array (20)

sizeof b -> the size of the pointer (8 on my machine)

~~~
armada651
I was really surprised that it wasn't mentioned since it is the most important
difference and a cause of frustration for programmers just beginning to learn
C.

------
eliben
Funny how old articles resurface suddenly :-) It's great that HN still likes
technical content, though.

FWIW I also wrote a (shorter) follow-up post a few months later about multi-
dimensional arrays and their relation to pointers-to-pointers:
[http://eli.thegreenplace.net/2010/04/06/pointers-vs-
arrays-i...](http://eli.thegreenplace.net/2010/04/06/pointers-vs-arrays-in-c-
part-2d)

------
userbinator
I like to think of them as being different and the equivalence - in some cases
- only comes from what is known as the "array-pointer-decay" semantic:

[http://www.lysator.liu.se/c/c-faq/c-2.html](http://www.lysator.liu.se/c/c-faq/c-2.html)

This is why &a+n is not equivalent to a+n, something that a surprisingly large
number of "C pointer tutorials" seem to get wrong.

------
sago
Excellent. Years ago (15-20?) I wrote a program with the bug he mentions at
the end. Way before Stack Overflow was a Google away. I could not work out why
it crashed. I ended up rewriting it and it worked, but never understood what
the bug was (in my mind it has been logged as 'don't extern arrays' ever
since). I just read that and ah-ha!

------
je-so
Another interesting thing to know about pointer and arrays is:

    
    
      #include <stdio.h>
      typedef int a_t[100];
    
      int main(void)
      {
         int  a[100];
         a_t* p = &a;
    
         // all printed pointer values are equal
         printf("%p %p %p %p\n", (void*)a, (void*)&a, (void*)p,  (void*)*p);
         return 0;
       }

------
golergka
Will I be correct if I say that in the end, the difference is that arrays are
immutable (in the address that they point to) and pointers are not?

~~~
Peaker
No. An array has size (N * sizeof(element)). Arrays are immutable in their
address in the same sense that any lvalue is immutable in its address.

    
    
      int x;
      int y[2];
    

The two declarations above are similar, one happens to be an (int). The other
an (int[2]).

The difference is that when you take the rvalue of an int, say x, you get an
rvalue int. When you take the rvalue of an int[2], you get a degraded (int *)
rvalue that points to its first element.

Until you convert the array object to an rvalue, it is no more related to an
(immutable) pointer than the above int. Both have an immutable address, like
all lvalues do.

------
justinmk
This is a well-presented version of
[http://c-faq.com/aryptr/](http://c-faq.com/aryptr/).

------
wbsun
Aren't everything equivalent in C?

~~~
wbsun
Why down vote? I don't think anyone who disagrees with this really knows C, or
more accurately, how most computers work. Can down voters name something that
can't be converted to any others? Unless you are using a Harvard architecture,
anything in memory are equivalent.

~~~
SamReidHughes
You can't just freely reinterpret memory in C without following certain rules,
or you get undefined behavior. Also, types have sizes, and alignment
restrictions, and on some systems, trap representations.

------
kazinator
Ah Betteridge's law of headlines at work!

Arrays are aggregates of contiguously allocated objects.

Pointers are values indicating the storage locations of objects.

Thus, not equivalent.

Pointers are involved when you access an array. That doesn't make it an array.
Just like scanning written text on a piece of paper using your index finger
does not mean that "paper" and "your index finger" are equivalent. That's true
even if you always instantiate an index finger before evaluating a piece of
paper: co-ocurrence is not equivalence.

~~~
gnuvince
Why is this comment being downvoted? The explanation is correct and the
analogy quite good too!

~~~
DanBC
I suspect it's downvoted for the mention of Betteridges Law of Headlines,
which (rightly) earns downvotes whenever it's mentioned.

~~~
kazinator
Look, it even worked for your comment! Must be true.

