【问题标题】:allocating a string inside a function inside a function in C在 C 中的函数内部分配函数内部的字符串
【发布时间】:2015-09-03 15:20:51
【问题描述】:

如果我在调用 func1 之前有一个 char *str=null ,它将它作为参数,而 func1 调用另一个函数 (func2),该函数也将这个字符串作为参数并分配和更改它。 函数的签名应该如下吗?

   void func1(char ** str)
   void func2(char *** str)

【问题讨论】:

    标签: c string function


    【解决方案1】:

    签名不必具有“嵌套”参数类型,除非您打算更改 str 的值,如在 func1 的正文中所见,如下所示:

    void func1(char ** str) {
      func2(&str);
      assert(str == 0);
    }
    void func2(char *** str) {
      *str = 0;
    }
    

    否则,char ** str 就足够了,尽管可能仍然没有必要:

    void func1(char ** str) {
      func2(str);
    }
    void func2(char ** str) {
      *str = strdup("");
    }
    int main() {
      char * str = NULL;
      func1(&str);
      assert(str != NULL);
      free(str);
    }
    

    不过,理想情况下,如果func2 总是分配一个字符串,它应该返回它:

    // https://github.com/KubaO/stackoverflown/tree/master/questions/cstr-alloc-32379663
    #include <string.h>
    #include <stdlib.h>
    
    /// Returns a newly allocated "foo". The user must free it.
    char* func2(void) {
      return strdup("foo");
    }
    

    同样,如果func1 总是分配字符串,它也应该简单地返回它:

    /// Returns a newly allocated "foobar". The user must free it.
    char* func1(void) {
      char* str1 = func2();
      const char str2[] = "bar";
      char* str = malloc(strlen(str1) + sizeof(str2));
      strcat(strcpy(str, str1), str2);
      free str1;
      return(str);
    }
    
    int main() {
      char* str = func1();
      printf("%s\n", str);
      free(str);
    }
    

    【讨论】:

    • 是的,你是对的,我不打算更改 func1 中的字符串,我可以在 func2 中返回它,但我想为 3 个参数执行此操作,也许我可以将它们放在一个结构中并返回它
    • @TamerMograbi 将它们放在一个结构中可能是最简单的方法,它也可以正确地自我记录。
    【解决方案2】:

    要记住的规则是“如果要直接在被调用函数中分配,则必须将指针的地址传递给函数”。 (当然,你可以不传递参数,只通过函数 return 提供malloc 的返回值)其次,如果你将指针的地址作为参数传递,你将拥有返回一些值以分配给main 中的字符串,因为str 被声明为NULL 指针。 (它是一个空指针——它本身有一个地址,但它什么也不指向)返回 char * 类型还是 void * 类型取决于你。 (它们都是对内存地址的简单引用)。

    在被调用函数中分配的主要方法有两种:(1) 通过函数返回为分配的内存块提供起始地址,或 (2) 将地址传递给指针并直接在被调用函数中分配。

    通过返回提供分配地址

    如果您打算使用返回来为新分配的内存块提供起始地址,则没有理由向函数传递任何内容。但是,您必须返回一个指向新分配的内存块开始的指针。一个简单的例子会有所帮助:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define ASIZE 16
    
    void *func1 ();
    void *func2 ();
    
    int main (void) {
    
        char *str = NULL;
    
        str = func1 ();
        strncpy (str, "hello, world!", ASIZE);
        printf ("\n %s\n\n", str);
    
        free (str);
    
        return 0;
    }
    
    void *func1 ()
    {
        return func2 ();
    }
    
    void *func2 ()
    {
        char *p = malloc (ASIZE);
    
        if (!p) {
            fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
            return NULL;
        }
    
        return p;
    }
    

    将地址直接分配给指针

    您可以使用的下一个选项是将 地址 传递给您希望在被调用函数中分配的指针。您的函数类型可以简单地为void,因为您不依赖于返回。

    新内存块的起始地址直接分配给被调用函数中指针的地址。它在main 中可用,因为指针在函数中直接更新。 (这就是为什么你必须传递指针的地址,如果你只是传递指针本身,函数会接收到指针的副本 [存储在不同的地址] -- 所以与main中的指针地址没有关系)

    例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define ASIZE 16
    
    void func1 (char **str);
    void func2 (char **str);
    
    int main (void) {
    
        char *str = NULL;
    
        func1 (&str);
        strncpy (str, "hello, world!", ASIZE);
        printf ("\n %s\n\n", str);
    
        free (str);
    
        return 0;
    }
    
    void func1 (char **str)
    {
        func2 (str);
    }
    
    void func2 (char **str)
    {
        if (!(*str = malloc (ASIZE))) {
            fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
            exit (EXIT_FAILURE);
        }
    }
    

    兼具灵活性

    您不必将自己限制在一种方法或另一种方法上。通过智能编码,您可以编写函数,以便它们可用于直接更新指针返回新块的起始地址以进行赋值。 (注意:size sz 在此示例中作为参数传递,而不是使用 #define):

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void *func1 (char **str, size_t sz);
    void *func2 (char **str, size_t sz);
    
    int main (void) {
    
        char *str1 = NULL;
        char *str2 = NULL;
        size_t sz = 16;
    
        func1 (&str1, sz);
        str2 = func1 (&str2, sz);
    
        strncpy (str1, "hello, world!", sz);
        strncpy (str2, "hello, Stack!", sz);
    
        printf ("\n %s\n", str1);
        printf (" %s\n\n", str2);
    
        free (str1);
        free (str2);
    
        return 0;
    }
    
    void *func1 (char **str, size_t sz)
    {
        return func2 (str, sz);
    }
    
    void *func2 (char **str, size_t sz)
    {
        if (!(*str = malloc (sz))) {
            fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
            return NULL;
        }
    
        return *str;
    }
    

    当然,所有示例都只打印hello, world!。 (以及最后一个示例中的hello Stack!注意:无论您将sz 作为参数传递还是使用#define,您都必须提供malloc(或calloc)的数量要分配的字节。 (由你决定)。

    现在的重点是使用内存错误检查器(例如 valgrind 或类似工具)验证您的内存使用情况:

    内存检查

    $ valgrind ./bin/layeredalloc
    ==28513== Memcheck, a memory error detector
    ==28513== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
    ==28513== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
    ==28513== Command: ./bin/layeredalloc
    ==28513==
    
     hello, world!
     hello, Stack!
    
    ==28513==
    ==28513== HEAP SUMMARY:
    ==28513==     in use at exit: 0 bytes in 0 blocks
    ==28513==   total heap usage: 2 allocs, 2 frees, 32 bytes allocated
    ==28513==
    ==28513== All heap blocks were freed -- no leaks are possible
    ==28513==
    ==28513== For counts of detected and suppressed errors, rerun with: -v
    ==28513== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
    

    通过使用valgrind,您可以确保所有分配的内存实际上都已释放,更重要的是您对内存的使用是正确的:

    ERROR SUMMARY: 0 errors from 0 contexts
    

    【讨论】:

    • “如果你要在一个被调用的函数中分配,你必须将指针的地址传递给函数”——但前提是你在可选上死机当你这样做时修改一些现有的指针,对吧?否则,为什么不直接返回指针,像mallocstrdup等呢?
    • 您的说法是正确的。我正在回答 OP 希望通过char **strchar ***str 提出的问题。在没有任何参数的情况下,只需返回 malloc 等的返回值。
    【解决方案3】:

    它会起作用,但这不是最好的方法。你可以这样做:

    int main(){
        char *str = null;
        func1(&str); // There you are passing the address of the pointer to the first char in the string
    }
    
    void func1(char** str){
        func2(str); // In this function str is a pointer to the address of the pointer to the string, so passing it the other function will can modify the string
    }
    
    void func2(char** str){
    // some code
    }
    

    【讨论】:

      【解决方案4】:

      如果我正确理解了您的问题,则您不需要通过引用第二个函数来传递字符串,因为它已经是对您要修改的指针的引用。请参阅下面的示例。

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      void func2(char **str)
      {
          /* allocate the string and populate it. It can be populated from func1 or main as well,
             provided this was called first to allocate the string. */
          *str = malloc(20 * sizeof(char));
          strcpy(*str, "Hello, World!");
      }
      
      void func1(char **str)
      {
          /* other stuff presumably */
      
          /* allocate some data to the string. no need to pass this by reference, 
             we already have the address of the pointer we want to modify. */
          func2(str);
      
          /* more stuff presumably */
      }
      
      int main()
      {
          char *str = NULL;
      
          /* Need to allocate data and store the location in the variable 'str'.
             Pass by reference so func1/func2 can modify the value (memory address)
             stored in the 'str' variable. */
          func1(&str);
      
          printf("%s\n", str);
      
          return 0;
      }
      

      【讨论】:

        【解决方案5】:

        一开始我显然不明白你的问题,但如果你想做的是这调用空指针上的一个函数,然后将它发送到另一个为指针分配内存的函数,然后使用 main 上的指针, 那么是的,你的签名很好。 代码示例:

        void f(char** p){g(&p);}
        void g(char*** q){**q = malloc(4); strcpy(**q,"abc");}
        int main(){
          char * p = 0;
          f(&p);
          printf("%s",p);
          return 0;
        }
        

        这不是最好的代码,但它可以完成工作

        【讨论】:

        • 计划使用malloc,因为函数之前str等于null
        猜你喜欢
        • 2011-09-26
        • 2013-10-31
        • 1970-01-01
        • 2011-11-11
        • 2023-01-10
        • 2013-10-20
        • 1970-01-01
        • 1970-01-01
        • 2016-07-29
        相关资源
        最近更新 更多