2016-01-06 153 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上的一个指向字符串文字或另一个数组的指针。