簡體   English   中英

具有結構的動態內存分配

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

您要在此處分配指針而不是charsizeof(char *) 根據系統的不同,它們分別是4字節(32位)或8字節(64位)。

因此,如果要在指針指向的位置分配數據,則必須使用malloc()進行分配,然后再對其進行free()分配。

或執行類似的操作:

struct Friends{
    char firstname[20]; 
    char lastname[20];
};

但是請確保您的字符串以\\0 char結尾。

如果我正確理解了您的問題,則希望根據您對將來存儲的要求,查看為firstnamelastname分配的內存。

恐怕這是不可能的。

當您得到類型為struct Friends的變量時,說struct Friends F; F.firstnameF.lastname將是指針,但它們不會指向任何有效的內存。 您需要分別執行F.firstnameF.lastname的分配。 就像是

F.firstname = malloc(32);
F.lastname = malloc(32);   //perform NULL check too

然后您實際上可以使用F.firstnameF.lastname

在我所看到的所有地方,他們在知道將用多少填充結構之前就分配了內存。

如果我理解正確,您在問我們如何分配內存,然后他們才知道要使用多少空間。

考慮以下結構:

struct foo
{
    char bar;
    char foobar;
};

struct foo *temp = (struct foo *) malloc(sizeof(struct foo));  

無論您是否將某些內容存儲在barfoobar變量中,此處都會動態分配2 * 1 = 2 bytes

如果不存儲任何內容,則變量(內存位置)將包含抓取值。

在我所看到的所有地方,他們在知道將用多少填充結構之前就分配了內存。

是的,沒有。

struct Friends {
    char *firstname; 
    char *lastname;
};

這完全取決於您打算如何使用結構。 您可以通過多種方式使用struct Friends 你可以聲明結構的靜態實例,然后只要將現有的字符串到你的地址, firstnamelastname成員指針,例如:

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);

但是,在大多數情況下,您將需要創建信息的副本,並firstnamelastname成員存儲字符串的副本。 在這種情況下,您需要為每個指針分配一個新的內存塊,並為每個指針分配每個新塊的起始地址。 在這里,您現在知道您的字符串是什么了,您只需要為每個字符串的長度分配內存(對於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以前使用malloccalloc分配的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)

因此,對於上面類似的類型,基本上有一個兩步分配過程:

  1. 首先,分配一個類型為struct Friends的對象,該對象包含兩個指針的空間。

  2. 其次,為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->firstnamef->lastname順序無關緊要; 重要的是在刪除f之前必須同時刪除它們。 釋放f 不會釋放f->firstnamef->lastname指向2的內容

請注意,所有這些假設都為我提供了已知大小的數據( addFriendfirstNamelastName參數); 我使用這些輸入字符串的長度來確定我需要分配多少空間。

有時您不提前知道需要留出多少空間。 通常的方法是分配一些初始存儲量,並根據需要使用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;
}


1. struct的第一個元素的地址與整個struct對象的地址相同; C在第一個struct成員之前不存儲任何類型的元數據。

2.請注意,您只能對通過malloccallocrealloc分配的對象進行free調用; 不會在指向字符串文字或其他數組的指針上調用free

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM