2016-01-06 137 views
1

看來我似乎並不真正瞭解指針的內存分配是如何工作的。帶結構的動態內存分配

簡單的例子:

我有一個結構,

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

,如果我現在要做的分配內存,它會讓我

2x sizeof(char) = 2x 1Byte 

但是,那並不空閒內存,我需要依賴多少個字符我填寫?

例如:

char array[10] needs 10x sizeof(char), 1byte for each character? 

我到處看看他們分配內存,他們知道多少,他們將填補結構之前。

+0

'2倍的sizeof(char)的2倍=應1Byte'是'2倍的sizeof(字符*)' - 這取決於平臺 –

+1

所有的假設都是錯誤的。 'malloc(sizeof(Friends))'會分配(至少)'2 * sizeof(char *)' - 介意'*'。它爲指針分配空間,而不是字符。如果你想填充一些東西,你將不得不執行額外的分配。 –

+0

非常感謝,我現在明白了! – Scarh

回答

1

我有一個結構,

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結束。

0

如果我正確地理解了您的問題,則希望根據您對未來存儲的要求,查看分配給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

0

我到處看看他們分配內存,他們知道多少,他們將填補結構之前。

如果我理解得當,你會問我們如何在知道我們要使用多少空間之前分配內存。

考慮以下結構:

struct foo 
{ 
    char bar; 
    char foobar; 
}; 

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

這裏2 * 1 = 2 bytes將您是否存儲在barfoobar變量或不是動態分配。

如果你沒有存儲任何東西,那麼變量(內存位置)包含垃圾值。

0

無處不看,他們分配內存,然後才知道他們將填補多少 。

是和否。

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成員的字符串副本。在這種情況下,您需要爲每個指針分配一個新的內存塊,並將每個新塊的起始地址分配給每個指針。在這裏,你現在知道你的字符串是什麼,你只需要爲每個字符串的長度分配內存(+1NUL終止字符),例如:

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。切勿盲目地嘗試釋放未被分配的內存。要釋放的成員,所有你需要的是:

/* 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,那麼你的編譯器將有類似的選項)

只要在代碼中動態分配內存,運行內存錯誤檢查程序以確保您不會以某種方式濫用分配的內存塊,並確認所有內存已被釋放。這很容易做到。所有操作系統都有一些類型的檢查器。在Linux上,valgrind是正常的選擇。例如:

$ 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) 
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處。它指向地址爲0x502010struct Friends類型的對象。該對象足夠大以存儲兩個指針值; newFriend->firstname住在地址0x502010 並指向字符串"John",該字符串居住在地址0x502030newFriend->lastname住在地址0x502018並指向字符串"Bode",該地址位於地址0x502050

解除分配,就釋放成員釋放的結構對象之前:

void deleteFriend(struct Friends *f) 
{ 
    free(f->firstname); 
    free(f->lastname); 
    free(f); 
} 

不要緊什麼順序免費f->firstnamef->lastname相對於對方;重要的是你必須先刪除它們,然後才能刪除f。釋放f將不會免費什麼f->firstnamef->lastname指向。

請注意,這一切都假設我正在給出已知大小的數據(addFriend中的firstNamelastName參數);我使用這些輸入字符串的長度來確定需要分配多少空間。

有時候你不會提前知道需要留出多少空間。通常的做法是分配一些初始數量的存儲空間,並根據需要使用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上的一個指向字符串文字或另一個數組的指針。