2

Recently was handling with this exercise:

Count the size of each element in an array. Create a function my_count_on_it, which receives a string array as a parameter and returns an array with the length of each string.

Structures:

typedef struct s_string_array {
    int size;
    char **array;
} string_array;

typedef struct s_integer_array {
    int size;
    int *array;
} integer_array;

So the right answer was:

#include <string.h>

integer_array *my_count_on_it(string_array *a) {
    integer_array *ptr;

    ptr = (integer_array *)malloc(sizeof(integer_array));
    ptr->size = a->size;
    ptr->array = (int*)malloc(ptr->size * sizeof(int));

    for (int i = 0; i < a->size; i++) {
        ptr->array[i] = strlen(a->array[I]);
    }

    return ptr;
}

I know that pointer stores address of some variable, but I can't understand why in this case I can't create a variable type of integer_array and do all operations(in the loop) with it, and only then store its address in integer_array * ptr? and one more question, if so why we are creating a pointer sizeof(integer_array)?

For instance:

int variable = 5;
int *ptr = &variable;

In this simple case, I create variable and pointer separately and store the address. The confusing thing in the first code is it seems to me like we are creating only a pointer, which is not pointing to some address.

chqrlie
  • 131,814
  • 10
  • 121
  • 189

1 Answers1

3

why in this case I can't create a variable type of integer_array and do all operations(in the loop) with it, and only then store its address in integer_array * ptr?

That would be invalid. If you create a local variable, like this:

integer_array* my_count_on_it(string_array* a) {
    integer_array local_arr;

    // ...

    return &local_arr;
}

Then you cannot return a pointer to it, because that variable is only valid until the function in which was defined returns. It's on the stack of the function, and the stack of the function becomes invalid when the function returns. Returning something like &local_arr is therefore wrong, because it refers to some memory that should not be used after returning from the function (it is also undefined behavior). The correct way to do this is to use malloc(), which allocates memory on the heap, which can be used anywhere else in the program and does not get automatically freed (you have to do it manually using free()).

if so why we are creating a pointer sizeof(integer_array)?

That's simply because you want to allocate enough space for the struct integer_array that you just defined above with that typedef. That's not the size of the pointer itself, but the size of the block of memory which that pointer points to.

The confusing thing in the first code is it seems to me like we are creating only a pointer, which is not pointing to some address.

Initially, when declared, the pointer is not pointing to something valid. Then, after you do this:

ptr = malloc(sizeof(integer_array));

The pointer now points to some address in the heap which is valid. At that address, malloc() reserved enough space to hold a variable of the size of your integer_array structure.

=== Before malloc(): =========================

ptr = ????? -------> INVALID

=== After malloc(): ==========================

                      Memory
                      |   ...         | ...
ptr = 0x120 ------->  +---------------+ 0x120
                      | space for     | 
                      | integer_array |
                      +---------------+ 0x128
                      |   ...         | ...

One more thing: casting the return value of malloc() is unnecessary and wrong, see here for an explanation.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • A local variable is not valid only in the function where it is declared. You can pass its address to other functions, and it is valid in those functions too. The object has *automatic storage duration*. It exists until execution of its associated block ends, and it may be used by any function anywhere as long as it exists. The proper concepts to use here are *scope* (where an identifier is visible) and *storage duration* or *lifetime* (when during program execution an object exists in the C model of computing). Calling something a “local variable” muddles the underlying concepts. – Eric Postpischil Apr 22 '20 at 12:26