[英]dynamic memory allocation with structs
似乎我不太了解使用指针进行内存分配的方式。
快速示例:
我有一个结构
struct Friends{
char *firstname;
char *lastname;
};
如果我现在分配内存,它将得到我
2x sizeof(char) = 2x 1Byte
但是,我需要的可用内存是否取决于我要填充的字符数?
例:
char array[10] needs 10x sizeof(char), 1byte for each character?
在我所看到的所有地方,他们在知道将用多少填充结构之前就分配了内存。
我有一个结构
struct Friends{ char *firstname; char *lastname; };
如果我现在分配内存,它将得到我
2x sizeof(char) = 2x 1Byte
您要在此处分配指针而不是char
的sizeof(char *)
。 根据系统的不同,它们分别是4字节(32位)或8字节(64位)。
因此,如果要在指针指向的位置分配数据,则必须使用malloc()
进行分配,然后再对其进行free()
分配。
或执行类似的操作:
struct Friends{
char firstname[20];
char lastname[20];
};
但是请确保您的字符串以\\0 char
结尾。
如果我正确理解了您的问题,则希望根据您对将来存储的要求,查看为firstname
和lastname
分配的内存。
恐怕这是不可能的。
当您得到类型为struct Friends
的变量时,说struct Friends F;
, F.firstname
和F.lastname
将是指针,但它们不会指向任何有效的内存。 您需要分别执行F.firstname
和F.lastname
的分配。 就像是
F.firstname = malloc(32);
F.lastname = malloc(32); //perform NULL check too
然后您实际上可以使用F.firstname
和F.lastname
在我所看到的所有地方,他们在知道将用多少填充结构之前就分配了内存。
如果我理解正确,您在问我们如何分配内存,然后他们才知道要使用多少空间。
考虑以下结构:
struct foo
{
char bar;
char foobar;
};
struct foo *temp = (struct foo *) malloc(sizeof(struct foo));
无论您是否将某些内容存储在bar
和foobar
变量中,此处都会动态分配2 * 1 = 2 bytes
。
如果不存储任何内容,则变量(内存位置)将包含抓取值。
在我所看到的所有地方,他们在知道将用多少填充结构之前就分配了内存。
是的,没有。
struct Friends {
char *firstname;
char *lastname;
};
这完全取决于您打算如何使用结构。 您可以通过多种方式使用struct Friends
。 你可以声明结构的静态实例,然后只要将现有的字符串到你的地址, firstname
和lastname
成员指针,例如:
int main (void) {
Struct Friends friend = {{Null}, {NULL]};
/* simple assignment of pointer address
* (memory at address must remain valid/unchanged)
*/
friend.firstname = argc > 1 ? argv[1] : "John";
friend.lastname = argc > 2 ? argv[2] : "Smith";
printf ("\n name: %s %s\n\n", friend.firstname, friend.lastname);
但是,在大多数情况下,您将需要创建信息的副本,并firstname
和lastname
成员存储字符串的副本。 在这种情况下,您需要为每个指针分配一个新的内存块,并为每个指针分配每个新块的起始地址。 在这里,您现在知道您的字符串是什么了,您只需要为每个字符串的长度分配内存(对于n 终止字符为+1
),例如:
int main (int argc, char **argv) {
/* declare static instance of struct */
struct Friends friend = {NULL, NULL};
char *first = argc > 1 ? argv[1] : "John";
char *last = argc > 2 ? argv[2] : "Smith";
/* determine the length of each string */
size_t len_first = strlen (first);
size_t len_last = strlen (last);
/* allocate memory for each pointer in 'friend' */
friend.firstname = malloc (len_first * sizeof *friend.firstname + 1);
friend.lastname = malloc (len_last * sizeof *friend.lastname + 1);
然后,您只需要将每个字符串复制到每个成员的地址即可:
/* copy names to new memory referenced by each pointer */
strcpy (friend.firstname, first);
strcpy (friend.lastname, last);
最后,一旦你所使用的内存分配完成后,你必须释放与内存free
。 注意:您只能free
以前使用malloc
或calloc
分配的malloc
。 切勿盲目尝试释放尚未分配的内存。 要释放成员,您需要做的是:
/* free allocated memory */
free (friend.firstname);
free (friend.lastname);
一个简单的示例将所有部分放在一起:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Friends {
char *firstname;
char *lastname;
};
int main (int argc, char **argv) {
/* declare static instance of struct */
struct Friends friend = {NULL, NULL};
char *first = argc > 1 ? argv[1] : "John";
char *last = argc > 2 ? argv[2] : "Smith";
/* determine the length of each string */
size_t len_first = strlen (first);
size_t len_last = strlen (last);
/* allocate memory for each pointer in 'friend' */
friend.firstname = malloc (len_first * sizeof *friend.firstname + 1);
friend.lastname = malloc (len_last * sizeof *friend.lastname + 1);
/* copy names to new memory referenced by each pointer */
strcpy (friend.firstname, first);
strcpy (friend.lastname, last);
printf ("\n name: %s %s\n\n", friend.firstname, friend.lastname);
/* free allocated memory */
free (friend.firstname);
free (friend.lastname);
return 0;
}
始终在启用警告的情况下进行编译,例如:
gcc -Wall -Wextra -o bin/struct_dyn_alloc struct_dyn_alloc.c
(如果不使用gcc
,那么您的编译器将具有类似的选项)
每当您在代码中动态分配内存时,都要通过内存错误检查程序运行,以确保您不会以某种方式滥用已分配的内存块,并确认所有内存都已释放。 这很简单。 所有操作系统都有某种类型的检查器。 valgrind
是Linux上的常规选择。 例如:
$ valgrind ./bin/struct_dyn_alloc
==14805== Memcheck, a memory error detector
==14805== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14805== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14805== Command: ./bin/struct_dyn_alloc
==14805==
name: John Smith
==14805==
==14805== HEAP SUMMARY:
==14805== in use at exit: 0 bytes in 0 blocks
==14805== total heap usage: 2 allocs, 2 frees, 11 bytes allocated
==14805==
==14805== All heap blocks were freed -- no leaks are possible
==14805==
==14805== For counts of detected and suppressed errors, rerun with: -v
==14805== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
因此,对于上面类似的类型,基本上有一个两步分配过程:
首先,分配一个类型为struct Friends
的对象,该对象包含两个指针的空间。
其次,为struct Friends
每个成员将指向的对象分配内存。
快速而肮脏的例子:
struct Friends *addFriend( const char *firstName, const char *lastName )
{
/**
* First, allocate an instance of `struct Friends`, which will contain
* enough space to store two pointers to `char`
*/
struct Friends *f = malloc( sizeof *f ); // sizeof *f == sizeof (struct Friends)
if ( f )
{
/**
* Allocate space to store a *copy* of the contents of the firstName
* parameter, assign the resulting pointer to the firstname member of
* the struct instance.
*/
f->firstname = malloc( strlen( firstName ) + 1 );
if ( f->firstname )
strcpy( f->firstname, firstName );
/**
* Do the same for lastName
*/
f->lastName = malloc( strlen( lastName ) + 1 );
if ( f->lastname )
strcpy( f->lastname, lastName );
}
return f;
}
如果我们将此函数称为
struct Friends newFriend = addFriend( "John", "Bode" );
我们在内存中得到以下内容:
+---+ +---+ +---+---+---+---+---+
newFriend:| | --> newFriend->firstname: | | ---->|'J'|'o'|'h'|'n'| 0 |
+---+ +---+ +---+---+---+---+---+
newFriend->lastname: | | -+
+---+ | +---+---+---+---+---+
+-->|'B'|'o'|'d'|'e'| 0 |
+---+---+---+---+---+
这是它在我的系统上的播放方式:
Item Address 00 01 02 03
---- ------- -- -- -- --
newFriend 0x7fffe6910368 10 20 50 00 ..P.
0x7fffe691036c 00 00 00 00 ....
*newFriend 0x502010 30 20 50 00 0.P.
0x502014 00 00 00 00 ....
0x502018 50 20 50 00 P.P.
0x50201c 00 00 00 00 ....
newFriend->firstname 0x502030 4a 6f 68 6e John
newFriend->lastname 0x502050 42 6f 64 65 Bode
newFriend
指针变量位于地址0x007fffe6910368
。 它指向位于地址0x502010
struct Friends
类型的对象。 这个对象足够大,可以存储两个指针值。 newFriend->firstname
位于地址0x502010
1并指向字符串"John"
,该字符串位于地址0x502030
。 newFriend->lastname
位于地址0x502018
并指向字符串"Bode"
,该字符串位于地址0x502050
。
要解除分配,请先释放成员,再释放struct对象:
void deleteFriend( struct Friends *f )
{
free( f->firstname );
free( f->lastname );
free( f );
}
彼此释放f->firstname
和f->lastname
顺序无关紧要; 重要的是在删除f
之前必须同时删除它们。 释放f
不会释放f->firstname
和f->lastname
指向2的内容 。
请注意,所有这些假设都为我提供了已知大小的数据( addFriend
的firstName
和lastName
参数); 我使用这些输入字符串的长度来确定我需要分配多少空间。
有时您不提前知道需要留出多少空间。 通常的方法是分配一些初始存储量,并根据需要使用realloc
函数对其进行扩展。 例如,假设我们有代码从输入流中读取一行以换行符结尾的文本。 我们希望能够处理任何长度的行,因此我们首先分配足够的空间来处理大多数情况,如果需要更多空间,我们会根据需要将缓冲区的大小加倍:
size_t lineSize = 80; // enough for most cases
char *line = calloc( lineSize, sizeof *line );
char buffer[20]; // input buffer for reading from stream
/**
* Keep reading until end of file or error.
*/
while ( fgets( buffer, sizeof buffer, stream ) != NULL )
{
if ( strlen( line ) + strlen( buffer ) >= lineSize )
{
/**
* There isn't enough room to store the new string in the output line,
* so we double the output line's size.
*/
char *tmp = realloc( line, 2 * lineSize );
if ( tmp )
{
line = tmp;
lineSize *= 2;
}
else
{
/**
* realloc call failed, handle as appropriate. For this
* example, we break out of the loop immediately
*/
fprintf( stderr, "realloc error, breaking out of loop\n" );
break;
}
}
strcat( line, buffer );
/**
* If we see a newline in the last input operation, break out of the
* loop.
*/
if ( strchr( buffer, '\n' ) )
break;
}
struct
的第一个元素的地址与整个struct
对象的地址相同;
C在第一个struct
成员之前不存储任何类型的元数据。
malloc
, calloc
或realloc
分配的对象进行free
调用;
您不会在指向字符串文字或其他数组的指针上调用free
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.