简体   繁体   中英

realloc a struct pointer inner struct pointer in c

context: https://stackoverflow.com/a/34528438/15603477

The original post left some realloc code unfinished in read_solid function, inside if(strcmp("endfacet", line) == 0) {} . trying to finish this part. but failed.
Almost the same as original post code.

Problem: In if((*sol)->nfacets == (*sol)->maxfacets) {} code block, I need realloc a struct inner struct. if I set FACETS = 3 then a single solid struct pointer can only hold 3 facet pointer. Now I want to resize/reallocated to make it can hold more facets pointers.

/*
https://stackoverflow.com/questions/34522831/scanf-stops-working-abruptly/34528438#34528438
gcc \
-Wpointer-arith  -Werror=vla -Wendif-labels \
-Wimplicit-fallthrough -Wmissing-format-attribute  \
-Wcast-function-type -Wshadow=compatible-local \
-Wformat-security -Wno-format-truncation -Wno-stringop-truncation \
array_struct96.c  &&./a.out dat/solid.txt
valgrind ./a.out  dat/solid.txt

*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<errno.h>
#include<math.h>
/* constants - num dimensions, initial facets
 * allocated within each solid, and the max
 * chars per-line for fgets to read
 */
enum{NDIM = 3, FACETS = 3, MAXC= 256};
/* facets struct - declared as pointer member of 
 * struct solid allowing additional allocation to
 * hold as many normal/vertexes as required.
 */
typedef struct facet{
    double normal[NDIM];
    double vertex[NDIM][NDIM];
}facet;

/* struct solid holds the solid's name, 
 * a pointer to an array of stuct facet, 
 * the number of facets containing data, and 
 * the maximum presently allocated.
 */ 
typedef struct solid{
    char *name;
    facet *facets;
    size_t nfacets;
    size_t maxfacets;
}solid;
/* function prototypes */
solid *create_solid();
solid *read_solid(solid **sol, FILE *fp);
void print_solid(solid *sol);
void free_solid(solid *sol);
double xstrtod(char *str, char **ep);

/* simple allocate/initialize function
 * return must be assigned to pointer
 */
solid *create_solid()
{
    size_t i;
    solid *sol = calloc(1,sizeof *sol);
    sol->facets = calloc(FACETS, sizeof *(sol->facets));
    sol->nfacets = 0;
    sol->maxfacets = FACETS;
    for(i = 0; i < FACETS; i++){
        memset((sol->facets)[i].normal, 0,NDIM * sizeof(double));
        memset((sol->facets)[i].vertex, 0,NDIM * NDIM * sizeof(double));
    }
    return sol;
}

/*  simple print of normals & vertexes to stdout    */
void print_solid(solid *sol)
{
    if(!sol){
        fprintf(stderr,"printf_solid() error: invalid parameter 'sol'.\n");
        return;
    }
    size_t i,j;
    printf("Normal and vertexes for solid: %s\n", sol->name);
    printf("Total size of faces=%ld\n", sol->nfacets);

    for(i = 0; i < 5; i++){
        printf("record %ld: ", i+1);
        printf(
            "\nnormal[%3zu]:%10.4f\t%10.4f\t%10.4f\n",i
            ,sol->facets[i].normal[0]
            ,sol->facets[i].normal[1]
            ,sol->facets[i].normal[2]
        );
        for(j = 0; j < NDIM; j++)
            printf("vertex[%3zu]:%10.4f\t%10.4f\t%10.4f\n"
                ,j
                ,sol->facets[i].vertex[j][0]
                ,sol->facets[i].vertex[j][1]
                ,sol->facets[i].vertex[j][2]
            );
    }
}

void free_solid(solid *sol)
{
    if(sol->name) free(sol->name);
    if(sol->facets) free(sol->facets);
    if(sol) free(sol);
}

/* string to double with error checking. */
double xstrtod(char *str, char **ep)
{
    errno = 0;
    double val = strtod(str,ep);
    /* Check for various possible errors */
    if((errno == ERANGE  && (val == HUGE_VAL || val == HUGE_VALL)) ||
        (errno != 0 && val == 0)){
            perror("strtod");
            exit(EXIT_FAILURE);
    }
    if(ep && *ep == str){
        fprintf(stderr,"No double were found.\n");
        exit(EXIT_FAILURE);
    }
    return val;    
}

