Sign in to follow this  
DevFred

Are C arrays passed by reference?

Recommended Posts

I'm going to teach a small class the basics of C, and I want to introduce arrays much earlier than pointers. As far as I know, C only has pass by value. Is it safe to suggest beginners that C appears to pass arrays by reference, or will that confuse them? My understanding is that in a formal parameter declaration, char a[] is just syntactic sugar for char *a, and the actual array argument decays into a pointer, and that pointer is passed by value (correct me if I'm wrong). But it is impossible to explain that to the students on the first day.

Share this post


Link to post
Share on other sites
I was not really sure about it, but yes, arrays are passed by reference.

#include <stdio.h>

void function(int array[])
{
printf("Address: %p\n", array);
}

int main(int argc, char **argv)
{
int array[10];
printf("%p\n", array);
function(array);
return 0;
}


0022FF40
Address: 0022FF40

Share this post


Link to post
Share on other sites
That example proves nothing: in main() you are decaying the array to a pointer to pass to printf and the same thing is occurring when you call function().

An example that won't compile:

int main()
{
int example[4];
++example; // illegal
}



This, however, will:

void function( int arg[4] )
{
++arg;
}

int main()
{
int example[4];
function(example);
}



Finally:

void function( int arg[4] )
{
printf("arg size: %d\n",sizeof(arg));
}

int main()
{
int example[4];
function(example);
printf("array size: %d\n",sizeof(example));
}

Share this post


Link to post
Share on other sites
Okay my first example wasn't clear but it is not wrong:
#include <stdio.h>

void function(int array[])
{
array[0]++;
}

int main(int argc, char **argv)
{
int array[10];
array[0] = 5;
printf(" Before: %i\n", array[0]);
function(array);
printf(" After : %i\n", array[0]);
return 0;
}


array is a pointer in function(), but you can still use it as the same array like in main(). I think this is the important thing to DevFred as he does not want to introduce pointers yet.

Share this post


Link to post
Share on other sites
Arrays are not passed by either value or reference in C. You can either pass a pointer (or reference in C++) to an array, or decay the array to a pointer, but either situation will involve a pointer.

You may be able to hide some parts of this reality if you have not introduced pointers yet, by stating that the parameter int arr[] is an array being passed by reference, but its size is unknown (and as such, the value of sizeof(arr) has no meaning). This will work as long as you don't get too deep in the difference between arrays and pointers (but the main differences should not appear until you've dealt with pointers anyway).

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
You may be able to hide some parts of this reality if you have not introduced pointers yet, by stating that the parameter int arr[] is an array being passed by reference, but its size is unknown

That is exactly what I was going to do. Thanks for the confirmation.

Later on, I want to explain this behavior. I'm no C expert, this is my train of thought:
- all C89 sizeof expressions are evaluated at compile time
- the size of an array is not part of the array itself, but the compiler can figure it out from the declaration
- if it were possible to pass arrays by value, the size of the array would have to be part of the parameter declaration, so the function could only be called with a specific sized array as an argument, and that isn't very practical
- you could theoretically overload the function with different sized array parameters, but that's also not very practical
- and of course, there is no overloading in C
- thus, we need a solution that throws away the size of the array, and that is how it is done in C

Does that make sense? Hm, it still doesnt explain why arrays aren't passed by value. But if we see argument-parameter-binding as assignment, it makes sense if you know that you can't assign arrays to arrays. Maybe I should make that clear first.

Share this post


Link to post
Share on other sites
An array in C is really just a pointer to the first value of the array, nothing more (and strings are just char arrays with a terminator). So when ever you pass around an array you are just passing that pointer around.

IMO introducing arrays and not introducing pointers is a very bad idea. The [] is just using pointer arithmetic to get to the position in the array you want. array[5] is no different then *(array +=5). Without understanding the pointer math they will never really understand why their arrays are breaking. Just my 2 cents.

theTroll

Share this post


