在C中的函数内的函数内分配一个字符串

时间:2015-09-03 15:20:51

标签: c string function

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

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

5 个答案:

答案 0 :(得分:1)

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

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

通过返回提供分配地址

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

#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);
    }
}

提供两者的灵活性

您不必将自己局限于一种方法或另一种方法。通过智能编码,您可以编写函数,以便它们可以直接用于更新指针返回新块的起始地址以进行赋值。 (注意:大小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

答案 1 :(得分: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);
}

答案 2 :(得分:0)

我一开始显然不明白你的问题,但如果你想做的是这个调用空指针上的一个函数,然后将它发送到另一个为指针分配内存然后在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;
}

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

答案 3 :(得分:0)

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

#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;
}

答案 4 :(得分:0)

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

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
}