簡體   English   中英

在C中通過引用傳遞字符串

[英]pass strings by reference in C

我無法弄清楚如何通過函數的參數傳回字符串。 我是編程新手,所以我想這可能是一個初學者的問題。 你能給予的任何幫助都將非常感激。 這段代碼出錯了,我不知道為什么,但是我提供的代碼來展示我到目前為止所擁有的內容。

我已將其設為社區維基,因此請隨意編輯。

PS這不是功課。

這是原始版本

#include <stdio.h>

#include <stdlib.h>
#include <string.h>

void
fn(char *baz, char *foo, char *bar)
{
     char *pch;

     /* this is the part I'm having trouble with */

     pch = strtok (baz, ":");
     foo = malloc(strlen(pch));
     strcpy(foo, pch);

     pch = strtok (NULL, ":");
     bar = malloc(strlen(pch));
     strcpy(bar, pch);

     return;
}

int
main(void)
{
     char *mybaz, *myfoo, *mybar;

     mybaz = "hello:world";

     fn(mybaz, myfoo, mybar);

     fprintf(stderr, "%s %s", myfoo, mybar);
}

更新這是一個更新版本,其中包含一些建議:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLINE         1024

void
fn(char *baz, char **foo, char **bar)
{
     char line[MAXLINE];
     char *pch;

     strcpy(line, baz);

     pch = strtok (line, ":");
     *foo = (char *)malloc(strlen(pch)+1);
     (*foo)[strlen(pch)] = '\n';
     strcpy(*foo, pch);

     pch = strtok (NULL, ":");
     *bar = (char *)malloc(strlen(pch)+1);
     (*bar)[strlen(pch)] = '\n';
     strcpy(*bar, pch);

     return;
}

int
main(void)
{
     char *mybaz, *myfoo, *mybar;

     mybaz = "hello:world";

     fn(mybaz, &myfoo, &mybar);

     fprintf(stderr, "%s %s", myfoo, mybar);

     free(myfoo);
     free(mybar);
}

首先,那些mallocs應該是strlen(whatever)+1字節。 C字符串有一個0字符表示結尾,稱為NUL終結符,並且它不包含在strlen測量的長度中。

接下來,strtok修改你正在搜索的字符串。 您正在向它傳遞一個指向您不允許修改的字符串的指針(您無法修改文字字符串)。 這可能是段錯誤的原因。 因此,您可以將其復制到您自己的可修改緩沖區,而不是使用指向不可修改的字符串文字的指針,如下所示:

char mybaz[] = "hello:world";

這樣做是在堆棧上放置一個12字節大小的數組,並將字符串文字的字節復制到該數組中。 它的工作原理是因為編譯器在編譯時知道字符串的長度,並且可以相應地創建空間。 這節省了使用malloc用於該特定副本。

你引用的問題是你正在將mybaz,myfoo和mybar的傳遞給你的函數。 除非您將指針傳遞給myfoo和mybar,否則無法修改調用者的變量。 由於myfoo是一個char *,指向它的指針是一個char **:

void
fn(char *baz, char **foo, char **bar) // take pointers-to-pointers

*foo = malloc(...);  // set the value pointed to by foo

fn(mybaz, &myfoo, &mybar);  // pass pointers to myfoo and mybar

修改代碼中函數的foo對myfoo完全沒有影響。 myfoo是未初始化的,所以如果前兩個都沒有引起它,那么當您使用未初始化的指針進行打印時,很可能會發生段錯誤。

一旦你基本上工作了,你可能想要添加一些錯誤處理。 如果strtok找不到它正在查找的分隔符,則它可以返回NULL,並且不能使用NULL調用strlen 如果沒有足夠的內存, malloc可以返回NULL,也不能用NULL調用strcpy

哦,是的,那里的問題很少。

通常,如果您要從函數內部操作字符串,那么這些字符串的存儲最好不在函數內部。 實現這一目標的簡單方法是在函數外部聲明數組(例如在main() )並將數組(它們自動成為指向它們開頭的指針)傳遞給函數。 只要結果字符串不會溢出數組中分配的空間,這就可以正常工作。

你已經使用了更多功能但更困難的路徑:你使用malloc()為你的結果創建空間(好到目前為止!)然后嘗試將malloc'd空間分配給你傳入的指針。那,唉, 不管用。

進來的指針是一個值; 你無法改變它。 解決方案是將指針傳遞給指針,並在函數內部使用它來更改指針所指向的內容。

如果你那樣,太好了。 如果沒有,請要求更多說明。

每個人都忽視的一件事是你在存儲在const內存中的數組上調用strtok。 strtok寫入您傳遞的數組,因此請確保在調用strtok之前將其復制到臨時數組,或者只是分配原始數組:

char mybaz[] = "hello:world";

在C中,您通常通過傳遞1)數組的第一個元素的指針,以及2)數組的長度來傳遞引用。

