开发者

when do we need to pass the size of array as a parameter

开发者 https://www.devze.com 2023-03-19 07:34 出处:网络
I am a little bit confused about pass an array in C/C++. I saw some cases in which the 开发者_Go百科signature is like this

I am a little bit confused about pass an array in C/C++. I saw some cases in which the 开发者_Go百科signature is like this

void f(int arr[])

some is like this

void f(int arr[], int size)

Could anybody elaborate what's the difference and when and how to use it?


First, an array passed to a function actually passes a pointer to the first element of the array, e.g., if you have

int a[] = { 1, 2, 3 };
f(a);

Then, f() gets &a[0] passed to it. So, when writing your function prototypes, the following are equivalent:

void f(int arr[]);
void f(int *arr);

This means that the size of the array is lost, and f(), in general, can't determine the size. (This is the reason I prefer void f(int *arr) form over void f(int arr[]).)

There are two cases where f() doesn't need the information, and in those two cases, it is OK to not have an extra parameter to it.

First, there is some special, agreed value in arr that both the caller and f() take to mean "the end". For example, one can agree that a value 0 means "Done".

Then one could write:

int a[] = { 1, 2, 3, 0 }; /* make sure there is a 0 at the end */
int result = f(a);

and define f() something like:

int f(int *a)
{
    size_t i;
    int result = 0;
    for (i=0; a[i]; ++i)  /* loop until we see a 0 */
        result += a[i];
    return result;
}

Obviously, the above scheme works only if both the caller and the callee agree to a convention, and follow it. An example is strlen() function in the C library. It calculates the length of a string by finding a 0. If you pass it something that doesn't have a 0 at the end, all bets are off, and you are in the undefined behavior territory.

The second case is when you don't really have an array. In this case, f() takes a pointer to an object (int in your example). So:

int change_me = 10;
f(&change_me);
printf("%d\n", change_me);

with

void f(int *a)
{
    *a = 42;
}

is fine: f() is not operating on an array anyway.


WHen an array is passed in C or C++ only its address is passed. That is why the second case is quite common, where the second parameter is the number of elements in the array. The function has no idea, only by looking at the address of the array, how many elements it is supposed to contain.


you can write

void f( int *arr, int size )

as well, having latter (size) allows to not step outside the array boundaries while reading/writing to it


C and C++ are not the same thing. They have some common subset, though. What you observed here is that the "first" array dimension when passed to a function always results just in a pointer being passed. The "signature" (C doesn't use this term) of a function declared as

void toto(double A[23]);

is always just

void toto(double *A);

That is that the 23 above is somewhat redundant and not used by the compiler. Modern C (aka C99) has an extension here that lets you declare that A always has 23 elements:

void toto(double A[static 23]);

or that the pointer is const qualified

 void toto(double A[const 23]);

If you add other dimension the picture changes, then the array size is used:

void toto(double A[23][7]);

in both C and C++ is

void toto(double (*A)[7]);

that is a pointer to an array of 7 elements. In C++ these array bounds must be an integer constant. In C it can be dynamic.

void toto(size_t n, size_t m, double A[n][m]);

They only thing that you have to watch here is that here n and m come before A in the parameter list. So better you always declare functions with the parameters in that order.


The first signature just passes the array with no way to tell how big the array is and can lead to problems with out-of-bounds errors and/or security flaws.\

The second signature is a more secure version because it allows the function to check against the size of the array to prevent the first versions shortcomings.

Unless this is homework, raw arrays are a bit out-dated. Use std::vector instead. It allows passing the vector around without having to manually pass the size as it does this for you.


The size of an array is not passed with the array itself. Therefore, if the other function needs the size, it will have it as a parameter.

The thing is, some functions implicitly understand the array to be of a certain size. So they won't need to have it specified explicitly. For example, if your function operates on an array of 3 floats, you don't need the user to tell you that it is an array of 3 floats. Just take an array.

And then there are those functions (let's call them "terrible" because they are) that will fill an array in with arbitrary data up to a point defined by that data. sprintf is probably the "best" example. It will keep putting characters in that array until it is finished writing them. That's very bad, because there's no explicit or implicit agreement between the user and the function as to how big this array could be. sprintf will write some number of characters, but there's no way for the user to know exactly how many get written (in the general case).

Which is why you should never use sprintf; use snprintf or _snprintf, depending on your compiler.


Anytime you need to know the size of the array, it needs to be provided. There is nothing special about the two forms of passing the array itself; the first parameter is the same either way. The second method simply provides the information needed to know the size of the array while the first does not.

Sometimes the array itself holds the information about its size, though. In your first example, for instance, perhaps arr[0] is set to the size of the array, and the actual data begins at arr[1]. Or consider the case of c-strings... you provide just a char[], and the array is assumed to end at the first element equal to \0. In your example, a negative value may act as a similar sentinel. Or perhaps the function simply doesn't care about the array's size, and will simply assume it is large enough.

Such methods are inherently unsafe, though... it is easy to forget to set arr[0] or to accidently overwrite the null terminator. Then, f suddenly has no way of knowing how much space it has available to it. Always prefer to provide the size explicitly, either via a size parameter like you show, or with a second pointer to the end of the array. The latter is the method generally taken by the standard library functions in C++. You still have the issue of providing an incorrect size, though, which is why in C++ it isn't recommended you ever use such an array in the first place... use an actual container that will keep track of that information for you.


The difference is that the second one includes a parameter that indicates the array size. The logical conclusion is that if you don't use such a parameter, the function doesn't know what the array size is. And this indeed turns out to be the case. In fact, it doesn't know you have an array. In fact, you don't have to have an array to call the function.

The array syntax here, without a specified size inside the square brackets, is a fake-out. The parameter is actually a pointer. For more information, please see http://c-faq.com/aryptr/index.html , especially section 4.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号