Link to post
Share on other sites
Read 6.3.

"A reference to an object of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T. "

theTroll

Share this post


Link to post
Share on other sites
I agree that an array is not a pointer, but what pointer arithmetic is disallowed?


int a[] = { 1, 2, 3 };
int b;
int i = 1;

b = a[i]; //works
b = i[a]; //works
b = *(i + a); //works
b = *(a + i); //works
b = *a; // works

Share this post


Link to post
Share on other sites
MY apologies, the statement sounds a little too broad. I was referring to my example earlier. Examples of pointer arithmetic that pointers can do but arrays cannot are +=, -=, ++ and --. Its a minor point, if you pretend arrays are const pointers, but its still an important difference.

Another is that the address of an array and the first element in the array are the same, whereas a pointer has a distinct address.

Share this post


Link to post
Share on other sites
Quote:
Original post by TheTroll
Read 6.3.

"A reference to an object of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T. "

theTroll


The key phrase in that quote is "decays to". "Array" is a distinct type in C++, and most importantly is not a pointer. Arrays however are implicitly convertible to a pointer to their first element. This is the same reason why an int is not a double. The langauge simply provides an implicit int->double conversion.

Also I'd like to make another nitpick: arrays are not passed by reference. The reason is that this is how you pass an array by reference:


// Passing an array by reference
void myFun(char (&myArray)[255])
{
std::cout << sizeof(myArray) << std::endl; // This will output 255 bytes
}


Also, they're not passed as a pointer either, because this is how you pass them as a pointer:


// Passing an array by pointer
void myFun(char (*myArray)[255])
{
std::cout << sizeof(myArray) << std::endl; // This will be four bytes, aka the size of a pointer
}


In C++ we cant make willy nilly use of the phrase "pass by reference" as it means something distinct. However raising that point might cause a student undue confusion.

Share this post


Link to post
Share on other sites
In C++ you can declare a function like this.

int sum_arr(int arr[], int n)

and it would be the same as this

int sum_arr(int * arr, int n)

this is because in C++
int * arr and int arr[]
have the exact meaning but only when used in a function header or prototype.

Share this post


Link to post
Share on other sites
Quote:
Original post by rip-off
the address of an array and the first element in the array are the same

No, &a and &a[0] have distinct types.

For example, given int a[10];, &a yields a pointer to an array of 10 ints, whereas &a[0] yields a pointer to an int.

Quote:
Original post by fpsgamer
In C++ we cant make willy nilly use of the phrase "pass by reference" as it means something distinct.

You are absolutely correct, but I never mentioned C++ :) If I had to teach a C++ class, I would start with vectors, not arrays.
Since there is no real pass by reference in C, I figured it couldn't hurt too much to say "arrays appear to be passed by reference".

Quote:
Original post by mattnenterprise
this is because in C++
int * arr and int arr[]
have the exact meaning but only when used in a function header or prototype.

Correct, but why do you emphasize C++? C has the exact same (non-recursive) specifications regarding T[] and T* for function parameters.

Share this post


Link to post
Share on other sites
Quote:
Original post by DevFred
Quote:
Original post by rip-off
the address of an array and the first element in the array are the same

No, &a and &a[0] have distinct types.


I know [smile].

I didn't mention types, I am talking about if you were to print their values.

The point was supposed to be that if an array is a pointer, and its address is the same as the first element in the array, then the pointer value would vary when you changed the value of the first element, which is obviously a nonsensical situation.

Share this post


Link to post
Share on other sites
Quote:
Original post by DevFred
Quote:
Original post by fpsgamer
In C++ we cant make willy nilly use of the phrase "pass by reference" as it means something distinct.

You are absolutely correct, but I never mentioned C++ :) If I had to teach a C++ class, I would start with vectors, not arrays.
Since there is no real pass by reference in C, I figured it couldn't hurt too much to say "arrays appear to be passed by reference".


