在 C 中退出分段错误结构

Exited segmentation fault Struct in C

所以我有一个结构,我想创建一个带有循环的函数,该循环从用户输入中获取并将它们存储到结构中。但是在 1 个循环之后程序因分段错误而崩溃,我找不到问题所在。

错误如下:

> clang-7 -pthread -lm -o main main.c
> ./main
4
ath
67
68
thes
exited, segmentation fault
>

代码如下:

#define STR 100000
typedef struct city{
 float x;
 float y;
 char *name;
}City;

City *GetCities(int *N){
int i;
City *p;
char c;
scanf("%d",&*N);
p=malloc(sizeof(City));
p->name=malloc(STR*sizeof(char));

if (p->name==NULL){
  printf("Could not find enough memory");
}

for (i=0; i<*N; i++){
   while((c = getchar()) !='\n' && c!= EOF);
   fgets(p[i].name,STR,stdin);
   p[strcspn(p[i].name,"\n")].name="[=11=]";
   p[i].name=realloc(p[i].name,(strlen(p[i].name)+1)*sizeof(char));
   scanf("%f ",&p[i].x);
   scanf("%f",&p[i].y);
} 
return p;
}

这个会起作用:

//includes were missing
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STR 100000
typedef struct city{
 float x;
 float y;
 char *name;
}City;

City *GetCities(int *N){
  int i;
  char c;
  City *list, *city;
  //N is already a pointer so using a '&*' is confusing and I'm unsure whether this is at all valid syntax
  scanf("%d", N);
  //allocate enough memory! multiplied by number of cities
  list = malloc(sizeof(City) * (*N));
  
  for (i=0; i < *N; i++) {
     //use pointer arithmetic to get the current city from the allocated block
     //this needs to be done only once, now you can go on and reuse it extensively
     city = list + i;
     
     city->name = malloc(STR * sizeof(char));
     if (city->name==NULL){
       printf("Could not find enough memory");
     }
     while((c = getchar()) !='\n' && c!= EOF);
     //i hope rest is clear
     fgets(city->name, STR, stdin);
//use city->name[strlen(city->name)-1]='[=10=]' instead?
     city->name[strcspn(city->name,"\n")]='[=10=]';
     //at least you didn't forget the +1 in the realloc! :-)
     city->name = realloc(city->name,(strlen(city->name)+1)*sizeof(char));
     scanf("%f ", &city->x);
     scanf("%f",  &city->y);
  }
  
  return list;
}

//please also show the main, but I guess it was something like this
int main () {
    int i, n;
    City *cities, *city;
    cities = GetCities(&n);
    for (i=0; i < n; i++) {
        city = cities + i;
        printf("The %dth city \"%s\" got coordinates x=%f and y=%f!\n", i, city->name, city->x, city->y);
        //don't forget to free the name
        free(city->name);
    }
    //don't forget to free the memory
    free(cities);
}

我还建议在循环中使用较小的行长度缓冲区和 运行 fgets 以节省 ram space。

要求输入包括即将到来的数据的总数通常是一个糟糕的设计选择,并且很容易删除该要求。例如:

    #include <stddef.h>                                                                
    #include <stdio.h>                                                                 
    #include <stdlib.h>                                                                
                                                                                       
    FILE * xfopen(const char *, const char *);                                         
    void * xrealloc(void *, size_t, size_t, void *);                                   
                                                                                       
    struct city{                                                                       
            float x, y;                                                                
            char *name;                                                                
    };                                                                                 
                                                                                       
    int                                                                                
    get_city(struct city *e)                                                           
    {                                                                                  
            int c;                                                                     
            char *end;                                                                 
            size_t cap = 32;                                                           
            e->name = xrealloc(NULL, 1, cap, &end); 
            /* Copy one line.  We coould use getline(), or "%m[^\n]" */
            while( (c = getchar()) != EOF && c != '\n' ){                              
                    *end++ = c;                                                        
                    if( end == e->name + cap ){                                        
                            e->name = xrealloc(e->name, 1, cap += 32, &end);           
                    }                                                                  
            }                                                                          
            *end = '[=10=]';                                                               
            if( c == EOF ){                                                            
                    return EOF;                                                        
            }                                                                          
            if( (c = scanf("%f %f ", &e->x, &e->y)) != 2 ){                            
                    if( c != EOF ){                                                    
                            fprintf(stderr, "Invalid input\n");                        
                    }                                                                  
                    return EOF;                                                        
            }                                                                          
            return 0;                                                                  
    }                                                                                  
                                                                                       
    struct city *                                                                      
    GetCities(int *n)                                                                  
    {                                                                                  
            size_t cap = 4;                                                            
            struct city *e, *p = xrealloc(NULL, sizeof *p, cap, &e);                   
            while( get_city(e) != EOF ){                                               
                    if( ++e == p + cap ){                                              
                            p = xrealloc(p, sizeof *p, cap += 4, &e);                  
                    }                                                                  
            }                                                                          
            *n = e - p;                                                                
            return p;                                                                  
    }

    int                                                                                
    main(void)                                                                         
    {                                                                                  
            int n;                                                                     
            while( (n = getchar()) != EOF && n != '\n' ){                              
                    ;  /* Discard first line of input */                               
            }                                                                          
            struct city *p = GetCities(&n);                                            
            struct city *e = p + n;                                                    
                                                                                       
            for( ; p < e; p++ ){                                                       
                    printf("%s: %f, %f\n", p->name, p->x, p->y);                       
            }                                                                          
            return 0;                                                                  
    }                                                                                  
                                                                                       
    void *                                                                             
    xrealloc(void *buf, size_t num, size_t siz, void *endvp)                           
    {                                                                                  
            char **endp = endvp;                                                       
            char *b = buf;                                                             
            ptrdiff_t offset = b && endp && *endp ? *endp - b : 0;                     
            b = realloc(b, num * siz);                                                 
            if( b == NULL ){                                                           
                    perror("realloc");                                                 
                    exit(EXIT_FAILURE);                                                
            }                                                                          
            if( endp != NULL ){                                                        
                    *endp = b + offset;                                                
            }                                                                          
            return b;                                                                  
    }  

要使程序运行,您需要做三件基本的事情:

  1. 分配N个城市
  2. 对于每个城市,在读取名称之前为名称分配内存
  3. 正确终止名称(分配空字符,而不是字符串)

这是更正后的代码:

City *GetCities(int *N)
{
    int i;
    City *p;
    char c;

    scanf("%d", N);
    p = malloc(*N * sizeof(City));
    for (i = 0; i < *N; i++) {
        p[i].name = malloc(STR * sizeof(char));
        if (p[i].name == NULL) {
            printf("Could not find enough memory");
        }
        while ((c = getchar()) != '\n' && c != EOF);
        fgets(p[i].name, STR, stdin);
        p[i].name[strcspn(p[i].name, "\n")] = '[=10=]';
        scanf("%f ", &p[i].x);
        scanf("%f", &p[i].y);
    }
    return p;
}

剩下的就是验证输入并确保第一次调用 malloc 不会失败。您可能还需要一个函数来释放分配的内存。