3

I have tried to understand why you can say the name of an array is a pointer to its first element in C. Could you please tell me if my understanding below is correct?

Both the name of an array and the name of other variable (int, char, float...) are a representation of the address in memory where the actual values are stored. But, since making a copy of all the elements of an array is not efficient when you pass it to a funciton, computer scientists decades ago designed the C language to pass arrays by reference. This makes the name of an array work essentially in the same way as a pointer to its first element. Thus, you can say the name of an array is technically the same as a poitner to its first element although not completely the same.

Thank you for your help in advance!

2 Answers 2

3

First I commend you for asking the question and trying to affirm your understanding of the language and what you are learning. It is admirable and requires confidence and a degree of bravery. And it shows to me you are not just trying to solve the questions but truly learn the material and understanding of the course. This makes me confident that you will, and that you are on your way to success in this field. This is far and away one of the better questions on this forum I have seen in weeks, and I wish I could give you more than one upvote for asking it.

TLDR short version Your first point is 100% right: the variable name of an array in C decays to being a pointer to the first element the array. But this is completely unrelated from the rest of what you talk about.

You are kinda right in regard to argument passing but also kinda not right. Remember it extends beyond just arrays, but to structures as well. In fact they needed a system to pass EVERY type of variable. If you pass an integer to a function by value for example, none of the changes are made to the original variable, the changes made in the function are function local and are made on a copy of the variable. If you want the changes to be visible outside the function and avoid the copy, you pass by address/pointer. The copy is avoided and the actual variable is modified. If you can only pass by a pointer, then you can use the const keyword in one of two places to denote that either the pointer cannot be changed or what it points to can not be changed. But what can and should you pass for structs and arrays? Well the only thing you have is generally the address of the struct or array generally speaking.

Full Answer

Nothing in C is 'passed by reference', that is a C++ concept. In C you can pass by value or 'pass by address'. This is subtly different from 'pass by reference' and some folks in C who have never gone into C++ do use this terminology but you should be aware that there is a difference. But for the sake of your question we can say that you have the gist of that correct.

