结构的动态内存分配

时间:2016-01-06 19:09:06

标签: c arrays pointers memory memory-management

似乎我并不真正理解指针的内存分配是如何工作的。

快速举例:

我有一个结构,

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

如果我现在分配内存,它会让我

    2x sizeof(char) = 2x 1Byte

但是,我需要的空闲内存是否取决于我填充的字符数?

示例:

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

在他们知道他们将用它填充结构的程度之前,无处不在我看他们分配内存。

5 个答案:

答案 0 :(得分: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结尾。

答案 1 :(得分:0)

如果我理解您的问题,您希望根据您对未来存储的要求,查看为firstnamelastname分配的内存。

我害怕,这是不可能的。

当你得到struct Friends类型的变量时,说struct Friends F;F.firstnameF.lastname将成为指针,但它们不会指向任何有效< / em>记忆。您需要分别执行F.firstnameF.lastname的分配。像

这样的东西
F.firstname = malloc(32);
F.lastname = malloc(32);   //perform NULL check too

然后您实际上可以使用F.firstnameF.lastname

答案 2 :(得分:0)

  

在他们知道他们将用它填充结构的程度之前,无处不在我看他们分配内存。

如果我理解得当你知道我们如何在他们知道我们将使用多少空间之前分配内存。

考虑以下结构:

struct foo
{
    char bar;
    char foobar;
};

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

无论您是否在2 * 1 = 2 bytesbar变量中存储内容,都会动态分配foobar

如果您不存储任何内容,则变量(内存位置)包含草料值。

答案 3 :(得分: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成员的字符串副本。在这种情况下,您需要为每个指针分配一个新的内存块,并为每个指针分配每个新块的起始地址。在这里,您现在知道您的字符串是什么,并且您只需要为每个字符串的长度分配内存( nul-terminatedating 字符为+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释放内存。 注意:您只能使用freemalloc分配的calloc内存。永远不要盲目地尝试释放那些没有分配的内存。要释放成员,您只需要:

    /* 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)

答案 4 :(得分:0)

所以对于像你这样的类型,你基本上有一个两步分配过程:

  1. 首先,您分配一个struct Friends类型的对象,其中包含两个指针的空间;

  2. 其次,为struct Friends的每个成员指向的对象分配内存。

  3. 快速而肮脏的例子:

    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。它指向struct Friends类型的对象,该对象位于地址0x502010。该对象足以存储两个指针值; newFriend->firstname住在地址0x502010 1 ,并指向字符"John",该字符串位于地址0x502030newFriend->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

    请注意,这一切都假设我已经获得了已知大小的数据(firstName中的lastNameaddFriend参数);我使用这些输入字符串的长度来确定我需要分配多少空间。

    有时你不提前知道需要留出多少空间。通常的方法是分配一些初始存储量,并根据需要使用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.请注意,您只能在使用freemalloccalloc分配的对象上调用realloc;你将在指向字符串文字或另一个数组的指针上调用free