C数组的结构分段错误
C array of structs segmentation fault
我正在尝试创建一个动态结构数组,我可以成功地向其中添加一个结构。但是我添加的任何更多结构都会导致分段错误。这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 4
struct Person {
char *first_name;
char *last_name;
unsigned int age;
};
int add_person(struct Person **people, size_t *people_size, size_t *population, struct Person p) {
if ((sizeof(struct Person) * *population) > *people_size) {
return -1;
}
if ((sizeof(struct Person) * (*population + 1)) >= *people_size) {
*people_size = *people_size + sizeof(struct Person) * PEOPLE_BLOCK;
*people = realloc(*people, *people_size);
if (!*people) {
return -1;
}
}
*people[*population] = p;
++*population;
return 0;
}
int main(int argc, char const *argv[]) {
size_t population;
size_t people_size;
struct Person *people, timn, batman;
population = 0;
people_size = sizeof(struct Person) * PEOPLE_BLOCK;
people = malloc(people_size);
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add_person(&people, &people_size, &population, timn);
printf("Person 0's first name: %s\n", people[0].first_name);
batman.first_name = "Bat";
batman.last_name = "Man";
batman.age = 42;
add_person(&people, &people_size, &population, batman);
printf("Person 1's first name: %s\n", people[1].first_name);
free(people);
return 0;
}
如果能帮助我解释为什么会发生这种情况,我将不胜感激!
一个问题是 add_person() 的最后一个参数,即参数“(struct Person) p”。当 'timn' 和 'batman' 被传递到 add_person() 函数时,它们作为原始结构的 copy 被传递。在 add_person() 结构中,该数据实际上 在堆栈上 并且在函数范围之外是易变的。尝试将最后一个参数更改为指针。
问题出在这一行:
*people[*population] = p;
改为:
(*people)[*population] = p;
为什么需要括号?
编译器有rules of operator precedence。应用它们时,它会将您的代码视为:
*(people[*population]) = p;
这不是您想要的。给定一个指向指针的指针 Type **pp
,
*pp[n] = value;
的意思是“获取从 pp
开始的第 n
个指针,并在从指针所持有的地址取消引用的位置分配 value
。换句话说,它本质上意味着这个:
Type *p = pp[n];
*p = value;
你真正想要的是这个:
Type *p = *pp;
p[n] = value;
这就是 (*pp)[n]
,区分指针到指针的取消引用,给你的。否则,您将使用无效指针,从而导致您的错误。
不确定这个答案是否有帮助,但无论如何。
我不明白你的代码,你想做什么。
你直接用元素个数,第一人称指针,最大元素个数。你可能会遇到很多问题。
您将文字字符串直接存储在结构中,这意味着在实际情况下(不使用文字)会导致内存泄漏。
这是我的看法。出于测试原因,我将 PEOPLE_BLOCK 变小了。
希望这有帮助。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 2
typedef struct _Person {
char *first_name;
char *last_name;
unsigned int age;
} Person;
typedef struct _VectorPeople {
Person * people;
size_t num;
size_t max;
} VectorPeople;
void init(VectorPeople *v)
{
v->max = PEOPLE_BLOCK;
v->num = 0;
v->people = (Person *) malloc( sizeof(Person) * v->max );
}
void clear(VectorPeople *v)
{
// Clear persons
Person * it = v->people;
while( ( it - v->people ) < v->num ) {
free( it->first_name );
free( it->last_name );
++it;
}
// Clear vector
v->max = v->num = 0;
free( v->people );
v->people = NULL;
}
void add(VectorPeople *v, Person *p)
{
// Reserve
if ( v->num >= v->max ) {
v->max += PEOPLE_BLOCK;
// Realloc
v->people = realloc( v->people, v->max * sizeof(Person) );
if ( v->people == NULL ) {
exit( -1 );
}
}
// Copy strings
p->first_name = strdup( p->first_name );
p->last_name = strdup( p->last_name );
// Insert
v->people[ ( v->num )++ ] = *p;
}
int main(int argc, char const *argv[]) {
VectorPeople vp;
Person timn;
Person batman;
Person bond;
Person superman;
init( &vp );
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add( &vp, &timn );
batman.first_name = "Batn";
batman.last_name = "Man";
batman.age = 42;
add( &vp, &batman );
bond.first_name = "James";
bond.last_name = "Bond";
bond.age = 45;
add( &vp, &bond );
superman.first_name = "Super";
superman.last_name = "Man";
superman.age = 45;
add( &vp, &superman );
int i = 0;
for(; i < vp.num; ++i ) {
printf( "Person: %s, %s.\n", vp.people[ i ].last_name, vp.people[ i ].first_name );
}
clear( &vp );
return 0;
}
您的代码中存在一些错误。要记住一件事,当你动态分配内存时,你负责跟踪它并在你不再需要它时释放它(否则,你会像筛子一样泄漏内存).
在您的代码中,您尝试创建一个结构数组,其中包含指向字符数组的指针。 char *
指针未分配,不能简单地以您尝试的方式分配。 strdup
可以提供帮助,但是您刚刚分配了内存,所以 free
当您完成它时。
尝试分配具有不同(未知)长度 first_name
和 last_name
的结构数组需要您跟踪每个分配。从某种意义上说,您最好将 people
声明为 pointer to pointer to Person
这允许对 people
进行迭代,而不必将人口存储在某个地方,从而允许您迭代直到第一个 NULL
指针遇到了。
同样,为你的结构创建一个typedef
可以大大减少你写sizeof (struct Person)
的次数。它使代码保持整洁,并帮助您思考指针的迷雾。
这是一个使用指针到指针到结构的示例,我认为您打算这样做。下面是仅使用指向结构的指针的实现。评估两者并决定您更喜欢哪种实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max);
Person **realloc_person (Person **ppl, size_t *n);
void free_person (Person *p);
void free_person_names (Person *p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person **people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (&timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0]->first_name, people[0]->last_name, people[0]->age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (&batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1]->first_name, people[1]->last_name, people[1]->age);
for (i = 0; i < population; i++)
free_person (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
if (!((*ppl)[*pop] = malloc (sizeof ***ppl)))
return NULL;
size_t i = (*pop)++;
(*ppl)[i]-> first_name = strdup (p.first_name);
(*ppl)[i]-> last_name = strdup (p.last_name);
(*ppl)[i]-> age = p.age;
return (*ppl)[i];
}
/* realloc an array of pointers to Person setting memory to 0. */
Person **realloc_person (Person **ppl, size_t *n)
{
Person **tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free memory for a Person */
void free_person (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
free (p);
}
/* free only names of Person (for temp structs) */
void free_person_names (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
}
注意: 已更新以更正 ppl
重新分配的起始地址。
仅使用人物数组
虽然本质上与使用指向 Person
的指针不同,但使用指向 Person
的简单指针消除了迭代数组的能力,直到 NULL
或(空)遇到指针。以下是仅使用 Person
:
数组的相同代码的实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max);
Person *realloc_person (Person *ppl, size_t *n);
void free_person_names (Person p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person *people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0].first_name, people[0].last_name, people[0].age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1].first_name, people[1].last_name, people[1].age);
for (i = 0; i < population; i++)
free_person_names (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
size_t i = (*pop)++;
(*ppl)[i].first_name = strdup (p.first_name);
(*ppl)[i].last_name = strdup (p.last_name);
(*ppl)[i].age = p.age;
return ppl[i];
}
/* realloc an array Person setting memory to 0. */
Person *realloc_person (Person *ppl, size_t *n)
{
Person *tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free only names of Person (for temp structs) */
void free_person_names (Person p)
{
if (p.first_name) free (p.first_name);
if (p.last_name) free (p.last_name);
}
输出
$ ./bin/struct_add_person
Person 0
first name: Timn
last name : Timothy
age : 38
Person 1
first name: Bat
last name : Man
age : 42
我正在尝试创建一个动态结构数组,我可以成功地向其中添加一个结构。但是我添加的任何更多结构都会导致分段错误。这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 4
struct Person {
char *first_name;
char *last_name;
unsigned int age;
};
int add_person(struct Person **people, size_t *people_size, size_t *population, struct Person p) {
if ((sizeof(struct Person) * *population) > *people_size) {
return -1;
}
if ((sizeof(struct Person) * (*population + 1)) >= *people_size) {
*people_size = *people_size + sizeof(struct Person) * PEOPLE_BLOCK;
*people = realloc(*people, *people_size);
if (!*people) {
return -1;
}
}
*people[*population] = p;
++*population;
return 0;
}
int main(int argc, char const *argv[]) {
size_t population;
size_t people_size;
struct Person *people, timn, batman;
population = 0;
people_size = sizeof(struct Person) * PEOPLE_BLOCK;
people = malloc(people_size);
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add_person(&people, &people_size, &population, timn);
printf("Person 0's first name: %s\n", people[0].first_name);
batman.first_name = "Bat";
batman.last_name = "Man";
batman.age = 42;
add_person(&people, &people_size, &population, batman);
printf("Person 1's first name: %s\n", people[1].first_name);
free(people);
return 0;
}
如果能帮助我解释为什么会发生这种情况,我将不胜感激!
一个问题是 add_person() 的最后一个参数,即参数“(struct Person) p”。当 'timn' 和 'batman' 被传递到 add_person() 函数时,它们作为原始结构的 copy 被传递。在 add_person() 结构中,该数据实际上 在堆栈上 并且在函数范围之外是易变的。尝试将最后一个参数更改为指针。
问题出在这一行:
*people[*population] = p;
改为:
(*people)[*population] = p;
为什么需要括号?
编译器有rules of operator precedence。应用它们时,它会将您的代码视为:
*(people[*population]) = p;
这不是您想要的。给定一个指向指针的指针 Type **pp
,
*pp[n] = value;
的意思是“获取从 pp
开始的第 n
个指针,并在从指针所持有的地址取消引用的位置分配 value
。换句话说,它本质上意味着这个:
Type *p = pp[n];
*p = value;
你真正想要的是这个:
Type *p = *pp;
p[n] = value;
这就是 (*pp)[n]
,区分指针到指针的取消引用,给你的。否则,您将使用无效指针,从而导致您的错误。
不确定这个答案是否有帮助,但无论如何。 我不明白你的代码,你想做什么。
你直接用元素个数,第一人称指针,最大元素个数。你可能会遇到很多问题。
您将文字字符串直接存储在结构中,这意味着在实际情况下(不使用文字)会导致内存泄漏。
这是我的看法。出于测试原因,我将 PEOPLE_BLOCK 变小了。 希望这有帮助。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 2
typedef struct _Person {
char *first_name;
char *last_name;
unsigned int age;
} Person;
typedef struct _VectorPeople {
Person * people;
size_t num;
size_t max;
} VectorPeople;
void init(VectorPeople *v)
{
v->max = PEOPLE_BLOCK;
v->num = 0;
v->people = (Person *) malloc( sizeof(Person) * v->max );
}
void clear(VectorPeople *v)
{
// Clear persons
Person * it = v->people;
while( ( it - v->people ) < v->num ) {
free( it->first_name );
free( it->last_name );
++it;
}
// Clear vector
v->max = v->num = 0;
free( v->people );
v->people = NULL;
}
void add(VectorPeople *v, Person *p)
{
// Reserve
if ( v->num >= v->max ) {
v->max += PEOPLE_BLOCK;
// Realloc
v->people = realloc( v->people, v->max * sizeof(Person) );
if ( v->people == NULL ) {
exit( -1 );
}
}
// Copy strings
p->first_name = strdup( p->first_name );
p->last_name = strdup( p->last_name );
// Insert
v->people[ ( v->num )++ ] = *p;
}
int main(int argc, char const *argv[]) {
VectorPeople vp;
Person timn;
Person batman;
Person bond;
Person superman;
init( &vp );
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add( &vp, &timn );
batman.first_name = "Batn";
batman.last_name = "Man";
batman.age = 42;
add( &vp, &batman );
bond.first_name = "James";
bond.last_name = "Bond";
bond.age = 45;
add( &vp, &bond );
superman.first_name = "Super";
superman.last_name = "Man";
superman.age = 45;
add( &vp, &superman );
int i = 0;
for(; i < vp.num; ++i ) {
printf( "Person: %s, %s.\n", vp.people[ i ].last_name, vp.people[ i ].first_name );
}
clear( &vp );
return 0;
}
您的代码中存在一些错误。要记住一件事,当你动态分配内存时,你负责跟踪它并在你不再需要它时释放它(否则,你会像筛子一样泄漏内存).
在您的代码中,您尝试创建一个结构数组,其中包含指向字符数组的指针。 char *
指针未分配,不能简单地以您尝试的方式分配。 strdup
可以提供帮助,但是您刚刚分配了内存,所以 free
当您完成它时。
尝试分配具有不同(未知)长度 first_name
和 last_name
的结构数组需要您跟踪每个分配。从某种意义上说,您最好将 people
声明为 pointer to pointer to Person
这允许对 people
进行迭代,而不必将人口存储在某个地方,从而允许您迭代直到第一个 NULL
指针遇到了。
同样,为你的结构创建一个typedef
可以大大减少你写sizeof (struct Person)
的次数。它使代码保持整洁,并帮助您思考指针的迷雾。
这是一个使用指针到指针到结构的示例,我认为您打算这样做。下面是仅使用指向结构的指针的实现。评估两者并决定您更喜欢哪种实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max);
Person **realloc_person (Person **ppl, size_t *n);
void free_person (Person *p);
void free_person_names (Person *p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person **people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (&timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0]->first_name, people[0]->last_name, people[0]->age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (&batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1]->first_name, people[1]->last_name, people[1]->age);
for (i = 0; i < population; i++)
free_person (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
if (!((*ppl)[*pop] = malloc (sizeof ***ppl)))
return NULL;
size_t i = (*pop)++;
(*ppl)[i]-> first_name = strdup (p.first_name);
(*ppl)[i]-> last_name = strdup (p.last_name);
(*ppl)[i]-> age = p.age;
return (*ppl)[i];
}
/* realloc an array of pointers to Person setting memory to 0. */
Person **realloc_person (Person **ppl, size_t *n)
{
Person **tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free memory for a Person */
void free_person (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
free (p);
}
/* free only names of Person (for temp structs) */
void free_person_names (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
}
注意: 已更新以更正 ppl
重新分配的起始地址。
仅使用人物数组
虽然本质上与使用指向 Person
的指针不同,但使用指向 Person
的简单指针消除了迭代数组的能力,直到 NULL
或(空)遇到指针。以下是仅使用 Person
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max);
Person *realloc_person (Person *ppl, size_t *n);
void free_person_names (Person p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person *people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0].first_name, people[0].last_name, people[0].age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1].first_name, people[1].last_name, people[1].age);
for (i = 0; i < population; i++)
free_person_names (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
size_t i = (*pop)++;
(*ppl)[i].first_name = strdup (p.first_name);
(*ppl)[i].last_name = strdup (p.last_name);
(*ppl)[i].age = p.age;
return ppl[i];
}
/* realloc an array Person setting memory to 0. */
Person *realloc_person (Person *ppl, size_t *n)
{
Person *tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free only names of Person (for temp structs) */
void free_person_names (Person p)
{
if (p.first_name) free (p.first_name);
if (p.last_name) free (p.last_name);
}
输出
$ ./bin/struct_add_person
Person 0
first name: Timn
last name : Timothy
age : 38
Person 1
first name: Bat
last name : Man
age : 42