繁体   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