What does it mean to have array with size in function parameters
I saw a function that looked something like this
int foo(int array[100])
{
...
}
how is this 开发者_C百科different from
int foo(int *array)
Is it different?
In what places should/can we use the former method.
They are functionally the same. You should use the second method and pass a parameter for the length of the array.
Otherwise, you are asking for trouble:
// this will compile without warning, even if using -Wall
int myArray[50] = {0};
foo(myArray);
If foo()
assumes that the array is actually 100 elements long, it will overrun the array.
Better way:
int foo(int *array, size_t array_len) {
// do stuff
}
Even better, use a vector
, which carries its size with it and you cannot (under normal circumstances) access beyond the end of the vector:
int foo(const std::vector<int>& array) {
// do stuff
}
In C++, you cannot pass an array as an argument to a function. A function declaration that exhibits a parameter of type array of T
is converted, according to §8.3.5 to pointer to T
. This means that the following declarations are exactly equivalent:
void f( int a[10] );
void f( int a[] );
void f( int *a );
So in fact, as you point out they are exactly equivalent for the compiler even if the first one might be misleading to developers reading the code, as the given size in the declaration will not be enforced.
This is different to function parameters that are of type reference to array of T
, where the argument does not decay to a pointer, but rather keeps the full type:
void f( int (&a)[10] ); // takes an array of exactly 10 integers
In this case, the compiler will actually enforce the type of the reference, which is array of 10 int
(including size). The code inside the function can assume that there will always be 10 elements, and the compiler will ensure that.
§8.3.5 [dcl.fct] /3
[...]After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively.[...]
Nothing, they work in the same way. Here a short example:
int WithArray(int array[10])
{
return array[1] + array[2]; // breakpoint 1
}
int WithPointer(int *pointer)
{
return *(pointer + 1) + *(pointer + 2); // breakpoint 2
}
void main()
{
int array[] = {0,1,2,3,4,5,6,7,8,9};
int b = WithPointer(array);
int a = WithArray(array);
printf("a = %d\nb = %d\n", a, b);
}
Ok, I'll call WithPointer() first, just in case WIthArray() copies the array on the stack. Here's the stack at the second breakpoint:
Breakpoint 2, WithPointer (pointer=0xbffff418) at prova.c:10
10 return *(pointer + 1) + *(pointer + 2);
(gdb) x/20x ($esp - 8)
0xbffff404: 0x08049ff4 0xbffff418 0xbffff448 0x0804843b
0xbffff414: 0xbffff418 0x00000000 0x00000001 0x00000002
0xbffff424: 0x00000003 0x00000004 0x00000005 0x00000006
0xbffff434: 0x00000007 0x00000008 0x00000009 0x08048460
0xbffff444: 0x00000000 0xbffff4c8 0x00144bd6 0x00000001
As expected, there's our pointer (0xbffff418, the first value on the second line) and, right after that, array[] (which is on main()'s stack frame). Let's check the stack inside WithArray():
(gdb) continue
Continuing.
Breakpoint 1, WithArray (array=0xbffff418) at prova.c:5
5 return array[1] + array[2];
(gdb) x/20x ($esp - 8)
0xbffff404: 0x08049ff4 0xbffff418 0xbffff448 0x08048449
0xbffff414: 0xbffff418 0x00000000 0x00000001 0x00000002
0xbffff424: 0x00000003 0x00000004 0x00000005 0x00000006
0xbffff434: 0x00000007 0x00000008 0x00000009 0x08048460
0xbffff444: 0x00000003 0xbffff4c8 0x00144bd6 0x00000001
Exactly the same thing! So there's no difference about how they're passed to functions. And they're handled in the same way too, look:
(gdb) disass WithPointer
Dump of assembler code for function WithPointer:
0x080483cc <+0>: push %ebp
0x080483cd <+1>: mov %esp,%ebp
0x080483cf <+3>: mov 0x8(%ebp),%eax # get base address
0x080483d2 <+6>: add $0x4,%eax # compute offset
0x080483d5 <+9>: mov (%eax),%edx # dereference and get val.
0x080483d7 <+11>: mov 0x8(%ebp),%eax # base address
0x080483da <+14>: add $0x8,%eax # offset (2 * sizeof(int))
0x080483dd <+17>: mov (%eax),%eax # get *eax
0x080483df <+19>: lea (%edx,%eax,1),%eax # tricky way to add them
0x080483e2 <+22>: pop %ebp
0x080483e3 <+23>: ret
End of assembler dump.
(gdb) disass WithArray
Dump of assembler code for function WithArray:
0x080483b4 <+0>: push %ebp
0x080483b5 <+1>: mov %esp,%ebp
0x080483b7 <+3>: mov 0x8(%ebp),%eax # first element of array
0x080483ba <+6>: add $0x4,%eax # move to the second
0x080483bd <+9>: mov (%eax),%edx # and get its value
0x080483bf <+11>: mov 0x8(%ebp),%eax # base of array
0x080483c2 <+14>: add $0x8,%eax # compute address of second
0x080483c5 <+17>: mov (%eax),%eax # element and get load it
0x080483c7 <+19>: lea (%edx,%eax,1),%eax # compute sum
0x080483ca <+22>: pop %ebp
0x080483cb <+23>: ret
End of assembler dump.
Codes are identical. Note that the array is handled as a pointer.
No difference with this declaration
int foo(int array[100]) //1
int foo(int array[]) //2
int foo(int *array) //3
If function can take only fixed sized array, in this case 100 elements, 1 version are more clear to programer which use this function. In all other cases - 3 are good choice
精彩评论