I have point.h
and polygon.h
files, with their associated .c
files. In point.h
// point.h
#ifndef POINT_H
#define POINT_H
typedef struct Point point;
point *point_alloc(void);
void *point_free(point *);
void *point_init(point *, float, float);
void *point_print(const point *);
float point_get_x(const point *);
float point_get_y(const point *);
void *point_set_x(point *, float);
void *point_set_y(point *, float);
#endif
In point.c
, I have
// point.c
#include <stdlib.h>
#include <stdio.h>
#include "point.h"
#define GET_VALUE_ERROR (2L)
struct Point
{
float x;
float y;
};
point *point_alloc(void)
{
point *pt = malloc(sizeof *pt);
if(NULL == pt)
{
fprintf(stderr, "Could not allocate point.\n");
return NULL;
}
else
{
return pt;
}
}
void *point_free(point *pt)
{
if(NULL == pt)
{
fprintf(stderr, "Could not free point.\n");
return NULL;
}
else
{
free(pt);
pt = NULL;
return NULL;
}
}
void *point_init(point *pt, float x, float y)
{
if(NULL == pt)
{
fprintf(stderr, "Cannot initiate point.\n");
return NULL;
}
else
{
pt -> x = x;
pt -> y = y;
return NULL;
}
}
void *point_print(const point *pt)
{
if(NULL == pt)
{
fprintf(stderr, "Cannot print point.\n");
return NULL;
}
else
{
printf("Point at (%f, %f)\n", pt -> x, pt -> y);
return NULL;
}
}
float point_get_x(const point *pt)
{
if(NULL == pt)
{
fprintf(stderr, "Cannot get point.\n");
return GET_VALUE_ERROR;
}
else
{
return pt -> x;
}
}
float point_get_y(const point *pt)
{
if(NULL == pt)
{
fprintf(stderr, "Cannot get point.\n");
return GET_VALUE_ERROR;
}
else
{
return pt -> y;
}
}
void *point_set_x(point *pt, float x)
{
if(NULL == pt)
{
fprintf(stderr, "Cannot get point.\n");
return NULL;
}
else
{
pt -> x = x;
return NULL;
}
}
void *point_set_y(point *pt, float y)
{
if(NULL == pt)
{
fprintf(stderr, "Cannot get point.\n");
return NULL;
}
else
{
pt -> y = y;
return NULL;
}
}
while in polygon.h
I have
// polygon.h
#ifndef POLYGON_H
#define POLYGON_H
typedef struct Polygon polygon;
polygon *polygon_alloc(unsigned);
void *polygon_free(polygon *);
void *polygon_init(polygon *, unsigned, float, point *);
void *polygon_print(const polygon *);
unsigned polygon_get_nside(const polygon *);
void *polygon_set_nside(polygon *, unsigned);
point *polygon_get_centre(const polygon *);
void *polygon_set_centre(polygon *, point *);
point *polygon_get_vertex(const polygon *, unsigned);
void *polygon_set_vertex(polygon *, unsigned, float, float);
#endif
and in polygon.c
I have
// polygon.c
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "point.h"
#include "polygon.h"
#ifndef M_PI
#define M_PI (3.14159265358979323846264338327950288)
#endif
#define GET_NSIDE_ERROR (2U)
struct Polygon
{
unsigned nside;
point *centre;
point **vertices;
};
polygon *polygon_alloc(unsigned nside)
{
if(nside == 0)
{
fprintf(stderr, "Cannot have a 0-sided polygon.\n");
return NULL;
}
else
{
polygon *poly = malloc(sizeof(*poly));
if(NULL == poly)
{
fprintf(stderr, "Cannot allocate polygon.\n");
return NULL;
}
else
{
poly -> nside = nside;
poly -> centre = point_alloc();
if(NULL == poly -> centre)
{
fprintf(stderr, "Cannot allocate polygon centre.\n");
free(poly);
poly = NULL;
return NULL;
}
else
{
poly -> vertices = malloc(nside * sizeof(point*));
if(NULL == poly -> vertices)
{
fprintf(stderr, "Cannot allocate polygon vertices.\n");
free(poly -> centre);
poly -> centre = NULL;
free(poly);
poly = NULL;
return NULL;
}
else
{
for(unsigned i = 0; i < nside; i++)
{
poly -> vertices[i] = point_alloc();
if(NULL == poly -> vertices[i])
{
fprintf(stderr, "Cannot allocate node %u.\n", i);
for(unsigned j = 0; j < i; j++)
{
free(poly -> vertices[j]);
poly -> vertices[j] = NULL;
}
free(poly -> centre);
poly -> centre = NULL;
free(poly -> vertices);
poly -> vertices = NULL;
free(poly);
poly = NULL;
}
}
}
}
}
return poly;
}
}
void *polygon_free(polygon *poly)
{
if(NULL == poly)
{
fprintf(stderr, "Cannot free polygon.\n");
return NULL;
}
else
{
if(NULL == poly -> centre)
{
fprintf(stderr, "Cannot free polygon centre.\n");
return NULL;
}
else
{
free(poly -> centre);
poly -> centre = NULL;
if(NULL == poly -> vertices)
{
fprintf(stderr, "Cannot free polygon vertices.\n");
return NULL;
}
else
{
for(unsigned i = 0; i < poly -> nside; i++)
{
if(NULL == poly -> vertices[i])
{
fprintf(stderr, "Cannot free vertex %u.\n", i);
return NULL;
}
else
{
free(poly -> vertices[i]);
poly -> vertices[i] = NULL;
}
}
free(poly -> vertices);
poly -> vertices = NULL;
}
}
free(poly);
poly = NULL;
}
return NULL;
}
void *polygon_init(polygon *poly, unsigned nside, float radius, point *centre)
{
if(NULL == poly)
{
fprintf(stderr, "Polygon not existing.\n");
return NULL;
}
else if(NULL == centre)
{
fprintf(stderr, "Polygon centre not existing.\n");
return NULL;
}
else
{
polygon_set_nside(poly, nside);
polygon_set_centre(poly, centre);
for(unsigned i = 0; i < poly -> nside; i++)
{
float angle = (1 + 2 * i) * (M_PI / poly -> nside);
float x = radius * cos(angle);
float y = radius * sin(angle);
polygon_set_vertex(poly, i, x, y);
}
return NULL;
}
}
void *polygon_set_nside(polygon *poly, unsigned nside)
{
if(NULL == poly)
{
fprintf(stderr, "Cannot set polygon number of sides.\n");
return NULL;
}
else
{
poly -> nside = nside;
return NULL;
}
}
void *polygon_set_centre(polygon *poly, point *centre)
{
if(NULL == poly)
{
fprintf(stderr, "Cannot set polygon number of sides.\n");
return NULL;
}
else
{
poly -> centre = centre;
return NULL;
}
}
// Rest of implementation
Apparently, the allocation and/or free of the polygon is not correct. To see this, I wrote this main.c
reading
#include <stdio.h>
#include <stdlib.h>
#include "point.h"
#include "polygon.h"
int main() {
unsigned nside = 3;
float radius = 1.0;
point* centre = point_alloc();
point_init(centre, 0.0, 0.0);
polygon* poly = polygon_alloc(nside);
polygon_init(poly, nside, radius, centre); // this one is a problem
polygon_print(poly);
polygon_free(poly);
point_free(centre);
exit(EXIT_SUCCESS);
}
compiled using the command gcc -Wall -Wextra -Wpedantic -Werror -std=c99 .\point.c .\polygon.c .\main.c -o .\main.exe
, I have a a "red cross in my IDE (I use VSCode
), as illustrated here in the picture (which indicates that something is wrong) and I do not know neither from where it comes, nor how to solve it?
EDIT
I corrected the allocation/freeing function thanks to the answer below, and was able to find that the problem is caused by the polygon_init
function, but I cannot pinpoint what is wrong there?
Your program segfaults in free_polygon()
because you free the vertices
array before each of its verticies[i]
element:
free(poly -> vertices);
poly -> vertices = NULL;
for(unsigned i = 0; i < poly -> nside; i++)
{
if(NULL == poly -> vertices[i])
{
fprintf(stderr, "Cannot free vertex %u.\n", i);
return NULL;
}
else
{
free(poly -> vertices[i]);
poly -> vertices = NULL;
}
}
In general you want to free resources in reverse order of how they are allocated (free vertices[i]
before vertices
). Any error in free_polygon()
may result in memory leaks. For example if poly->vertices[i] == NULL
then we don't free remaining poly->verticies[i]
, poly->vertices
, poly->centre
or poly
. As your alloc_polygon()
takes care of partial constructions is probably ok but it's a risky design:
void* free_polygon(polygon* poly) {
if(NULL == poly) {
fprintf(stderr, "Cannot free polygon.\n");
return NULL;
}
if(NULL == poly->vertices) {
fprintf(stderr, "Cannot free polygon vertices.\n");
return NULL;
}
for(unsigned i = 0; i < poly->nside; i++) {
if(NULL == poly->vertices[i]) {
fprintf(stderr, "Cannot free vertex %u.\n", i);
return NULL;
}
free(poly->vertices[i]);
poly->vertices[i] = NULL;
}
free(poly->vertices);
if(NULL == poly->centre) {
fprintf(stderr, "Cannot free polygon centre.\n");
return NULL;
}
free(poly->centre);
free(poly);
return NULL;
}
Other suggestions:
free_polygon()
always return NULL
consider changing it's return type to void
.!poly
is easier to read then NULL == poly
.alloc_polygon()
is quite complicated; consider designing the allocation and free function as a matching pair so you can always call free_polygon()
on a partially constructed object by initializing variables with NULL, calloc
instead of malloc()
of verticies
and free resources in reverse. I routinely use goto
for cleanup chains but if you have qualms you can duplicate those two lines instead in each error case. Compilers (at least gcc) will warn you if you miss a variable initialization before a goto
which I find helpful.poly
object's lifetime expired after a call to free_polygon()
I never bother setting any of the variables to NULL
. That said it may help you catch a use after free defect.polygon_alloc()
instead of alloc_polygon()
.polygon *poly
to polygon* poly
. It avoids confusion if you ever declare two pointers on one line.polygon *polygon_alloc(unsigned nside) {
polygon *poly = malloc(sizeof *poly);
if(!poly) {
fprintf(stderr, "Cannot allocate polygon.\n");
goto err;
}
poly->nside = nside;
poly->vertices = NULL;
poly->centre = alloc_point();
if(!poly->centre) {
fprintf(stderr, "Cannot allocate polygon centre.\n");
goto err;
}
poly->vertices = calloc(nside, sizeof *poly->vertices);
if(!poly->vertices) {
fprintf(stderr, "Cannot allocate polygon vertices.\n");
goto err;
}
for(unsigned i = 0; i < nside; i++) {
poly->vertices[i] = alloc_point();
if(!poly->vertices[i]) {
fprintf(stderr, "Cannot allocate node %u.\n", i);
goto err;
}
}
return poly;
:err
polygon_free(poly);
return NULL;
}
void polygon_free(polygon *poly) {
if(!poly) return;
if(poly->vertices)
for(unsigned i = 0; i < poly->nside; i++)
free(poly->vertices[i]);
free(poly->vertices);
free(poly->centre);
free(poly);
}