cdes58042169
cdes58042169

Reputation: 95

passing structure array to function

I've been learning C for about 2 months, still a novice:(

I know there are other similar questions on this site. I've read them, but still couldn't really understand, so here I am. Below is my code:

//naming my structure as ball
typedef struct ball_room {
int enter;
int exit;
} ball;

//I've omitted some irrelevant details for brevity
    int i, n, max;
    scanf("%d", &n);
    ball person[n];
    .../*assign values to ball.enter and ball.exit with user input*/
    max = 1;
    for (i = 0; i < n; i++)
        if (ball_room(person, person[i].enter, n) > max)
            max = ball_room(person, person[i].enter, n);
    printf("%d\n", max);
    return 0;
}

and below is my function receiving the array:

//This function returns the number of people in the ballroom
//when b[j](person[j] in "main") enters
int ball_room(ball *b, int i, int n)
{
    int people = 0, j;
    for (j = 0; j < n; j++)
        if (b[j].enter <= i && b[j].exit > i)
            people++;
    return people;
}

my question is that why is it b[j].enter instead of b[j]->enter, which my compiler wouldn't accept? In my limited experience, when manipulating structure itself (the object), I use . to go inside the structure, and when it's a pointer (the address), I use -> (hope this is correct.)

And in this case, I pass the array to function using ball *b, which represent the address of person[0], so I can access the whole array. But shouldn't ball *b be in the form of a pointer and therefore I should use -> to access its content? It's just an address that I pass to the function.

This is my first time doing something with an array of structures, please help me get this clear, thank you!

Upvotes: 1

Views: 2582

Answers (5)

Aconcagua
Aconcagua

Reputation: 25536

For explanation of the current issue, see Eric's answer; in some of the answers given so far there is dangerous wording applied, so just to make clear: When do we have an array and when a pointer???

Consider the following:

int a[7];

As long as we can refer to a directly, we still have an array and can use any operations that are valid on, e. g. getting size:

size_t n = sizeof(a); // size in bytes, not ints, i. e. 7*sizeof(int)

You can pass arrays to functions or even do pointer arithmetics on:

f(a);
a + 1;

In both cases, the array "decays" to a pointer, though, and the result is a pointer as well. Be aware that you can assign new values to a pointer, but not to an array itself (you can assign new values to the array's elements, directly or via pointer), so you cannot do things like ++a either.

When an array decays to a pointer, it gets a pointer to its first element:

int* ptr = a;
int* ptr = &*a;    // only pointers can be dereferenced -> a decays!
int* ptr = &a[0];  // short for &*(a + 0)...

All result in exactly the same; however, the following is invalid:

int* ptr = &a;

Taking the address of an entire array actually is possible, but the resulting pointer is not of type "pointer to element" nor of type "pointer to pointer to element" (int** in the example), but of type "pointer to array of specific size". Syntax for is ugly, though, but the following would be legal again:

int(*aptr)[7] = &a;

You need to read: if I dereference ptr, I get int[7]...

Once decayed, there is only a pointer to the array left (more precisely: to one of the array elements, directly after decaying, to the first; array and first element always share the same address, so, although of different type, both pointers ptr and aptr from above hold exactly the same value). Pointers can be moved around within the array, but they do not hold as much information as the array itself, especially, the array size gets lost. This is why one needs to pass the array's length together with the pointer to functions (if needed; another variant is a sentinel value denoting the array end such as the terminating null character in strings or the null pointer following the string arguments in main's arguments list):

int a[7];
f(a, sizeof(a)/sizeof(*a)); // division: sizeof is in bytes, dividing by size
                            // of first element gives number of elements

Possibly with f as:

void f(int b[], size_t n)
//     ^^^^^^^ in function parameter lists, just alternative syntax for int* b !!!
//             however, we can show more explicitly that we expect a pointer
//             to an array this way...   
{
    size_t m = sizeof(b); // as b is a POINTER, gives the constant (but hardware specific!)
                          // size of a pointer (on typical modern 64-bit hardware 8 bytes),
                          // no matter what size of the array being pointed to is!!!

    while(n)
    {
        *b++ = n--;
    //    ^^ advances pointer, NOT array!
    }
}

Hope this helps to avoid confusion.

Upvotes: 2

fnisi
fnisi

Reputation: 1233

In C, the array name is a pointer to array’s first element, hence your function declaration has name ball *band works when you pass a ball[] instance.

Try dynamically allocating the memory by using malloc() and passing that pointer to your function.

Upvotes: 0

Eric Postpischil
Eric Postpischil

Reputation: 224311

Given ball *b, b[j] is an element from the elements that b points to. Thus b[j] is not a pointer; it is a struct. Since it is a struct, you use . to refer to members in it.

The definition of b[j] in the C standard is that it is *((b)+(j)). So it takes the pointer b, moves j elements beyond it, and then applies *.

Since * is already applied in b[j], you do not need ->, just ..

Upvotes: 6

bestestefan
bestestefan

Reputation: 871

you use . instead of -> because of this declaration of parameters:

int ball_room(ball *b, int i, int n)

b is expected to be pointer to data with type ball, so you can access it in various ways:

  1. array way: e.g. b[5].somefield = 15 - you use dot here, because if b is of type ball *, it means that b is pointer OR it is array of objects with type b, if it's array of objects with type b (which is your case) you use . to access fields of object
  2. pointer way: e.g. (b+5)->somefield = 15 - it will do exactly same thing as code above, but you will access data in pointer way

Upvotes: 5

Goswin von Brederlow
Goswin von Brederlow

Reputation: 12362

In C/C++ an array devolves into the address of it's first member. So when you pass the array to ball_room what actually gets passed is &ball[0].

Now inside ball_room the reverse happens. b is a pointer to ball. But here you use it as an array b[j]. So it un-devolves back into an array of structs. So what b[j] gives you is the struct and not a pointer to a struct. Consequently you access it using . instead of ->.

You can also use (b + j)->somefield. Or for even more fun how about writing j[b].somefield. The later being a really confusing left-over from the eraly compiler days when a[b] truly got turned into *(a + b) internally.

Upvotes: 2

Related Questions