Are C arrays passed by reference?

Started by
43 comments, last by MadKeithV 15 years, 8 months ago
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 behaviorptr_to_array[0] = 42; // This works fine, defined behaviorptr_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]
Advertisement
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]
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]
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.
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.

Quote:Original post by Iftah
Thanks.


I have to apologize, I got things wrong again. Updated post above on the previous page and now quoted bellow below >_<.

I can't even refer to the location of my own posts relative to each other properly >_<

Quote:
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:Original post by MaulingMonkey
On my platform, the 3rd line, with my updated snippet, behaves similarly to:

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

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


Hmmm type punning. That has the nice tendency to break as soon as some compilers (*cough*gcc*cough*) optimize at non-trivial levels. The net result is reading from uninitialized memory at best.
Quote:Original post by DevFred
Quote:Original post by TheTroll
An array in C is really just a pointer to the first value of the array

No.


thanks for the link! I didn't know about that site
http://c-faq.com
Quote:Original post by fpsgamer
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.


The "int vs double" argument is actually quite an unfortunate and wrong example. Ints and doubles have very different layouts in memory, and to convert from one to the other requires actual work.
A pointer-to-int and an array-to-int are nearly indistinguishable in memory except for the level of indirection. The array points to a memory location where integer values are stored, the pointer points to an address that points to a memory location where an integer value is stored. In the pointer case, the application first has to fetch the actual address stored in the pointer, while the array case has the address encoded right into the array variable.

The source example and its disassembly below help to demonstrate that.

Example:
int int_array[4] = { 1, 2, 3, 4};int (*ptr_to_array)[4] = &int_array;int *int_ptr = int_array;int_array[0] = 4;int_array[1] = 3;  // To show the difference between the first and other elements.(*ptr_to_array)[1] = 3;*(int_ptr+2) = 2;int_ptr[3] = 1;


Disassembled example:
; 13   :   int int_array[4] = { 1, 2, 3, 4};	mov	DWORD PTR _int_array$[ebp], 1	mov	DWORD PTR _int_array$[ebp+4], 2	mov	DWORD PTR _int_array$[ebp+8], 3	mov	DWORD PTR _int_array$[ebp+12], 4; 14   :   int (*ptr_to_array)[4] = ∫_array;	lea	eax, DWORD PTR _int_array$[ebp]	mov	DWORD PTR _ptr_to_array$[ebp], eax; 15   :   int *int_ptr = int_array;	lea	eax, DWORD PTR _int_array$[ebp]	mov	DWORD PTR _int_ptr$[ebp], eax; 16   : ; 17   :   int_array[0] = 4;	mov	DWORD PTR _int_array$[ebp], 4; 18   :   int_array[1] = 3;  // To show the difference between the first and other elements.	mov	DWORD PTR _int_array$[ebp+4], 3; 19   :   (*ptr_to_array)[1] = 3;	mov	eax, DWORD PTR _ptr_to_array$[ebp]	mov	DWORD PTR [eax+4], 3; 20   :   *(int_ptr+2) = 2;	mov	eax, DWORD PTR _int_ptr$[ebp]	mov	DWORD PTR [eax+8], 2; 21   :   int_ptr[3] = 1;	mov	eax, DWORD PTR _int_ptr$[ebp]	mov	DWORD PTR [eax+12], 1
It's only funny 'till someone gets hurt.And then it's just hilarious.Unless it's you.
Quote:Original post by MadKeithV
A pointer-to-int and an array-to-int are nearly indistinguishable in memory except for the level of indirection. The array points to a memory location where integer values are stored, the pointer points to an address that points to a memory location where an integer value is stored.


No. The array is a memory location (rather, a sequence of adjacent memory locations) where integer values are stored. The pointer is an address of a memory location where an integer value is stored (with the note that this location might actually be within an array, especially at the beginning). Alternatively said, it "points at" that memory location. There is also no array-to-int; it is an array-of-int.

This topic is closed to new replies.

Advertisement