如果您確定緩沖區大小,則有時可以省略數組的長度,並且可以通過查找空終止字符(值為0或'\\0'字符)來知道字符串的長度。

從你的代碼示例看來,你試圖設置指針指向的值。 所以你可能想要一個char**指針。 並且您將傳入要設置的char*變量的地址。

你想傳回兩個指針。 所以你需要用一對指針指針來調用它。 像這樣的東西:

void
fn(char *baz, char **foo, char **bar) {
   ...
   *foo = malloc( ... );
   ...
   *bar = malloc( ... );
   ...
}

代碼很可能是段錯誤,因為你為字符串分配了空間,但忘記了字符串末尾有一個額外的字節,即空終止符。

此外,您只傳入指針。由於指針是32位值(在32位機器上),您只需將整數指針的值傳遞給“fn”。 以同樣的方式,你不會表達一個傳遞給函數的整數返回給調用函數(沒有顯式地返回它),你不能指望一個指針做同樣的事情。 因此新的指針值永遠不會返回到main函數。 通常你通過將指針傳遞給C中的指針來完成此操作。

另外別忘了自由動態分配內存!!

void
fn(char *baz, char **foo, char **bar)
{
     char *pch;

     /* this is the part I'm having trouble with */

     pch = strtok (baz, ":");
     *foo = malloc(strlen(pch) + 1);
     strcpy(*foo, pch);

     pch = strtok (NULL, ":");
     *bar = malloc(strlen(pch) + 1);
     strcpy(*bar, pch);

     return;
}

int
main(void)
{
     char *mybaz, *myfoo, *mybar;

     mybaz = "hello:world";

     fn(mybaz, &myfoo, &mybar);

     fprintf(stderr, "%s %s", myfoo, mybar);

     free( myFoo );
     free( myBar );
}

to do is strdup(), which allocates new memory of the appropriate size and copies the correct characters in. 其他答案描述了如何修復你的工作答案,但是一個簡單的方法來完成你的是strdup(),它分配適當大小的新內存並復制正確的字符。

但仍需要使用char * vs char **修復業務。 沒有辦法解決這個問題。

基本問題是,雖然存儲器(使用malloc() )為您嘗試返回的結果分配為myfoomybar ,但指向這些分配的指針實際上並未返回到main() 因此,稍后調用printf()很可能會轉儲核心。

解決方案是將參數聲明為指向char ponter,並將myfoomybar的地址mybarfn 這樣的事情(未經測試)應該可以解決這個問題:

void
fn(char *baz, char **foo, char **bar)
{
     char *pch;

     /* this is the part I'm having trouble with */

     pch = strtok (baz, ":");
     *foo = malloc(strlen(pch)+1);  /* include space for NUL termination */
     strcpy(*foo, pch);

     pch = strtok (NULL, ":");
     *bar = malloc(strlen(pch)+1);  /* include space for NUL termination */
     strcpy(*bar, pch);

     return;
}

int
main(void)
{
     char mybaz[] = "hello:world";
     char *myfoo, *mybar;

     fn(mybaz, &myfoo, &mybar);
     fprintf(stderr, "%s %s", myfoo, mybar);
     free(myfoo);
     free(mybar);
}

不要忘記稍后釋放每個分配的字符串,否則會產生內存泄漏。

要在一次調用中同時執行malloc()和strcpy(),最好使用strdup() ,因為它還會記住為您從代碼中寫出的終止NUL分配空間。 *foo = strdup(pch)更清晰,更容易維護替代方案。 由於strdup()是POSIX而不是ANSI C,您可能需要自己實現它,但是通過這種用法的清晰度可以很好地回報這些工作。

從C函數返回字符串的另一種傳統方式是讓調用者分配存儲並為函數提供其地址。 例如,這是sprintf()使用的技術。 它遇到的問題是,假設已經分配了比實際可用空間更多的空間,則無法使這樣的調用站點完全安全地防止由被調用函數引起的緩沖區溢出錯誤。 對此問題的傳統修復是要求還傳遞緩沖區長度參數,並在代碼審查中仔細驗證實際分配和呼叫站點聲明的長度。

編輯:

你得到的實際段錯誤很可能是在strtok()內部,而不是printf()因為你寫的樣本試圖將字符串常量傳遞給strtok() ,它必須能夠修改字符串。 這是官方未定義的行為。

解決此問題的方法是確保將bybaz聲明為初始化數組,而不是指向char的指針。 初始化的數組將位於可寫內存中,而字符串常量可能位於只讀內存中。 在許多情況下,字符串常量存儲在用於保存可執行代碼本身的內存的相同部分中,而現代系統都試圖使程序難以修改其自己的運行代碼。

在我工作的嵌入式系統中,代碼很可能存儲在某種ROM中,並且無法進行物理修改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM