【发布时间】:2015-09-03 15:20:51
【问题描述】:
如果我在调用 func1 之前有一个 char *str=null ,它将它作为参数,而 func1 调用另一个函数 (func2),该函数也将这个字符串作为参数并分配和更改它。 函数的签名应该如下吗?
void func1(char ** str)
void func2(char *** str)
【问题讨论】:
如果我在调用 func1 之前有一个 char *str=null ,它将它作为参数,而 func1 调用另一个函数 (func2),该函数也将这个字符串作为参数并分配和更改它。 函数的签名应该如下吗?
void func1(char ** str)
void func2(char *** str)
【问题讨论】:
签名不必具有“嵌套”参数类型,除非您打算更改 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);
}
【讨论】:
要记住的规则是“如果要直接在被调用函数中分配,则必须将指针的地址传递给函数”。 (当然,你可以不传递参数,只通过函数 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
【讨论】:
malloc、strdup等呢?
char **str 或char ***str 提出的问题。在没有任何参数的情况下,只需返回 malloc 等的返回值。
它会起作用,但这不是最好的方法。你可以这样做:
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
}
【讨论】:
如果我正确理解了您的问题,则您不需要通过引用第二个函数来传递字符串,因为它已经是对您要修改的指针的引用。请参阅下面的示例。
#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;
}
【讨论】:
一开始我显然不明白你的问题,但如果你想做的是这调用空指针上的一个函数,然后将它发送到另一个为指针分配内存的函数,然后使用 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;
}
这不是最好的代码,但它可以完成工作
【讨论】: