【问题标题】:c char * questionc 字符 * 问题
【发布时间】:2011-03-15 08:39:26
【问题描述】:

这是一个绝对菜鸟问题,但我似乎在任何地方都找不到合适的答案,所以在这里,给出这个代码:

#include <stdio.h>
#include <stdlib.h>

void initialize( char * buffer)
{
    buffer = malloc(1);
    *buffer='a';
}


int main(void) {
    char * buff;
    buff = malloc(sizeof(char));
    *buff = 'b';
    initialize(buff);
    puts("Contents of buffer are: ");
    puts(buff);
    return 0;
}

main 的输出总是'b',这对我来说真的很奇怪。我认为通过传递一个指针来初始化我可以修改在main中声明的指针的值,但由于某种原因,它似乎是按值传递变量,并且在返回时我有main中指定的值,因此是'b' .

我想知道这是为什么...我应该传递对指针的引用吗?类似 char *& ??

问候, 亚历克斯

【问题讨论】:

  • 当您已经分配到 main 中的同一个指针但还没有释放它时,为什么还要在 initialize 中调用 malloc
  • 仅用于测试目的...试图了解内存分配在 c 中的工作原理:D

标签: c char


【解决方案1】:

您在函数中做的第一件事是将内存中的另一个位置重新分配给buffer,并使用新的malloc。然后你修改这个新的内存位置,但是 main 函数中的buff 没有被修改,因为它仍然指向旧的位置。

如果您删除 malloc,它将起作用:

void initialize( char * buffer) {
    *buffer='a';
}

现在我们正在修改初始内存位置。

【讨论】:

  • +1 而且,为了让 OP 了解正在发生的事情,指针是通过副本传递的,因此它与您在 main 中声明的指针完全不同,但它仍然指向相同的内存位置。因此,修改 它已经指向的内容 可以按预期工作,但修改指针本身只是修改传递给函数的副本。如果需要修改指针,则必须将指针传递给指针,即char**,然后将 malloc 的返回值设置为等于*buffer
  • 这是正确的,我试过了,它有效,我忘记了 malloc 返回一个指针,所以是的,我正在修改另一个内存位置。谢谢 Krtek!
  • 这转到 Ed S. :如果我理解正确,当调用初始化时会复制原始指针,现在我有两个指针指向同一个内存地址?我想我应该选择 Jeff M 提供的解决方案。
  • C 中的所有内容都是按值传递的,甚至是指针,所以是的,您需要传递一个指向 char 的指针才能更改原始指针。
  • 请注意,如果 C 对参数使用“传递引用”,这可能会像您预期的那样工作。但是它没有(它使用按值传递,就像大多数常见的编程语言一样),因此这不起作用。参见例如en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference
【解决方案2】:

如果首选没有内存泄漏的无错误 sn-p,这里有一些已发布内容的替代方案,更适用于现实世界中的编程。

请注意,mallocs 也应该释放相同的“代码模块”。 不要在一个模块中 malloc 并在另一个模块中释放,这是非常糟糕的做法!

版本 1:

#include <stdio.h>
#include <stdlib.h>

void set (char* buffer)
{
    *buffer='a';
}


int main (void)
{
    char* buff;

    buff = malloc(sizeof(char));  /* main allocates */
    *buff = 'b';

    set (buff);

    puts("Contents of buffer are: ");
    printf("%c", *buff);  /* don't use puts on single characters! */

    free(buff); /* main cleans up its own mess */
    return 0;
}

版本 2:

/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include "my_functions.h"


int main (void)
{
    char * buff;

    buff = malloc(sizeof(char));
    *buff = 'b';

    alloc_and_set (&buff);

    puts("Contents of buffer are: ");
    printf("%c", *buff);

    cleanup();
    return 0;
}

my_functions.h

#ifndef MY_FUNCTIONS_H
#define MY_FUNCTIONS_H

void alloc_and_set (char** buffer);
void cleanup (void);

#endif /* MY_FUNCTIONS_H */

my_functions.c

#include "my_functions.h"

static char* internal_buffer = NULL;  /* used to keep track of allocated data */

void alloc_and_set (char** buffer)
{
  if(internal_buffer == NULL)  /* make function memory leak proof */
  {
    internal_buffer = malloc(sizeof(char));
  }

  *internal_buffer = 'a';
  *buffer = internal_buffer;
}

void cleanup (void)
{
    free(internal_buffer);   /* my_functions module cleans up its own mess */
    internal_buffer = NULL;
}

【讨论】:

  • 哇,很棒的补充,感谢 Lundin!会记住这些建议:)
【解决方案3】:

您需要修改 initialize() 函数以获取指向缓​​冲区的指针并传入指针的地址。尽管它接收您传入的缓冲区,但您无法更改 buff 指向的内容。请注意,在这种情况下您会发生内存泄漏,并且您不能将内容打印为字符串,因为它没有正确地以 null 终止。

void initialize(char **buffer)
{
    *buffer = malloc(sizeof(char));
    **buffer='a';
}

int main(void) {
    char *buff;
    buff = malloc(sizeof(char));
    *buff = 'b';
    initialize(&buff);
    puts("Contents of buffer are: ");
    printf("%c\n", *buff);
    return 0;
}