Thanks for correcting me :) I am clearly the one who forgot what language we are talking about.

Share this post


Link to post
Share on other sites
Personally, I wish more classes taught about pointers early. It's a concept that many students have trouble with, yet is so fundamental that I've never understood why it gets put off until later.

Certainly, there's many basic concepts that should be taught before pointers, but I for one think that arrays aren't one of them. Teach arrays AFTER pointers, then you can more clearly describe how arrays differ. I've never liked it when I enter a new class, and am told that something is "like this... but not really", and then later being told that "actually... this is how it REALLY works".

[Edited by - gharen2 on July 28, 2008 1:29:48 AM]

Share this post


Link to post
Share on other sites
Post edited for code errors. Copy & Pasted for 9001% more accuracy.

Quote:
Original post by fpsgamer
Quote:
Original post by TheTroll
Read 6.3.

"A reference to an object of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T. "

theTroll


The key phrase in that quote is "decays to".


Indeed (emphasis added in the original quote). Decays does not mean "is", it means "is implicitly convertable to". This can be an important distinction -- for example, trying to cast a pointer to an array of int:
int array[3];
int (*ptr_to_array)[3] = &array;

into a pointer to a pointer to an int:
int ** ptr_to_ptr = (int**)ptr_to_array;
// Note that the above won't compile without the explicit cast.

And then using that pointer yields undefined behavior. And no, I'm not talking "in theory", I'm talking about in practice almost certainly having a crash at best:
// Compiled with MSVC2008 and executed on a typical x86 box:
array[0] = 42; // This works fine, defined behavior
(*ptr_to_array)[0] = 42; // This works fine, defined behavior
(*ptr_to_ptr)[0] = 42; // This generates an Access violation writing location 0x0000002a (42)
// (it tried to treat the integer value stored at array[0] as an address)
// (Yes, this statement invoked undefined behavior)


