[英]allocating a string inside a function inside a function in C
如果在調用func1之前我有一個char * str = null,它將其作為參數,而func1調用另一個函數(func2),該函數還將該字符串作為參數並進行分配和更改。 函數的簽名應該如下嗎?
void func1(char ** str)
void func2(char *** str)
要記住的規則是“如果要直接在調用的函數中進行分配,則必須將指針的地址傳遞給該函數”。 (你當然可以,沒有傳遞參數,只需要提供返回malloc
通過函數返回)第二,如果你沒有通過指針作為參數的地址,你將不得不返回一些值分配給字符串main
as 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
簽名不必具有“嵌套”參數類型,除非您計划更改在func1
主體中看到的str
的值,如下所示:
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);
}
我一開始顯然不明白您的問題,但是如果您要執行的操作是,此操作在null指針上調用一個函數,然后將其發送到另一個為該指針分配內存的函數,然后在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;
}
這不是最好的代碼,但可以完成工作
如果我正確理解了您的問題,則無需通過引用將字符串傳遞給第二個函數,因為它已經是您要修改的指針的引用。 請參見下面的示例。
#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
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.