对以下 cmets 的回答:

我不明白为什么仍然需要指向指针的指针...这像 C++ 中的引用吗?

当我阅读您的代码时,我认为您想更改 buff 变量(在 main() 中)所指向的内容,一个新的缓冲区。我以为当我看到您在 initialize() 函数中分配一个新缓冲区时。为了能够从不同的位置(或函数)更改变量的值,您需要该变量的地址。由于它最初是一个字符指针char *,因此您需要一个指向该字符指针char ** 的指针。是的,这就像 C++ 中的引用,只是更冗长。

那么我可以修改传递的变量的值吗?

如果您打算只修改传入的缓冲区的内容,Krtek 将涵盖这一点。您需要做的就是忽略缓冲区的新分配并修改缓冲区指向的内容。

void initialize(char *buffer)
{
    /* *buffer = malloc(sizeof(char)); */ /* do not need this */
    /* modify the buffer that was passed in */
    *buffer='a';
}

关于内存泄漏问题

实际上,内存泄漏是由于分配了一个新的缓冲区而没有释放之前分配的内存造成的。你实际上有这个:

char *buff = malloc(sizeof(char)); /* allocate some memory */
buff = malloc(sizeof(char));       /* oops, memory leak */

你的意思是使用非空终止字符串的puts?这就是你使用 printf("%c\n", *buff) 的原因吗?强制字符串以 \n?

结尾

这是一个不同的问题。 puts() 接受一个以 null 结尾的字符串并将其打印到标准输出,然后是一个新行。打印潜在的垃圾文本不会是内存泄漏,它只是......别的东西。您的缓冲区仅包含单个字符的空间(没有空终止符)。因此,您不应该使用puts() 来打印缓冲区的内容。您应该只打印那个单个字符。将printf()'\n' 一起使用最终会得到与puts() 相同的输出行为。

【讨论】:

  • 我认为 OP 只是想更改已分配内存的值,而不是分配新空间。考虑到这个问题,指针的指针可能暂时有点太多了;)
  • 这假设您实际上想要分配一个新缓冲区,就像您在代码中一样。否则,Krtek 就是您要找的。​​span>
  • @Krtek:当然,直到我注意到你的回答后,我才真正意识到这一点。 :)
  • 为什么还要在互联网上发布包含内存泄漏的代码?
  • @Lundin:我回答这个问题的印象是 Alejandro 正在学习内存管理的细节。我本可以修复它并完成它,但如果我们只是不承认他的代码存在问题的事实,这对他没有任何好处。我相信他(和其他任何人)会以这种方式从中学到更多。
【解决方案4】:

您修改了指针的副本以指向另一个地方,但您的真实指针并未指向它。 如果您不想让您的代码正常工作,请使用此功能。

void initialize( char ** buffer)
{
    *buffer = malloc(1);
    **buffer='a';
}

然后拨打initialize(&amp;buff);

【讨论】:

    【解决方案5】:

    当你这样做时

    buffer = malloc(1);
    

    最初从 main 传递的缓冲区变量不受影响,因为它是按值传递的。

    如果你这样写:

    void initialize( char **buffer)
    {
        *buffer = malloc(1);
        **buffer='a';
    }
    

    然后你会看到变化。

    Edit1:您需要了解 * 运算符。 buffer 本身就是一个指针。 *buffer实际上是buffer指向的位置,改变它实际上并不影响buffer。

    【讨论】:

      【解决方案6】:
      void initialize( char * buffer)
      {
      
          // buffer is a local pointer to this function which will have 
          // same value as buff in main AT THIS POINT.
      
          // now you call malloc to allocate memory and make buffer
          // point to that memory. So the value of buffer got changed now.
          // buff in main remains unchanges.
          buffer = malloc(1);
      
          // now you write to the memory allocated.
          *buffer='a';
      
          // key point is buff in main is still pointing where it was.
      }
      
      
      int main(void) {
      
          // buff is now a wild pointer.
          char * buff;
      
          // now you allocate memory using malloc and make buff point to that memory.
          buff = malloc(sizeof(char));
      
          // you write to the memory pointed to by buff.
          *buff = 'b';
      
          // you pass buff BY VALUE to the function initilize.
          initialize(buff);
      
          // since a copy of buff (buffer) was changed, buff still points
          // to a memory that has b in it.
          puts("Contents of buffer are: ");
          puts(buff);
          return 0;
      }
      

      如果你想让被调用的函数影响buff的值,你需要传递它的地址:

      void initialize( char ** buffer)
      {
          *buffer = malloc(1);
          **buffer='a';
      }
      

      并将其称为:

      initialize(&buff);
      

      但请注意,这会泄漏 main 中分配的内存。由于您已经在 main 中分配了内存,因此您可以删除 malloc:

      void initialize( char * buffer)
      {
          *buffer='a';
      }
      

      【讨论】:

      • 感谢codaddict的详细解释!那么我假设第一次调用 malloc 如果之后没有管理会导致内存泄漏?
      • @AlejandroVK:是的。就像这样做:char *ptr = malloc(1); ptr = malloc(1) 无法获取通过第一个 malloc 分配的内存,这称为内存泄漏。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多