-2

I'm trying to create dynamically allocated array of clusters, where every cluster has its own dynamically allocated array of points.

typedef struct Point
{
    int ID; //each point has some ID and XY coordinates
    int X;
    int Y;
}Point;

typedef struct Cluster
{
    int size;      //how many points can cluster hold
    int count;     //how many points cluster actually holds
    Point *points; //dynamically allocated array of points
}Cluster;

I am able to initialize clusters array and populate it with this function:

void fill_array(Cluster **arr, int how_many_clusters)
{
    *arr = (Cluster *)malloc(sizeof(Cluster) * how_many_clusters);

    for(int i = 0; i < how_many_clusters; i++)
    {
        Point point;
        point.ID = i;
        point.X = i * 2;
        point.Y = i * 3;
        (*arr)[i].size = 1;
        (*arr)[i].count = 1;
        (*arr)[i].points = (Point *)malloc(sizeof(Point));
        (*arr)[i].points[i] = point;
    }
}

In main I want to create the array, fill it with function mentioned before and then free it.

int main ()
{
    Cluster *clusters;
    int clusters_count = 20;
    fill_array(&cluters, clusters_count);

    for(int i = clusters_count - 1; i >= 0; i--)
    {
        free(clusters[i].points); //crash point
        clusters[i].points = NULL;
    }
    free(clusters);

    return 0;
}

When I compile and run this program, it crashes at second iteration of freeing with segmentation fault. I have spent last 2 days digging through the internet, but didn't find anything about what I'm doing wrong. Can anyone point out where is the mistake I can't see?

  • `(*arr)[i].points[i] = point;` writes out of bounds. you only allocated space for one Point – M.M Dec 07 '17 at 04:48

3 Answers3

1
(*arr)[i].points[i] = point;

This doesn't look right. It should be

(*arr)[i].points[0] = point;
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
1

There is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?. malloc returns void * compatible with any pointer. A cast can only serve to muddy the waters. Simply allocate your pointer points and then call sizeof on the dereferenced pointer, e.g.:

(*arr)[i].points = malloc (sizeof *(*arr)[i].points);

While a correct statement for the allocation, it wholly fails to validate the allocation succeeds, instead, check the return from malloc is not NULL, e.g.

         if (!((*arr)[i].points = malloc (sizeof *(*arr)[i].points))) {
            perror ("malloc failed - points");
            exit (EXIT_FAILURE);
        }

The same for the allocation of *arr, e.g.

    if (!(*arr = malloc (sizeof **arr * nclusters))) {
        perror ("malloc failed - nclusters");
        exit (EXIT_FAILURE);
    }

(note: how_many_clusters was shortened to nclusters, we are not writing a novel)

Your primary problem is:

        (*arr)[i].points[i] = point;

You only allocate memory for one Point struct, therefore points[i] makes no sense at all. You allocated sizeof(Point) -- how many of them do you think there are?

Since your goal is to copy point to the newly allocated block of memory at the address held by the pointer (*arr)[i].points, "How Do You Access (or Set) the value Referenced (e.g. Pointed-to) by a Pointer?" (Answer:, you dereference the pointer to obtain the value).

That's all you need do here. No additional [..] tacked on the end to dereference (*arr)[i].points, just dereference it with the unary '*', e.g.

        *(*arr)[i].points = point;  /* copy struct to allocated memory */

Putting the pieces altogether, you can do something like the following:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int ID; //each point has some ID and XY coordinates
    int X;
    int Y;
} Point;

typedef struct {
    int size;      //how many points can cluster hold
    int count;     //how many points cluster actually holds
    Point *points; //dynamically allocated array of points
} Cluster;

void fill_array (Cluster **arr, int nclusters)
{
    if (!(*arr = malloc (sizeof **arr * nclusters))) {
        perror ("malloc failed - nclusters");
        exit (EXIT_FAILURE);
    }
    for (int i = 0; i < nclusters; i++)
    {
        Point point;
        point.ID = i;
        point.X = i * 2;
        point.Y = i * 3;
        (*arr)[i].size = 1;
        (*arr)[i].count = 1;
        if (!((*arr)[i].points = malloc (sizeof *(*arr)[i].points))) {
            perror ("malloc failed - points");
            exit (EXIT_FAILURE);
        }
        *(*arr)[i].points = point;  /* copy struct to allocated memory */
    }
}

int main (void) {

    Cluster *clusters;
    int clusters_count = 20;

    fill_array (&clusters, clusters_count);

    for(int i = 0; i < clusters_count; i++)
    {
        free(clusters[i].points); //crash point
        clusters[i].points = NULL;
    }
    free (clusters);

    return 0;
}

(note: you do not have to free each clusters[i].points in reverse order. (it's not wrong, there is just no need to go out of your way to do it). Just free them as you work your way through the loop.)

Example Use/Validation

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/fillclusters_alloc
==26217== Memcheck, a memory error detector
==26217== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26217== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==26217== Command: ./bin/fillclusters_alloc
==26217==
==26217==
==26217== HEAP SUMMARY:
==26217==     in use at exit: 0 bytes in 0 blocks
==26217==   total heap usage: 21 allocs, 21 frees, 560 bytes allocated
==26217==
==26217== All heap blocks were freed -- no leaks are possible
==26217==
==26217== For counts of detected and suppressed errors, rerun with: -v
==26217== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

The following bit:

    (*arr)[i].points = (Point *)malloc(sizeof(Point));
    (*arr)[i].points[i] = point;

does not allocate the correct amount of memory. i will be some number greater than zero, but you've only allocated space for one Point. You're smashing your heap and a crash will follow.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285