亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何從參數(char*)中獲取字符串傳遞給C函數?

如何從參數(char*)中獲取字符串傳遞給C函數?

Go
鴻蒙傳說 2022-08-01 10:30:05
我很難準確地描述這個問題。讓我展示下面的代碼:我在C中有一個函數:int foo(char *deviceID){    char a[]="abc";    deviceID=a;    return 1;}顯然,我在這個函數中傳遞了要更改的char*deviceID參數,只是忽略返回整數,我想要的是獲取設備ID的值;在圍棋中:func getID() string{    var b []byte    C.foo((*C.char)(unsafe.Pointer(&b)))    return string(b)}但似乎我什么也沒得到。任何人都可以幫我找出問題嗎?
查看完整描述

2 回答

?
ibeautiful

TA貢獻1993條經驗 獲得超6個贊

您的 C 代碼有誤

正如GSerg在評論中指出的那樣,并在Thanh Do的答案中所示,您的C代碼不正確(通常會編譯和運行,但沒有有用的效果)。


編寫正確的C代碼本身就足夠困難了。將其連接到 Go 運行時可能更加困難。您現有的 C 代碼只有一個返回值,但您可能希望它返回兩個值:一個整數和一個指向 char 的指針,該指針指向以字節結尾的一系列值中的第一個,即 C 字符串。然而,C中的字符串是出了名的棘手。char'\0'


您的局部變量包含合適的字符串:a


char a[]="abc";

這里有類型或(在C中,即在Go中最接近的匹配將是),但此變量的生存期僅在函數本身結束之前。具體而言,數組具有塊范圍和自動持續時間。1 這意味著一旦函數本身返回,四個字節就會保持 evanesce。aarray 4 of charchar[4][4]bytea'a', 'b', 'c', '\0'


在C代碼中,有許多不同的方法可以解決這個問題。哪一個合適取決于真正的問題:在這個玩具示例中,您已經重現了問題,最簡單的方法是給出可 vrai 的靜態持續時間。結果將如下所示:a


int foo(char **deviceID) {

    static char a[] = "abc";

    *deviceID = a;

    return 1;

}

請注意,此函數現在采用指向指針的指針。從更多的C代碼中,它可以這樣調用:


    char *name;

    ret ok;


    ret = foo(&name);

調用方的指針 , 類型 為 ,已被填充 (覆蓋) 的適當類型值,即指向 char 的指針,指向有效字符串,其生存期是整個程序的生存期(“靜態持續時間”)。namechar *char *


另一種方法(Thanh Do的答案中所示的方法)是使用 。這樣做很危險,因為現在由調用方來分配足夠的存儲空間來保存整個字符串?,F在有必要選擇一些最大長度來填充(或添加一個參數來減輕危險,就像我們稍后會做的那樣)。我們的調用 C 代碼片段現在可能顯示為:strcpyfoo


    char buf[4];

    ret ok;


    ret = foo(buf);

但你應該問:我們如何知道讓buf有四個字節的空間?答案要么是“我們沒有”——我們只是幸運地把它做得足夠大——要么是“因為我們可以看到這個函數總是只寫四個字節”。但是如果我們使用第二個答案,那么,我們可以看到該函數總是寫入四個字節(然后總是返回1)。那么,我們為什么要費心調用函數呢?我們可以這樣寫:foofoo'a', 'b', 'c', '\0'foo


    char buf[4] = "foo";

    int ret = 1;

并完全省略函數調用。因此,一個真實世界的例子也許需要兩個參數:指向要填充的緩沖區的指針和該緩沖區的大?。ㄒ宰止潪閱挝唬H绻覀冃枰目臻g超過可用空間,在我們的函數中,我們將返回失?。ㄕ{用方現在必須注意)或截斷名稱,或者兩者兼而有之:foo


int foo(char *buffer, size_t len) {

    char a[] = "abc";


    if (strlen(a) + 1 > len) {

        return 0; /* failure */

    }

    strcpy(buffer, a);

    return 1; /* success */

}

函數現在依賴于其調用方來正確發送這兩個值,但至少它可以正確且安全地使用,這與早期版本的 不同。foofoo


1 個這些術語特定于 C 語言。雖然 Go 聲明也有范圍,但這個術語的使用方式略有不同,因為 Go 中的基礎存儲是垃圾回收的。C 中某些數據的持續時間可以綁定到變量的作用域,而這在 Go 中根本不會發生。


您的 Go 代碼有誤

無論你用C代碼做什么,你現有的Go代碼也存在問題:


func getID() string {

    var b []byte

    C.foo((*C.char)(unsafe.Pointer(&b)))

    return string(b)

}

這里的類型是或“字節片”。它的初始值為零(轉換為字節切片類型)。b[]byte


Go 中的切片值實際上是一個三元素組,如果你愿意的話,它是一種類型。請參閱反射。SliceHeader 以獲取更多詳細信息。該構造生成指向此切片標頭的指針。struct&b


根據你如何修復你的函數——它是使用 ,還是通過取一個 type 的值來設置一個 type 值,甚至像 Allen ZHU 的答案那樣使用——你絕對不想傳遞給 C 代碼。僅僅用掩飾這個錯誤來包裝。foostrcpychar *char **malloc&b&bunsafe.Pointer


相反,假設您選擇有 take 和 .然后我們要做的是傳遞給C函數:C.foochar *size_t


類型的值,指向 n 個>= 4 s 中的第一個;*C.charC.char

可以覆蓋的數目,包括終止的“\0”。C.char

我們應該保存返回值,并確保它是魔術常數1(最好也去掉魔術常量,但你可以稍后再這樣做)。


以下是我們修改后的函數:getID


func getID() string {

        b := make([]C.char, 4)

        ret := C.foo(&b[0], C.size_t(len(b)))

        if ret != 1 {

                panic(fmt.Sprintf("C.foo failed (ret=%d)", int(ret)))

        }

        return C.GoString(&b[0])

}

我把一個完整的示例程序放到Go Playground中,但不允許在那里構建C部分。(我在另一個系統上構建并運行了它。


查看完整回答
反對 回復 2022-08-01
?
森林海

TA貢獻2011條經驗 獲得超2個贊

char *deviceID => 這是指向調用數組(客戶端代碼)的第一個字符的指針。我們可以說設備ID存儲調用數組的起始地址的地址(客戶端代碼)。假設數組為 B。所以它存儲了B的地址[0]


deviceID=a => deviceID 存儲 [a] 數組的起始地址。


當程序返回時,deviceID 指向第一個字符。我們可以說 deviceID 再次存儲了 B[0] 的起始地址的地址。


沒有變化。


在C中,我們可以使用strcpy將內容復制到地址。


int foo(char *deviceID){

    char a[]="abc";

    strcpy(deviceID, a);

    return 1;

}


int foo(char *deviceID){

    char a[]="abc";

    

    for (int i = 0; i < 4; i++){

        a[i] = a[i];  //note: copy the last character such as a[3] = '\0';

    }

    return 1;

}


查看完整回答
反對 回復 2022-08-01
  • 2 回答
  • 0 關注
  • 141 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號