Passing an array as an argument to a function in C

后端 未结 10 1197
太阳男子
太阳男子 2020-11-22 06:03

I wrote a function containing array as argument, and call it by passing value of array as follows.

void arraytest(int a[])
{
    // changed the array a
    a         


        
相关标签:
10条回答
  • 2020-11-22 06:15

    You are passing the value of the memory location of the first member of the array.

    Therefore when you start modifying the array inside the function, you are modifying the original array.

    Remember that a[1] is *(a+1).

    0 讨论(0)
  • 2020-11-22 06:19

    Arrays in C are converted, in most of the cases, to a pointer to the first element of the array itself. And more in detail arrays passed into functions are always converted into pointers.

    Here a quote from K&R2nd:

    When an array name is passed to a function, what is passed is the location of the initial element. Within the called function, this argument is a local variable, and so an array name parameter is a pointer, that is, a variable containing an address.

    Writing:

    void arraytest(int a[])
    

    has the same meaning as writing:

    void arraytest(int *a)
    

    So despite you are not writing it explicitly it is as you are passing a pointer and so you are modifying the values in the main.

    For more I really suggest reading this.

    Moreover, you can find other answers on SO here

    0 讨论(0)
  • 2020-11-22 06:21

    If you want to pass a single-dimension array as an argument in a function, you would have to declare a formal parameter in one of following three ways and all three declaration methods produce similar results because each tells the compiler that an integer pointer is going to be received.

    int func(int arr[], ...){
        .
        .
        .
    }
    
    int func(int arr[SIZE], ...){
        .
        .
        .
    }
    
    int func(int* arr, ...){
        .
        .
        .
    }
    

    So, you are modifying the original values.

    Thanks !!!

    0 讨论(0)
  • 2020-11-22 06:21

    Arrays are always passed by reference if you use a[] or *a:

    int* printSquares(int a[], int size, int e[]) {   
        for(int i = 0; i < size; i++) {
            e[i] = i * i;
        }
        return e;
    }
    
    int* printSquares(int *a, int size, int e[]) {
        for(int i = 0; i < size; i++) {
            e[i] = i * i;
        }
        return e;
    }
    
    0 讨论(0)
  • 2020-11-22 06:26

    In C, except for a few special cases, an array reference always "decays" to a pointer to the first element of the array. Therefore, it isn't possible to pass an array "by value". An array in a function call will be passed to the function as a pointer, which is analogous to passing the array by reference.

    EDIT: There are three such special cases where an array does not decay to a pointer to it's first element:

    1. sizeof a is not the same as sizeof (&a[0]).
    2. &a is not the same as &(&a[0]) (and not quite the same as &a[0]).
    3. char b[] = "foo" is not the same as char b[] = &("foo").
    0 讨论(0)
  • 2020-11-22 06:31

    Standard array usage in C with natural type decay from array to ptr

    @Bo Persson correctly states in his great answer here:

    When passing an array as a parameter, this

    void arraytest(int a[])
    

    means exactly the same as

    void arraytest(int *a)
    

    However, let me add also that the above two forms also:

    1. mean exactly the same as

       void arraytest(int a[0])
      
    2. which means exactly the same as

       void arraytest(int a[1])
      
    3. which means exactly the same as

       void arraytest(int a[2])
      
    4. which means exactly the same as

       void arraytest(int a[1000])
      
    5. etc.

    In every single one of the array examples above, the input parameter type decays to an int *, and can be called with no warnings and no errors, even with build options -Wall -Wextra -Werror turned on (see my repo here for details on these 3 build options), like this:

    int array1[2];
    int * array2 = array1;
    
    // works fine because `array1` automatically decays from an array type
    // to `int *`
    arraytest(array1);
    // works fine because `array2` is already an `int *` 
    arraytest(array2);
    

    As a matter of fact, the "size" value ([0], [1], [2], [1000], etc.) inside the array parameter here is apparently just for aesthetic/self-documentation purposes, and can be any positive integer (size_t type I think) you want!

    In practice, however, you should use it to specify the minimum size of the array you expect the function to receive, so that when writing code it's easy for you to track and verify. The MISRA-C-2012 standard (buy/download the 236-pg 2012-version PDF of the standard for £15.00 here) goes so far as to state (emphasis added):

    Rule 17.5 The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements.

    ...

    If a parameter is declared as an array with a specified size, the corresponding argument in each function call should point into an object that has at least as many elements as the array.

    ...

    The use of an array declarator for a function parameter specifies the function interface more clearly than using a pointer. The minimum number of elements expected by the function is explicitly stated, whereas this is not possible with a pointer.

    In other words, they recommend using the explicit size format, even though the C standard technically doesn't enforce it--it at least helps clarify to you as a developer, and to others using the code, what size array the function is expecting you to pass in.


    Forcing type safety on arrays in C

    As @Winger Sendon points out in a comment below my answer, we can force C to treat an array type to be different based on the array size!

    First, you must recognize that in my example just above, using the int array1[2]; like this: arraytest(array1); causes array1 to automatically decay into an int *. HOWEVER, if you take the address of array1 instead and call arraytest(&array1), you get completely different behavior! Now, it does NOT decay into an int *! Instead, the type of &array1 is int (*)[2], which means "pointer to an array of size 2 of int", or "pointer to an array of size 2 of type int". So, you can FORCE C to check for type safety on an array, like this:

    void arraytest(int (*a)[2])
    {
        // my function here
    }
    

    This syntax is hard to read, but similar to that of a function pointer. The online tool, cdecl, tells us that int (*a)[2] means: "declare a as pointer to array 2 of int" (pointer to array of 2 ints). Do NOT confuse this with the version withOUT parenthesis: int * a[2], which means: "declare a as array 2 of pointer to int" (array of 2 pointers to int).

    Now, this function REQUIRES you to call it with the address operator (&) like this, using as an input parameter a POINTER TO AN ARRAY OF THE CORRECT SIZE!:

    int array1[2];
    
    // ok, since the type of `array1` is `int (*)[2]` (ptr to array of 
    // 2 ints)
    arraytest(&array1); // you must use the & operator here to prevent
                        // `array1` from otherwise automatically decaying
                        // into `int *`, which is the WRONG input type here!
    

    This, however, will produce a warning:

    int array1[2];
    
    // WARNING! Wrong type since the type of `array1` decays to `int *`:
    //      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from 
    //      incompatible pointer type [-Wincompatible-pointer-types]                                                            
    //      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
    arraytest(array1); // (missing & operator)
    

    You may test this code here.

    To force the C compiler to turn this warning into an error, so that you MUST always call arraytest(&array1); using only an input array of the corrrect size and type (int array1[2]; in this case), add -Werror to your build options. If running the test code above on onlinegdb.com, do this by clicking the gear icon in the top-right and click on "Extra Compiler Flags" to type this option in. Now, this warning:

    main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]                                                            
    main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’    
    

    will turn into this build error:

    main.c: In function ‘main’:
    main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
         arraytest(array1); // warning!
                   ^~~~~~
    main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
     void arraytest(int (*a)[2])
          ^~~~~~~~~
    cc1: all warnings being treated as errors
    

    Note that you can also create "type safe" pointers to arrays of a given size, like this:

    int array[2];
    // "type safe" ptr to array of size 2 of int:
    int (*array_p)[2] = &array;
    

    ...but I do not necessarily recommend this, as it reminds me a lot of the C++ antics used to force type safety everywhere, at the exceptionally high cost of language syntax complexity, verbosity, and difficulty architecting code, and which I dislike and have ranted about many times before (ex: see "My Thoughts on C++" here).


    For additional tests and experimentation, see also the link just below.

    References

    See links above. Also:

    1. My code experimentation online: https://onlinegdb.com/B1RsrBDFD
    0 讨论(0)
提交回复
热议问题