如果我在調用func1之前有一個char * str = null,它將其作爲參數並且func1調用另一個函數(func2),該函數也將該字符串作爲參數,分配和改變它。 函數的簽名應該如下?在函數內部的一個函數中分配一個字符串C
void func1(char ** str)
void func2(char *** str)
如果我在調用func1之前有一個char * str = null,它將其作爲參數並且func1調用另一個函數(func2),該函數也將該字符串作爲參數,分配和改變它。 函數的簽名應該如下?在函數內部的一個函數中分配一個字符串C
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);
}
我顯然沒有在第一次明白你的問題,但是如果你想怎麼辦,這是什麼呼籲空指針的函數,然後將其發送到爲指針分配內存,然後使用另一個函數主要的指針,然後是的,你的簽名是好的。 代碼示例:
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是函數 –
如果我正確理解你的問題,你不應該需要通過參考傳遞字符串第二個函數,因爲它已經是你想要修改的指針的引用。看下面的例子。
#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;
}
它會工作,但它不是最好的方法。你可以做這樣的事情:
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
}
要記住的是:「如果你要直接在一個名爲函數來分配,你必須通過地址的指針功能」的規則。 (當然,你可以不傳參數,只通過函數返回返回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);
}
}
提供的兩個
你不必限制自己一個方法或其他的靈活性。通過智能編碼,您可以編寫自己的函數,以便可以直接使用它們來更新指針和也會返回分配的新塊的起始地址。 (注:尺寸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'等呢? –
您的陳述是真實的。我正在回答OP希望通過'char ** str'或'char *** str'傳遞的問題。在沒有任何參數的情況下,只需返回'malloc'等的返回值即可。 –
你是對的,我不打算改變func1中的字符串,我可以在func2中返回它,但我想爲3個參數做它,也許我可以把它們放在一個結構中,然後返回它 –
@TamerMograbi把它們放在一個結構體中可能是最簡單的方法,它也可以正確地自我文檔化。 –