int main (int argc, char **argv) {
    solid *sol = NULL;  /* pointer to hold values */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open */
        fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
        return 1;
    }

    if (read_solid (&sol, fp)) {    /* read data from file  */
        print_solid (sol);          /* print data read      */
        free_solid (sol);           /* free all memory      */
    }
    if (fp != stdin) fclose (fp);   /* close if not stdin   */

    return 0;
}

/* read_solid takes the poiner address for a struct solid,
 * and will read all normal and vertexes into the struct,
 * updating 'nfacets' with the number of facets for which
 * there is data, and will validate each read and conversion
 * of values from string to double. retuns filled struct on
 * success, NULL otherwise
 */
solid *read_solid(solid **sol, FILE *fp)
{
    char line[MAXC] = {0};  /*  temporary line buffer */
    size_t idx = 0, vidx = 0;   /* line & vertex indexes */
    if(!*sol) *sol = create_solid();     /* allocate & initialize struct */
    
    while(fgets(line,MAXC,fp))   /* read each line in file */
    {
        size_t len = 0;
        char *p, *ep;
        p = ep = line;
        len = strlen(line);  /* get length & remove '\n' */
        if(line[len - 1] == '\n')
            line[--len] = 0;

        if(!(ep = strchr(line,' ')))    /* test if space in line */
        {   /* if endfacet, update nfacets */
            if(strcmp("endfacet", line) == 0)
            {
                (*sol)->nfacets++;
                if((*sol)->nfacets == (*sol)->maxfacets)
                {
                    fprintf(stderr,"read_solid() warning: limit readed\n");
                    printf("(*sol)->nfacets=%ld\n", (*sol)->nfacets);
                    // sol->facets = calloc(FACETS, sizeof *(sol->facets));
                    printf("2 * ((*sol)->maxfacets:%ld\n",2 * (*sol)->maxfacets);

                    void *tmp = realloc ( (*sol)->facets, 2 * ( (*sol)->maxfacets ) * sizeof *(*sol)->facets);
                        if (!tmp) { /* if realloc fails, original pointer still valid */
                            perror ("realloc-sales");   /* throw error */
                            return (*sol);              /* return current pointer      */ 
                        }                               /* (don't exit or return NULL) */
                        (*sol)->facets = tmp;    /* assign reallocated block to (*sol)->facets  */
                        /* (optional) zero newly allocated memory */
                        // memset ((*sol)->facets + (*sol)->nfacets, 0, ((*sol)->maxfacets) * sizeof *((*sol)->facets));
                    (*sol)->maxfacets *= 2;  /* update (*sol)->maxfacets to new size */
                    printf("this part do called\n");
                    break;
                }
            }
            goto processed;
        }
        if(strncmp("solid",line,ep - p) == 0)
        {
            (*sol)->name = strdup(ep + 1);
            goto processed;
        }else if(strncmp("facet",line, ep - p) == 0)
        {
            size_t i;
            while(*ep &&(*ep < '0' || *ep > '9')) ep++; /* skip no digit value */
            if(!*ep){
                fprintf(stderr,"read_solid() error: facet normal no values\n");
                return NULL;
            }
            p = ep;
            for(i = 0; i < NDIM; i++){   /* convert to double & validate */
                /* for which sol index.
                for a specific index of sol
                then assign double to normal.*/
                (*sol)->facets[(*sol)->nfacets].normal[i] = xstrtod(p,&ep);
                p = ep;
            }
            goto processed;
        }else if(strncmp("vertex", line, ep - p) == 0)
        { /* read vertex values */
            size_t i;
            p = ep + 1;
            for(i = 0; i< NDIM; i++){/* convert to double & validate */
                (*sol)->facets[(*sol)->nfacets].vertex[vidx][i] = xstrtod(p,&ep);
                p = ep;
            }
            vidx = vidx < 2 ? vidx + 1 : 0; /* update/reset vertex index */
            goto processed;
        }else if(strncmp("outer",line,ep - p) == 0){ /* skip line begin with outer*/
            goto processed;
        }else if(strncmp("endsolid",line, ep - p) == 0){ /* skip line begin with endsolid*/
            goto processed;
        }else{
            fprintf(stderr,"read_solid() warning: invalid line at '%3zu'\n",idx);
        }
    processed:
        idx++;
    }
    
    if(!(*sol)->nfacets){
        fprintf(stderr,"read_solid() error: no data read.\n");
        free_solid(*sol);
        return NULL;
    }
    printf("idx=%ld\n", idx);
    return *sol;
}

