[英]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.