我正在尝试制作一个动态结构数组,我可以成功地向它添加一个结构。但是我添加的任何其他结构都会导致分段错误。这是我的代码:
#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: %sn", 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: %sn", people[1].first_name);
free(people);
return 0;
}
我将不胜感激任何关于为什么会发生这种情况的帮助,谢谢!
问题出在以下行:
*people[*population] = p;
将其更改为:
(*people)[*population] = p;
为什么需要括号?
编译器具有运算符优先级规则。应用它们时,它会将您的代码视为:
*(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 0n first name: %sn last name : %sn age : %hhun",
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 1n first name: %sn last name : %sn age : %hhun",
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 0n first name: %sn last name : %sn age : %hhun",
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 1n first name: %sn last name : %sn age : %hhun",
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
一个问题是add_person()的最后一个参数,具体来说,参数'(struct Person) p'。当"timn"和"batman"被传递到add_person()函数时,它们作为原始结构的副本传递。在 add_person() 结构中,该数据实际上在堆栈上,并且在函数范围之外是易失性的。尝试将最后一个参数更改为指针。