In print_sold function. for(i = 0; i < 5; i++) 0,1,2 is ok. but 3,4 print

record 4:
normal[  3]:    0.0000      0.0000          0.0000
vertex[  0]:    0.0000      0.0000          0.0000
vertex[  1]:    0.0000      0.0000          0.0000
vertex[  2]:    0.0000      0.0000          0.0000
record 5:
normal[  4]:    0.0000      0.0000          0.0000
vertex[  0]:    0.0000      0.0000          0.0000
vertex[  1]:    0.0000      0.0000          0.0000
vertex[  2]:    0.0000      0.0000          0.0000

I modified dat/solid.txt file, so all print should be no zero value. Which mean the realloc part failed.

Here is some heavily amended code that works without any obvious memory faults.

It has several major modifications, and umpteen minor ones (not all of which will be mentioned here):

  • Using a for loop instead of a while loop in read_solid() , which gets rid of the processed label.
  • Using multiple subfunctions to make it easier to read the body of the processing loop.
  • Since the xstrtod() function exits on error (in your code and this code), I've made the code "fail fast" in other places too.
  • The code reading the 'normal' data no longer skips over '+' or '-' signs.
  • The function directive() splits the line, isolating the directive from the data after it. This makes it easier to write the list of comparisons, and the code can use strcmp() instead of strncmp() .
  • There's a fairly simple if / else if / … / else chain in the loop, mostly just calling functions that do a task.
  • I renamed the structure types with an initial capital ( Facet , Solid ).
  • The read_solid() function is passed a pre-allocated Solid structure, which simplifies the code.
  • Because the reading loop breaks when it encounters the endsolid line, it is possible for it to read multiple solids from a single file.

Code:

/* SO 7456-2825 */
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { NDIM = 3, FACETS = 3, MAXC = 256 };

typedef struct Facet
{
    double normal[NDIM];
    double vertex[NDIM][NDIM];
} Facet;

typedef struct Solid
{
    char *name;
    Facet *facets;
    size_t nfacets;
    size_t maxfacets;
} Solid;

static Solid *create_solid(void);
static int read_solid(Solid *sol, FILE *fp);
static void print_solid(Solid *sol);
static void free_solid(Solid *sol);
static double xstrtod(char *str, char **ep);

static Solid *create_solid(void)
{
    Solid *sol;
    if ((sol = calloc(1, sizeof(*sol))) == NULL ||
        (sol->facets = calloc(FACETS, sizeof(sol->facets[0]))) == NULL)
    {
        free(sol);
        return NULL;
    }
    sol->nfacets = 0;
    sol->maxfacets = FACETS;
    return sol;
}

static void print_solid(Solid *sol)
{
    if (!sol)
    {
        fprintf(stderr, "%s error: invalid parameter\n", __func__);
        return;
    }

    printf("Normal and vertexes for solid: %s\n", sol->name);
    printf("Total size of faces=%zu\n", sol->nfacets);

    for (size_t i = 0; i < sol->nfacets; i++)
    {
        printf("Facet %zu: ", i + 1);
        printf(
            "\nNormal[%3zu]:%10.4f\t%10.4f\t%10.4f\n", i,
            sol->facets[i].normal[0],
            sol->facets[i].normal[1],
            sol->facets[i].normal[2]
            );
        for (size_t j = 0; j < NDIM; j++)
        {
            printf("Vertex[%3zu]:%10.4f\t%10.4f\t%10.4f\n",
                   j,
                   sol->facets[i].vertex[j][0],
                   sol->facets[i].vertex[j][1],
                   sol->facets[i].vertex[j][2]
                   );
        }
    }
}