[Edited by - MaulingMonkey on July 28, 2008 3:54:54 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by gharen2
Personally, I wish more classes taught about pointers early.

The problem is: IMHO there are no simple examples where pointers make sense (without arrays and dynamic allocation), except for emulating pass by reference (by passing pointers by value). Maybe you have a good example?

I have seen way too many introductions to pointers using code like this:

int i = 3;
int *p = &i;
*p = 5;
assert i == 5;

And almost everyone immediately asks themselves "Why the hell would I want to do that? Why not just i = 5; ?". Poor examples like these are the reason for countless "What's the use of pointers?" threads on gamedev.

I think I am going to introduce pointers as a means to implement a swap function, although I am not completely satisfied with that.

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
Quote:
Original post by fpsgamer
Quote:
Original post by TheTroll
Read 6.3.

"A reference to an object of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T. "

theTroll


The key phrase in that quote is "decays to".


Indeed (emphasis added in the original quote). Decays does not mean "is", it means "is implicitly convertable to". This can be an important distinction -- for example, trying to cast a pointer to an array of int:
int array[3];
int (*ptr_to_array)[3] = &array;

into a pointer to a pointer to an int:
int ** ptr_to_ptr = (int**)ptr_to_array;
// Note that the above won't compile without the explicit cast.

And then using that pointer yields undefined behavior. And no, I'm not talking "in theory", I'm talking about in practice almost certainly having a crash at best:
// Compiled with MSVC2008 and executed on a typical x86 box:
array[0] = 42; // This works fine, defined behavior
ptr_to_array[0] = 42; // This works fine, defined behavior
ptr_to_ptr[0] = 42; // This generates an Access violation writing location 0x0000002a (42)
// (it tried to treat the integer value stored at array[0] as an address)
// (Yes, this statement invoked undefined behavior)


Actually I think there is something wrong with your second example also: you are assigning an int into an array of 3 ints.
It should be ptr_to_array[0][0] =42 or (*ptr_to_array)[0] = 42

As for the 3rd example, it will work if you wrote ptr_to_ptr[0] = (int*)42 right? (just making sure I follow you)

edit: I tried it in g++, and your 2nd and 3rd examples don't compile. When I changed it to my suggestions it worked fine, no access violation.

[Edited by - Iftah on July 28, 2008 3:55:56 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by DevFred
Quote:
Original post by gharen2
Personally, I wish more classes taught about pointers early.

The problem is: IMHO there are no simple examples where pointers make sense (without arrays and dynamic allocation), except for emulating pass by reference (by passing pointers by value). Maybe you have a good example?

I have seen way too many introductions to pointers using code like this:

int i = 3;
int *p = &i;
*p = 5;
assert i == 5;

And almost everyone immediately asks themselves "Why the hell would I want to do that? Why not just i = 5; ?". Poor examples like these are the reason for countless "What's the use of pointers?" threads on gamedev.

I think I am going to introduce pointers as a means to implement a swap function, although I am not completely satisfied with that.


Agreed,
but I see no pedagogic wrong with saying:
"Pointers are basically integers holding addresses. They are useful for many very powerful things which we will see in the later lectures. This lecture will be just teaching the basics, which will seem not useful for now".

Students learn tons of seemingly not very useful math before they reach the usefulness of it in advanced classes. Putting off pointers uses for a lecture or two shouldn't kill them.

I think its better to talk about pass-by-reference (in C at least) only after pointers are explained, and better also to talk about arrays (in C again) only after pointers, but I am not an expert in C or teaching.

[Edited by - Iftah on July 28, 2008 3:00:17 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Iftah
Actually I think there is something wrong with your second example also: you are assigning an int into an array of 3 ints.


Entirely correct. This is what I get for coding the "pretty version" and "give me the error to copy and paste" versions seperately <_<.

Originals updated in a moment.

Quote:
As for the 3rd example, it will work if you wrote ptr_to_ptr[0] = (int*)42 right?

In this case, on my platform, yes.
Uhg, I'm really talking out of my ass tonight. On my platform, the 3rd line, with my updated snippet, behaves similarly to:

(*(int*)((*ptr_to_array)[0])) = 42;
or:
(*(int*)(array[0])) = 42;
or:
*((int*)42) = 42;

However, since both invoke undefined behavior, it'd be entirely valid for your compiler to make your program do something different.

Quote:
"Pointers are basically integers holding addresses.

To be anal retentive, far pointers in the x86's 16-bit segmented model mode are a good counterexample of a case where pointers aren't integers at all (but rather a pair of them combined in a bizarre fashion), but I agree with the rest of your point.

[Edited by - MaulingMonkey on July 28, 2008 4:51:15 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
Quote:
As for the 3rd example, it will work if you wrote ptr_to_ptr[0] = (int*)42 right?

In this case, on my platform, yes. However, since both invoke undefined behavior, it'd be entirely valid for your compiler to make your program do something different.

Thanks. The casted version seems right to me, but I trust you its undefined because I was always weak at identifying those.
Quote:
Quote:
"Pointers are basically integers holding addresses.

To be anal retentive, far pointers in the x86's 16-bit segmented model mode are a good counterexample of a case where pointers aren't integers at all (but rather a pair of them combined in a bizarre fashion), but I agree with the rest of your point.
Agreed, there are probably some CPU platforms that implement pointers even wierder, so perhaps that line should say "variables holding address", or some less implementation restrictive statement.

I also oversimplified greatly (too much?) by not mentioning the type of the pointed to data (which is the difference between (Type*) and (void *)), but I wanted to keep the intro sentence short... the rest of the lecture is for the details.

Share this post


Link to post
Share on other sites
Quote:
Original post by DevFred
And almost everyone immediately asks themselves "Why the hell would I want to do that? Why not just i = 5; ?". Poor examples like these are the reason for countless "What's the use of pointers?" threads on gamedev.


I find the scanf function to be an excellent example of why pointers are useful in C.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this