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
类型的结构,它方便地具有 typedef
到 str1
;声明:
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
。您需要了解何时何地同时使用它们。
如果您还有其他问题,请告诉我。
我正在尝试创建一个程序,其中包含一个结构,例如 "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
类型的结构,它方便地具有 typedef
到 str1
;声明:
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
。您需要了解何时何地同时使用它们。
如果您还有其他问题,请告诉我。