0

If there is an array with size 4, when the value of that array is sent to a function. Why does a warning appear?

#include <stdio.h>
#include <stdbool.h>

void greatestOf(int arr[], int *result) {
    *result = arr[0];

    for (int i = 1; i < 4; i++) {
        if (arr[i] > *result) {
            *result = arr[i];
        }
    }
}


int main() {
    int arr[4], x;
    printf("Input : ");
    scanf("%d %d %d %d", &arr[0], &arr[1], &arr[2], &arr[3]);

    greatestOf(arr[4], &x); // here warning text. When I try to use '''greatest(arr, &x)'''

    printf("Greatest number: %d\n", x);

    return 0;
}

I get the following warning message:

<source>: In function 'main':
<source>:20:19: warning: passing argument 1 of 'greatestOf' makes pointer from integer without a cast [-Wint-conversion]
   20 |     greatestOf(arr[4], &x); // here warning text. When I try to use '''greatest(arr, &x)'''
      |                ~~~^~~
      |                   |
      |                   int
<source>:4:21: note: expected 'int *' but argument is of type 'int'
    4 | void greatestOf(int arr[], int *result) {
      |                 ~~~~^~~~~

I want the warning text to disappear, I think if want to send value to function I have to include the size of the array. Because I want to send the whole value

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • `arr[4]` is a single element of the array. The *fifth* element of your four-element array. What does your learning material say about passing arrays to functions? – Some programmer dude May 28 '23 at 15:21
  • 2
    You should generally pass the length of the array into this function so you can use it internally and also simply return the result from the function e.g. `int greatestOf(int arr[], size_t arr_len)`. – jarmod May 28 '23 at 15:30
  • Instead of making the function `greatestOf` return `void`, you can make it return `int`. That way, only one parameter would be necessary. – Andreas Wenzel May 28 '23 at 15:38

3 Answers3

6

In the function call, arr[4] is an expression that represents the fifth element of arr, not that you're telling the function that arr is four elements long. Instead, you just pass arr. However, that leaves the problem of telling greatestOf how long arr is.

In this case, you know that the array passed to greatestOf will always four elements long, so you can just change the signature of greatestof to

void greatestOf(int arr[4], int *result) 

Here, int arr[4] is a type, not an expression, meaning an array of length 4 with the name arr.

If you didn't know how long arr was, then you've have to pass its length as a separate parameter, like this:

void greatestOf(int arr[], int arr_length, int *result) {
    *result = arr[0];

    for (int i = 1; i < arr_length; i++) {
        if (arr[i] > *result) {
            *result = arr[i];
        }
    }
}

Then call it like this:

greatestOf(arr, 4, &x);
Michael M.
  • 10,486
  • 9
  • 18
  • 34
5

In C, function arguments are usually passed by value. However, it is not possible to directly* (see footnote) pass an array to a function by value. Passing an array to a function is usually done by passing a pointer to the first element of the array. Only this pointer is then passed by value.

However, it is still allowed to specify an array as a function parameter, as you are doing:

void greatestOf( int arr[], int *result )

But when you do this, the array will decay to a pointer, so the declaration is equivalent to the following:

void greatestOf( int *arr, int *result )

Therefore, there is nothing wrong with how you are declaring the function parameters. However, there is something wrong with how you are calling the function. You are calling it like this:

greatestOf(arr[4], &x);

As previously stated, arrays are usually passed to functions by passing a pointer to the first element of the array. Therefore, the correct way would be to call it like this:

greatestOf( &arr[0], &x );

However, it is more common to simply write:

greatestOf( arr, &x );

This is possible because when you pass an array to a function, the array will automatically decay to a pointer to the first element of the array, which is &arr[0].

By writing arr[4] instead, you are passing the value of the (non-existant) 5th element of the array (array indexes are 0-based) to the function. This is wrong, because you need to pass a pointer to the first element.

Note that when declaring an array, you must specify the size of the array in [], but when used outside an expression, the [] has a completely different meaning: It means that you index into the array.

I think if want to send value to function I have to include the size of the array

There is no need to pass the size of the array to the function, if the size of the array is guaranteed to always be 4. However, in that case, I suggest that you change the function declaration

void greatestOf( int arr[], int *result )

to

void greatestOf( int arr[4], int *result )

in order to make clear to anybody reading the code that the function takes a pointer to an array whose length is 4. The 4 will be ignored by the compiler, because, as previously stated, the array declaration will decay to a pointer, in this case int *arr.

You can also write

void greatestOf( int arr[static 4], int *result )

which will tell the compiler that the parameter arr is guaranteed to be a valid pointer (e.g. non-NULL) to an array which is guaranteed to be at least 4 elements long. Doing this may improve performance.

If you want the function greatestOf to also be able to handle arrays that have a size different than 4, you can write the function like this:

int greatestOf( int arr[], size_t length )
{
    int max;

    if ( length == 0 )
    {
        fprintf( stderr, "Invalid array size!\n" );
        exit( EXIT_FAILURE );
    }

    max = arr[0];

    for ( size_t i = 1; i < length; i++ )
    {
        if ( arr[i] > max )
        {
            max = arr[i];
        }
    }

    return max;
}

Note that this code requires you to add #include <stdlib.h> to the top of the source file.

In the function main, instead of calling the function main like this

greatestOf(arr, &x);

you can now call it like this:

x = greatestOf( arr, sizeof arr / sizeof *arr );

The expresson sizeof arr / sizeof *arr is the size (in bytes) of the entire array divided by the size of a single array element. Therefore, this expression will evaluate to the number of elements in the array.


Footnote:

* It is possible to pass an array to a function by value, if it is enclosed in a struct. However, passing arrays by value is usually not recommended, because it creates a copy of the entire array, which can be bad for performance.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • *arr is guaranteed to be a valid pointer (e.g. non-NULL)* ===> Is that due to the `static` keyword? Could you point me to the relevant passage from the standard? This seems new to me. – Harith May 28 '23 at 15:46
  • 1
    @Haris: That is correct. [Here](https://en.cppreference.com/w/c/language/array) is the relevant documentation from cppreference.com. See [§6.7.6.3 ¶7 of the ISO C11 standard](http://port70.net/~nsz/c/c11/n1570.html#6.7.6.3p7) for the normative text. As far as I know, this feature does not exist in C++, though. – Andreas Wenzel May 28 '23 at 15:57
1

Similar constructions used in different contexts can have different meanings.

In this declaration of an array

int arr[4], x;

the construction arr[4] denotes an array with four elements.

In this function call

greatestOf(arr[4], &x);

the construction arr[4] denotes a non-existent scalar element of the type int with the index 4 (the valid range of indices of the array is [0, 3]) of the array arr. But the function expects a pointer of the type int * instead of an object of the type int.

So the compiler issues the warning.

Pay attention to that the compiler adjusts a parameter of an array type to pointer to the array element type. That is this function declaration

void greatestOf(int arr[], int *result);

is adjusted by the compiler to

void greatestOf(int *arr, int *result);

To call the function you need to use the array name like

greatestOf(arr, &x);

In this case the array designator in turn is converted to pointer to its first element. That is this call is equivalent to

greatestOf( &arr[0], &x);

As for your function then it should be declared at least like

void greatestOf( const int arr[], size_t n, int *result);

firstly, the first parameter should be declared with the qualifier const because elements of the array are not changed within the function.

And secondly, you need to pass the number of elements of an array (or a sub-array) to the function instead of using the magic number 4.

Nevertheless even if to declare the function such a way as shown above the function is unsafe. The user can pass value 0 as the second argument. In this case this statement

*result = arr[0];

invokes undefined behavior.

It is much better and safer to declare and define the function the following way.

int * greatestOf( const int arr[], size_t )
{
    const int *greatest = a;

    for ( size_t i = 1; i < n; i++ ) 
    {
        if ( *greatest < a[i] ) greatest = a + i;
    }

    return ( int * )greatest;
}

And the function is called like

int *greatest = greatestOf( arr, 4 );

printf( "Greatest number: %d\n", *greatest );
halfer
  • 19,824
  • 17
  • 99
  • 186
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335