C 中的结构实际上如何与 malloc 一起工作

How structures in C actually work with malloc

我正在尝试创建一个程序,其中包含一个结构,例如 "myStruct",其中包含一个字符串数组 "char* array"。然后是分配内存然后打印输出的函数,然后是如下所示的主要函数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct myStruct { char *array; } str1;

void display(str1 *p1) {
  char *array;
  array = (char *)malloc(10 * sizeof(char));
  printf("%s", p1->array);
  free(array);
}

int main(void) {
  str1 s1;
  strcpy(s1.array, "Damn");
  return 0;
}

但是我期望 "Damn" 作为输出,但我没有得到任何输出。 但是在编译过程中,显示​​以下警告:

user@localhost: ~ $ clang struct.c -Weverything
struct.c:7:6: warning: no previous prototype for function 'display' [-Wmissing-prototypes]
void display(str1 *p1) {
     ^
1 warning generated.

这意味着函数没有被调用。在这种情况下,我在语法和语义上都需要一些帮助。我不确定如何调用函数 display()。

您没有在 main() 中调用 display。给char *array分配内存,复制字符串,可以这样设计函数:

#define DEFAULT_LEN 10
void allocate(str1 *p1, const char * str) {
    char * array;
    if(str) {
        //(char *) is omittable in C (not C++)
        array = malloc(strlen(str) + 1);
        strcpy(array, str);
    }
    else {
        array = malloc(DEFAULT_LEN);
        array[0] = 0;
    }
    p1->array = array;
}

你可以像这样打印文本:

int main() {
    str1 s1; allocate(&s1, "Damn");
    puts(s1.array);
    deallocate(&s1);
    return 0;
}

您必须释放内存:

void deallocate(str1 *p1) {
    free(p1->array);
    p1->array = NULL;
}

str1 s1 s1分配在堆栈上,里面的数组只是一个指针,没有为其内容分配内存。

strcpy 然后尝试写入该指针指向的任何地方,它可能为零,也可能是随机的,具体取决于您的编译器及其设置。

您需要先为字符串分配一个缓冲区,例如:

str1 s1;
size_t len = strlen("Damn");
s1.array = malloc((len+1) * sizeof(char)); // +1 for the null terminator
strcpy(s1.array, "Damn");

让我们看看我们能否为您提供一些构建基础。当您声明 s1 时,您声明了一个 struct myStruct 类型的结构,它方便地具有 typedefstr1;声明:

str1 s1;

s1 创建存储,它具有 自动存储持续时间 并且仅当 s1 仍在范围内时才有效。 s1 的自动存储包括 一个指针 array 的存储,仅此而已。 (暂时忽略填充)。 one pointer array 是完全未初始化的,指向一些你没有能力写入的不确定地址。尝试写入未初始化的值会调用 Undefined Behavior 并且您的程序的有效操作会在此时停止。 (事实上​​,尝试写入它可能会导致分段错误)

如何为 s1.array 创建存储空间?这里就是你需要分配的地方。 ( 您尝试复制到 s1.array 之前)存储您的字符串需要多少内存? strlen(string) + 1(为 nul-terminating 字符提供存储的 +1)

知道这一点,并按照我在评论中留下的 link 进行操作,您可以使用以下方法为您的字符串分配存储空间:

s1.array = malloc (strlen ("Damn") + 1);

对于每次分配,在尝试使用内存块之前,您将验证分配是否成功。如果 malloc 失败(确实如此),您尝试使用无效块会使您回到原位,如果您根本没有分配 - 误入 未定义的行为 ...

现在您已将 s1.array 初始化为指向一个有效的内存块并且您已 验证 分配成功,您现在可以将字符串复制到 s1.array.

strcpy (s1.array, "Damn");

您的 display 函数无需分配任何内容(您当然可以)。您的 display 函数只需要显示现在保存在 s1.array 中的字符串。正如 Jonathan Leffler 提到的,您可以简单地将指针传递给 display(str1 *s),然后从内部输出 s->array。只需要:

void display (mystruct_t *s)
{
    printf ("%s\n", s->array);
}

您将从 main() 呼叫为:

     display (&s);           /* pass the address of s (a pointer to s) */

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址,因此,(2) 当不再需要时可以释放

当你从display()return时,s1.array的使用就结束了,可以释放了。现在养成释放您分配的内存的习惯,不要简单地依赖它在程序结束时被释放——随着您的代码变得更加复杂,这将带来好处。简单地说,

    free (s1.array);         /* if you allocate it, you free it when done */

并且由于 s1 具有自动存储功能,因此结构本身没有任何可用空间。

让我们举两个例子。第一个将声明具有 自动存储持续时间 的结构,并且仅为 s.array 分配(如上所述)。第二个将声明一个指向您的结构的指针,现在需要您为结构和数组分配。 (这又要求您释放数组和结构)

具有自动存储期限的结构

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STR "Damn"

typedef struct mystruct { 
    char *array;
} mystruct_t;

void display (mystruct_t *s)
{
    printf ("%s\n", s->array);
}

int main (void) {

    mystruct_t s;       /* struct with automatic storage - for the pointer */

    /* s is a struct, so you use the '.' operator access members */
    s.array = malloc (strlen (STR) + 1);    /* you must allocate for array */
    if (s.array == NULL) {                  /* validate each allocation */
        perror ("malloc-s.array");
        return 1;
    }
    strcpy (s.array, STR);  /* with valid memory pointed to, you can copy */

    display (&s);           /* pass the address of s (a pointer to s) */

    free (s.array);         /* if you allocate it, you free it when done */

    return 0;
}

声明一个指向结构的指针

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STR "Damn"

typedef struct mystruct { 
    char *array;
} mystruct_t;

void display (mystruct_t *s)
{
    printf ("%s\n", s->array);
}

int main (void) {

    mystruct_t *s;          /* declare a pointer to the struct itself */

    s = malloc (sizeof *s); /* now you must allocate for the struct */
    if (s == NULL) {        /* and you validate every allocation */
        perror ("malloc-s");
        return 1;
    }

    /* s is a pointer to struct, so you use -> to reference member */
    s->array = malloc (strlen (STR) + 1);   /* you must allocate for array */
    if (s->array == NULL) {                 /* validate each allocation */
        perror ("malloc-s->array");
        return 1;
    }
    strcpy (s->array, STR); /* with valid memory pointed to, you can copy */

    display (s);            /* s is a pointer, just pass it to display */

    free (s->array);        /* if you allocate it, you free it when done */
    free (s);               /* don't forget to free the struct */

    return 0;
}

(在这两种情况下,输出只是您的字符串)

仔细查看使用 '.'-> 运算符来取消引用结构和访问 array。您需要了解何时何地同时使用它们。

如果您还有其他问题,请告诉我。