【问题标题】:dynamic memory allocation with structs使用结构进行动态内存分配
【发布时间】:2016-04-10 23:57:26
【问题描述】:

似乎我并没有真正理解使用指针分配内存的工作原理。

快速示例:

我有一个结构,

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

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

    2x sizeof(char) = 2x 1Byte

但是,我需要的空闲内存不取决于我填充多少个字符吗?

示例:

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

在他们知道将用多少填充结构之前,他们在我所见的任何地方分配内存。

【问题讨论】:

  • 2x sizeof(char) = 2x 1Byte 应该是 2x sizeof(char *) - 这取决于平台
  • 所有假设都是错误的。 malloc(sizeof(Friends)) 将分配(至少)2 * sizeof(char*) - 注意*。它为指针分配空间,而不是为字符分配空间。如果你想用一些东西填充它们,你将不得不执行额外的分配。
  • 非常感谢,我现在知道了!
  • 如果你发现你没有为malloc分配足够的内存,你可以使用realloc来增加(或减少)它的大小。不要忘记允许字符串终止符。

标签: c arrays pointers memory memory-management


【解决方案1】:

我有一个结构,

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

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

2x sizeof(char) = 2x 1Byte

您在sizeof(char *) 分配指针,而不是char。根据系统,它们分别是 4 字节(32 位)或 8 字节(64 位)。

因此,如果您想要指针指向的数据,则必须分配该数据,例如malloc(),稍后再分配free()

或者做类似的事情:

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

但请确保您的字符串以 \0 char 结尾。

【讨论】:

    【解决方案2】:

    如果我正确理解了您的问题,您希望根据您对未来存储的要求查看为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

    【讨论】:

      【解决方案3】:

      在他们知道将用多少填充结构之前,他们在我所见的任何地方分配内存。

      如果我理解正确,您是在问我们如何在他们知道我们要使用多少空间之前分配内存。

      考虑以下结构:

      struct foo
      {
          char bar;
          char foobar;
      };
      
      struct foo *temp = (struct foo *) malloc(sizeof(struct foo));  
      

      无论您是否在barfoobar 变量中存储某些内容,这里都会动态分配2 * 1 = 2 bytes

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

      【讨论】:

        【解决方案4】:

        在他们知道多少之前,他们在我所见的任何地方分配内存 他们将填充结构。

        是和不是。

        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 成员创建信息的副本并存储字符串的副本。在这种情况下,您需要为每个指针分配一个新的内存块,并将每个新块的起始地址分配给每个指针。在这里,您现在知道您的字符串是什么,您只需为每个字符串的长度分配内存(+1 用于 nul-terminating 字符)例如:

        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,那么你的编译器会有类似的选项)

        每当您在代码中动态分配内存时,请通过内存错误检查程序运行,以确保您不会以某种方式滥用分配的内存块,并确认所有内存已被释放。做起来很简单。所有操作系统都有某种类型的检查器。 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)
        

        【讨论】:

          【解决方案5】:

          因此,对于上述类型,您基本上有一个两步分配过程:

          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。它指向struct Friends 类型的对象,该对象位于地址0x502010。这个对象足够大,可以存储两个指针值; newFriend-&gt;firstname 位于地址 0x5020101 并指向位于地址 0x502030 的字符串 "John"newFriend-&gt;lastname 位于地址 0x502018 并指向字符串 "Bode",该字符串位于地址 0x502050

          要解除分配,在释放结构对象之前释放成员:

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

          f-&gt;firstnamef-&gt;lastname 彼此释放的顺序无关紧要;重要的是您必须先删除它们,然后才能删除f。释放 f 不会释放 f-&gt;firstnamef-&gt;lastname 指向2的内容。

          请注意,这一切都假设我得到了已知大小的数据(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

          【讨论】:

            猜你喜欢
            • 2012-03-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-11-12
            • 1970-01-01
            • 2014-11-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多