static void free_solid(Solid *sol)
{
    if (sol)
    {
        free(sol->name);
        free(sol->facets);
        free(sol);
    }
}

static double xstrtod(char *str, char **ep)
{
    errno = 0;
    double val = strtod(str, ep);

    if ((errno == ERANGE  && (val == HUGE_VAL || val == HUGE_VALL)) ||
        (errno != 0 && val == 0))
    {
        perror("strtod");
        exit(EXIT_FAILURE);
    }
    if (ep && *ep == str)
    {
        fprintf(stderr, "No double were found.\n");
        exit(EXIT_FAILURE);
    }
    return val;
}

/* exits on failure */
static void more_facets(Solid *sol)
{
    fprintf(stderr, "%s() warning: limit reached (sol->nfacets = %zu)\n",
            __func__, sol->nfacets);

    size_t new_facets = 2 * sol->maxfacets;
    printf("new_facets = %zu\n", new_facets);

    void *tmp = realloc(sol->facets, new_facets * sizeof(sol->facets[0]));
    if (tmp == NULL)
    {
        perror(__func__);
        exit(EXIT_FAILURE);
    }
    sol->facets = tmp;
    sol->maxfacets = new_facets;
}

static void read_name(Solid *sol, char *name)
{
    sol->name = strdup(name);
    if (sol->name == NULL)
    {
        perror(__func__);
        exit(EXIT_FAILURE);
    }
}

static void read_normal(Facet *fp, char *ep)
{
    while (*ep && !isdigit((unsigned char)*ep) && *ep != '+' && *ep != '-')
        ep++;
    if (!*ep)
    {
        fprintf(stderr, "%s(): error: facet normal no values\n", __func__);
        return;
    }
    for (size_t i = 0; i < NDIM; i++)
    {
        char *p = ep;
        fp->normal[i] = xstrtod(p, &ep);
    }
}

static void read_vertex(Facet *facet, char *ep, size_t vidx)
{
    assert(vidx < NDIM);
    char *p = ep;
    for (size_t i = 0; i < NDIM; i++)
    {
        facet->vertex[vidx][i] = xstrtod(p, &ep);
        p = ep;
    }
}

/* Isolate directive into null-terminated string */
/* Return pointer to data after blank after directive, or null byte */
static char *directive(char *line)
{
    char *ep = strchr(line, ' ');
    if (ep != NULL)
        *ep++ = '\0';
    else
        ep += strlen(line);
    return ep;
}

/*
** Note that the scanning does not enforce the correct sequencing of
** facet, outer, vertex, vertex, vertex, endloop, endfacet
** There are other infelicities that are not detected.
*/
static int read_solid(Solid *sol, FILE *fp)
{
    char line[MAXC];
    size_t vidx = 0;

    for (size_t lineno = 0; fgets(line, sizeof(line), fp) != NULL; lineno++)
    {
        line[strcspn(line, "\n")] = '\0';
        char *ep = directive(line);

        if (strcmp("endfacet", line) == 0)
            continue;
        else if (strcmp("endloop", line) == 0)
            continue;
        else if (strcmp("outer", line) == 0)
            vidx = 0;
        else if (strcmp("endsolid", line) == 0)
            break;
        else if (strcmp("solid", line) == 0)
            read_name(sol, ep);
        else if (strcmp("facet", line) == 0)
        {
            assert(sol->nfacets <= sol->maxfacets);
            if (sol->nfacets == sol->maxfacets)
                more_facets(sol);
            read_normal(&sol->facets[sol->nfacets++], ep);
        }
        else if (strcmp("vertex", line) == 0)
            read_vertex(&sol->facets[sol->nfacets - 1], ep, vidx++);
        else
        {
            fprintf(stderr, "%s() warning: invalid directive '%s' at '%3zu' ignored\n",
                    __func__, line, lineno);
        }
    }

    if (!sol->nfacets)
    {
        fprintf(stderr, "read_solid() error: no data read.\n");
        free_solid(sol);
        return -1;
    }
    printf("idx=%zu\n", sol->nfacets);
    return 0;
}