In C the variable of an array decays into a pointer to the array because that is effectively what it is to start with; it's just that you don't have to malloc the memory, and you can't change it later (you can only change the elements of the array). What else could the variable name itself "do"? It has to point to something does it not? This just is. It's a mildly confusing thing which is why CS50x avoids the subject with the string class early on instead of exposing you to the char * array that it really is. This property has nothing to do with the second area you discuss. Effectively creating what is often called a c-style array you are really creating a static array and what's happening under the hood for <type> foo[4] is creation of const <type> *foo = malloc_from_stack(sizeof(<type>) * 4); Thus foo and &foo[0] have the same address, and with the caveat that this is a stack variable and when out of scope will be automatically free'd just like a non-pointer. Notice the leading const which means that foo can't be reassigned to a point to a different address, but foo's contents can be changed. foo is permanently typed to by pointer to array of 4. It should be somewhat self evident the reason for this is because if you reassigned foo to point to something else it would create leakage problems for its memory. (point to something else as in changing it later to be foo = int[5] or foo = malloc(sizeof(<type> * <any number including 4>) becuase then what should happen to foo's original stack memory? They didn't want to open up this rabbit hole. If you want to be able to treat foo dynamically it has to be just a pointer from the beginning.

Your understanding regarding passing arguments is only mildly incorrect. While the need for efficiency is one piece, it also has to do with ability to be precise and limitations of the language combined with a need to be able to pass all kinds of different variables: built-in (struck atomic) types such as int, c-style arrays like int[], pointers to types like char *, and finally and perhaps most importantly passing 'structures' (I don't want to call them objects and conflate the term here). The need for efficiency is just one of the many reasons for this. Ultimately they are mimicking the way the assembly instructions would work. This is also handy because what if you don't want to pass the whole array, what if I just want to pass from the 5th element on? Now I don't need a copy, I just pass function(&foo[4], length) where length is often how many more elements are present (often used for non-null terminated cases).

There are times where you want to make a copy and times where you don't, there are times where you want to build or populate the variable/structure or pointer and times where you don't. There are times where you want to modify what the pointer points to and times where you do not. C is not C++, it is a low level language and does not support overriding and defining many of the operations that would be needed in order to make custom structures properly assignable/copyable/etc which would be needed for being passed by value to other functions. For example if you defined a struct foo which had both pointer members and non-pointer members, which ones should be copied over (deep copy) and which ones should not (and how do we define this) if you passed it by value (or even assignment)? C deliberately doesn't give you a good way to define this. C kept things simpler, more predictable, straight forward and user definable. They left the copying to be done externally by the user, because it's a low level language, and the concept was the user would know whether they wanted to do a copy or not, and also because it is more akin to the way the assembly language would work itself.

By passing a pointer to the variable, we mimic what the assembly would do, and we sidestep having to have the language define any of these behaviors, which makes the language simpler and more deterministic, which in turn makes the compiler simpler and the optimization simpler.

In C++ this is an area of many pieces of change through the years in terms of context, optimization and evolving practices. We had the rule of 3, and now the rule of 5/0, we now have r-values, move-assignment, emplace-back and we also have copy-ellision and return-value-optimization, and all sorts of other quirks. If your head is spinning you are not to blame. Its complicated and it has vastly complicated optimization where taking certain actions can prevent other optimizations from occurring. Since C++11 a once mildly complicated area became drastically more complicated. Practices of 'always pass by reference or const-reference' have quite often been shifted to pass by value or by r-value or by reference or pointer... and I'll spare you the headache of going deeper here. It's a real rabbit hole

This is one of the many reasons for the saying "C makes it easy to shoot yourself in the foot, C++ makes it a harder but when you do you blow your leg off".

I hope that helps and makes sense. Best of luck to you on your journey

5
  • Very nice, thorough explanation. up votes for the q and the a.
    – Cliff B
    Commented Mar 19 at 3:48
  • Thank you very much, @UpAndAdam. I totally appreciate your help. So, maybe I can say an array's name decaying to a pointer is basically what it is, part of how C was created pursuing practical benefits.
    – Yuya Ito
    Commented Mar 20 at 10:10
  • Can I ask you another question if you don’t mind? The following is from a slide about pointers used in CS50. The word “then” in the third sentence implies what is said in the third sentence is true because of the first two sentences. But, just given the first and second sentences, why can you conclude an array’s name is actually a pointer as the third sentence does? Would you mind telling me the logic that bridges the third sentence and the first two?
    – Yuya Ito
    Commented Mar 20 at 10:14
  • 1, If x is an int-type variable, then &x is a pointer-to-int whose value is the address of x. 2, If arr is an array of doubles, then &arr[i] is a pointer-to-double whose value is the address of the ith element of arr. 3, An array’s name, then, is actually just a pointer to its first element.
    – Yuya Ito
    Commented Mar 20 at 10:15
  • 1
    Const correctness issues aside, You are being confused by a poorly laid out inference. And I have already literally answered your question above. Just ask what happens when i is zero aka the first element. The inference to be made is what does arr point to? It points to the same thing that &arr[0] points to. What is &arr[0] equal to? &(arr + sizeof(double)*0) == &(arr) --> so arr is a pointer to a double (forgetting const correctness and size nature).
    – UpAndAdam
    Commented Mar 20 at 17:57
1

Yes, as far as I know, your understanding is correct.

But as you already noted, they are not completely the same, indeed. And that is explained by the fact that, unlike regular pointers which can be reassigned to point to different memory addresses, the name of an array is fixed and cannot be reassigned.

In other words, once an array is declared, its name (or the pointer pointing to its first element) remains constant throughout the program execution.

On the other hand, regular pointers can be reassigned and used to dynamically allocate memory, which is obviously a huge difference. This feature is particularly useful in situations where you need to store data but don't know in advance how much space you will need, since pointers allow you to allocate as much space as needed dynamically, without having a crystal ball beforehand.

So yes, at the end of the day they are indeed all pointers, but the ones pointing to an array's 1st element can NOT change, while all the others can.

Hope that explanation helps!

1
  • Thank you so much for your help, @Stavros Marinos!
    – Yuya Ito
    Commented Mar 20 at 9:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .