如何在C语言中正确地为结构体数组分配内存

27

我将使用 strtok 读取两组 char*(或字符串),由于这两组 char 相关,格式为 (地址 : 命令\n),因此我决定使用一个结构体。

struct line* array = (struct line*)malloc(sizeof(file) * sizeof(struct line*));

这一行为函数malloc分配空间时导致了一个分段错误,我想知道你能否告诉我一个适当的方式来使用malloc为其分配空间。为了提供背景信息,这是我的其他代码:

struct line
{
    char* addr;
    char* inst;
};
while loop{
    x = strtok(line,": ");
    y = strtok(NULL,"\n");
    strcpy(array[i].addr,x); //assume that x and y are always 3characters
    strcpy(array[i].inst,++y);
    i++;
}

4
请记住,为struct line数组分配内存不会为addrinst字符串分配内存。根据struct line里面的指针如何使用,您可能还需要为字符串数据分配内存。 - Michael Burr
2个回答

35

分配内存对所有类型都是相同的。如果您需要分配一个line结构体的数组,可以使用以下代码:

struct line* array = malloc(number_of_elements * sizeof(struct line));

在你的代码中,你分配的是一个具有适当大小的指向line指针的数组,而不是指向line结构体的数组。此外,请注意没有必要转换malloc()的返回值。

请注意更好的编码风格是使用:

sizeof(*array)

改为:

sizeof(struct line)
这是因为即使你更改了array的类型,分配仍将按预期工作。虽然这种情况不太可能发生,但这是一个值得习惯的通用事情。
另外,请注意,可以通过typedef结构来避免不断重复单词struct
typedef struct line
{
    char* addr;
    char* inst;
} line;

你可以直接这样做:

line* array = malloc(number_of_elements * sizeof(*array));

当然不要忘记为array.addrarray.inst分配内存。


如果你喜欢每次都写 struct,因为这个关键字可以提醒你正在分配一个复杂类型而不是原始类型(如 intchar *)。此外,我更喜欢使用 sizeof(struct line),因为如果你写 sizeof(*array),它似乎在分配指针之前对其进行了解引用,如果它没有被初始化为 NULL,我敢打赌你会崩溃。 - Bemipefe
1
@Bemipefe sizeof(*array) 不会解引用。sizeof 是一个静态运算符,它检查其参数的类型。由于*array的类型是line,因此sizeof(*array)意味着sizeof(line) - Nikos C.
好的,明白了。这是因为它在编译时被评估,所以不会崩溃。无论如何,如果不了解这种语言的“内部”,这种语法都是具有误导性的。此外,如果您在函数内部更改类型,则还需要更改代码中的其他内容,因为可能会更改某些字段名称或某些字段大小,而默默地允许编译可能会导致一整套问题。 - Bemipefe
1
@Bemipefe C程序员经常使用sizeof(obj)而不是sizeof(type),这是一种非常常见的模式。显然,这是个人喜好问题。但请注意,当类型改变时,没有任何指示你需要同时更改sizeof的使用。无论你传递给其什么参数,sizeof都只会计算出一个大小。因此,即使得到的大小是错误的,所有内容都将编译正确且没有任何警告。这就是为什么很多人像这样使用sizeof的原因。 - Nikos C.
好的,你是对的,代码无论如何都会编译。当然这只是个人意见。我看不到代码中的语法,因为它只在那个上下文中有那个意义。也许如果你将calloc、malloc或realloc返回的指针显式转换为特定类型,你至少会从编译器得到一个警告。这可能取决于编译器和指定的选项。 - Bemipefe

8
根据您所描述的情况,您不需要为结构体分配内存,而是需要为成员变量char *addr;和char *inst;分配内存。如果您想要一个单一副本的结构体,第一段代码展示了如何初始化和赋值。如果您想要一个数组,第二个代码示例展示了不同之处。
这说明了如何为单个结构体行的成员变量分配内存:
typedef struct
{
    char* addr;
    char* inst;
}LINE;

LINE line;  

int main(void)
{   

    strcpy(line.addr, "anystring"); //will fail
    line.addr = malloc(80);
    line.inst = malloc(80);
    strcpy(line.addr, "someString");//success;
    strcpy(line.inst, "someOtherString");//success;

}

对于结构体数组行...

typedef struct
{
    char* addr;
    char* inst;
}LINE;  //same struct definition

LINE line[10]; //but create an array of line here.

int main(void)
{   
    int i;
    
    for(i=0;i<10;i++)
    {
      line[i].addr = malloc(80);
      line[i].inst = malloc(80);
    }

    for(i=0;i<10;i++)
    {
        strcpy(line[i].addr, "someString");
        strcpy(line[i].inst, "someOtherString");
    }
    //when done, free memory
    for(i=0;i<10;i++)
    {
        free(line[i].addr);
        free(line[i].inst);
    }      


}

添加以回应评论
针对@Adam Liss在此答案下的评论,以下代码演示了使用strdup()进行以下改进的方法:1)仅使用所需内存。2)在一步中执行内存创建和复制操作,因此以下块:

for(i=0;i<10;i++)
{
  line[i].addr = malloc(80);
  line[i].inst = malloc(80);
}

for(i=0;i<10;i++)
{
    strcpy(line[i].addr, "someString");
    strcpy(line[i].inst, "someOtherString");
}

成为:

for(i=0;i<10;i++)
{
  line[i].addr = strdup("someString");
  line[i].inst = strdup("someOtherString");
}

另外需要注意的一点:上面的示例中没有包含错误处理,以避免混淆主要概念的重点:但为了完整起见,因为malloc()strdup()都可能失败,每个函数的实际使用应在使用之前进行测试,例如:

而不是

  line[i].addr = strdup("someString");
  line[i].inst = strdup("someOtherString");

代码应该包括:
  line[i].addr = strdup("someString");
  if(!line[i].addr)
  {
      //error handling code here
  }
  line[i].inst = strdup("someOtherString");
  if(!line[i].inst)
  {
      //error handling code here
  }

2
或者使用 strdup(),并将剩余的80个字符用于更有用的事情。 :-) - Adam Liss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接