int main(int argc, char **argv)
{
    FILE *fp = argc > 1 ? fopen(argv[1], "r") : stdin;

    if (fp == NULL)
    {
        fprintf(stderr, "%s: error: file open failed '%s'\n", argv[0], argv[1]);
        return 1;
    }

    Solid *sol = create_solid();
    if (sol == NULL)
    {
        fprintf(stderr, "%s: error: failed to create solid\n", argv[0]);
    }

    if (read_solid(sol, fp) == 0)
    {
        print_solid(sol);
        free_solid(sol);
    }
    if (fp != stdin)
        fclose(fp);

    return 0;
}

When run on the full-size (24-facet) data file from the prior question, it produces the output:

more_facets() warning: limit reached (sol->nfacets = 3)
more_facets() warning: limit reached (sol->nfacets = 6)
more_facets() warning: limit reached (sol->nfacets = 12)
new_facets = 6
new_facets = 12
new_facets = 24
idx=24
Normal and vertexes for solid: Untitled-56807ca1
Total size of faces=24
Facet 1: 
Normal[  0]:    0.0000      0.0000     -1.0000
Vertex[  0]:  100.0000     50.0000      0.0000
Vertex[  1]:    0.0000      0.0000      0.0000
Vertex[  2]:    0.0000     50.0000      0.0000
Facet 2: 
Normal[  1]:    0.0000      0.0000     -1.0000
Vertex[  0]:    0.0000      0.0000      0.0000
Vertex[  1]:  100.0000     50.0000      0.0000
Vertex[  2]:  100.0000      0.0000      0.0000
Facet 3: 
Normal[  2]:   -1.0000      0.0000      0.0000
Vertex[  0]:    0.0000     50.0000     67.8050
Vertex[  1]:    0.0000      0.0000      0.0000
Vertex[  2]:    0.0000      0.0000     67.8050
Facet 4: 
Normal[  3]:   -1.0000      0.0000      0.0000
Vertex[  0]:    0.0000      0.0000      0.0000
Vertex[  1]:    0.0000     50.0000     67.8050
Vertex[  2]:    0.0000     50.0000      0.0000
Facet 5: 
Normal[  4]:   -0.0000      1.0000      0.0000
Vertex[  0]:    0.0000     50.0000     67.8050
Vertex[  1]:  100.0000     50.0000      0.0000
Vertex[  2]:    0.0000     50.0000      0.0000
Facet 6: 
Normal[  5]:   -0.0000      1.0000      0.0000
Vertex[  0]:  100.0000     50.0000      0.0000
Vertex[  1]:    0.0000     50.0000     67.8050
Vertex[  2]:  100.0000     50.0000     67.8050
Facet 7: 
Normal[  6]:    1.0000      0.0000      0.0000
Vertex[  0]:  100.0000     50.0000      0.0000
Vertex[  1]:  100.0000      0.0000     67.8050
Vertex[  2]:  100.0000      0.0000      0.0000
Facet 8: 
Normal[  7]:    1.0000      0.0000      0.0000
Vertex[  0]:  100.0000      0.0000     67.8050
Vertex[  1]:  100.0000     50.0000      0.0000
Vertex[  2]:  100.0000     50.0000     67.8050
Facet 9: 
Normal[  8]:   -0.0000     -1.0000     -0.0000
Vertex[  0]:  100.0000      0.0000     67.8050
Vertex[  1]:    0.0000      0.0000      0.0000
Vertex[  2]:  100.0000      0.0000      0.0000
Facet 10: 
Normal[  9]:   -0.0000     -1.0000     -0.0000
Vertex[  0]:    0.0000      0.0000      0.0000
Vertex[  1]:  100.0000      0.0000     67.8050
Vertex[  2]:    0.0000      0.0000     67.8050
Facet 11: 
Normal[ 10]:    0.8394      0.0000      0.5436
Vertex[  0]:  100.0000     50.0000     67.8050
Vertex[  1]:   69.0000     15.5000    115.6740
Vertex[  2]:  100.0000      0.0000     67.8050
Facet 12: 
Normal[ 11]:    0.8394      0.0000      0.5436
Vertex[  0]:   69.0000     15.5000    115.6740
Vertex[  1]:  100.0000     50.0000     67.8050
Vertex[  2]:   69.0000     34.5000    115.6740
Facet 13: 
Normal[ 12]:   -0.8394     -0.0000      0.5436
Vertex[  0]:   31.0000     15.5000    115.6740
Vertex[  1]:    0.0000     50.0000     67.8050
Vertex[  2]:    0.0000      0.0000     67.8050
Facet 14: 
Normal[ 13]:   -0.8394     -0.0000      0.5436
Vertex[  0]:    0.0000     50.0000     67.8050
Vertex[  1]:   31.0000     15.5000    115.6740
Vertex[  2]:   31.0000     34.5000    115.6740
Facet 15: 
Normal[ 14]:   -0.0000     -0.9514      0.3081
Vertex[  0]:   69.0000     15.5000    115.6740
Vertex[  1]:    0.0000      0.0000     67.8050
Vertex[  2]:  100.0000      0.0000     67.8050
Facet 16: 
Normal[ 15]:   -0.0000     -0.9514      0.3081
Vertex[  0]:    0.0000      0.0000     67.8050
Vertex[  1]:   69.0000     15.5000    115.6740
Vertex[  2]:   31.0000     15.5000    115.6740
Facet 17: 
Normal[ 16]:    0.0000      0.9514      0.3081
Vertex[  0]:   31.0000     34.5000    115.6740
Vertex[  1]:  100.0000     50.0000     67.8050
Vertex[  2]:    0.0000     50.0000     67.8050
Facet 18: 
Normal[ 17]:    0.0000      0.9514      0.3081
Vertex[  0]:  100.0000     50.0000     67.8050
Vertex[  1]:   31.0000     34.5000    115.6740
Vertex[  2]:   69.0000     34.5000    115.6740
Facet 19: 
Normal[ 18]:    0.0000     -0.8820      0.4713
Vertex[  0]:   50.0000     25.0000    133.4520
Vertex[  1]:   31.0000     15.5000    115.6740
Vertex[  2]:   69.0000     15.5000    115.6740
Facet 20: 
Normal[ 19]:    0.6832      0.0000      0.7302
Vertex[  0]:   69.0000     34.5000    115.6740
Vertex[  1]:   50.0000     25.0000    133.4520
Vertex[  2]:   69.0000     15.5000    115.6740
Facet 21: 
Normal[ 20]:   -0.6832      0.0000      0.7302
Vertex[  0]:   50.0000     25.0000    133.4520
Vertex[  1]:   31.0000     34.5000    115.6740
Vertex[  2]:   31.0000     15.5000    115.6740
Facet 22: 
Normal[ 21]:   -0.0000     -0.0000      1.0000
Vertex[  0]:   69.0000     15.5000    115.6740
Vertex[  1]:   31.0000     34.5000    115.6740
Vertex[  2]:   31.0000     15.5000    115.6740
Facet 23: 
Normal[ 22]:   -0.0000     -0.0000      1.0000
Vertex[  0]:   31.0000     34.5000    115.6740
Vertex[  1]:   69.0000     15.5000    115.6740
Vertex[  2]:   69.0000     34.5000    115.6740
Facet 24: 
Normal[ 23]:   -0.0000      0.8820      0.4713
Vertex[  0]:   50.0000     25.0000    133.4520
Vertex[  1]:   69.0000     34.5000    115.6740
Vertex[  2]:   31.0000     34.5000    115.6740

I think that the break you have in the code after allocating more facets was the main cause of your trouble.

If I were working on it further, I'd have a code structure that insisted on the correct sequencing. With the functions in place, it is fairly easy to see how to do this. This would eliminate the vidx in the main loop, which is still pretty ugly. I'd also consider making a structure type for the 3 numbers ( struct Point { double x; double y; double z; }; or struct Point { double value[3]; }; or similar, and using that in the Facet structure. It would simplify the argument handling.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM