簡體   English   中英

如何避免與char *一起提供長度?

[英]How to avoid providing length along with char*?

有一種功能 ,其將數據發送到服務器:

int send(
  _In_  SOCKET s,
  _In_  const char *buf,
  _In_  int len,
  _In_  int flags
);

在我看來,提供長度有點奇怪。 我需要編寫一個函數,向服務器發送一條線並將其包裝起來,這樣我們就不必顯式提供長度。 我是Java開發人員,在Java中,我們只能調用String::length()方法,但現在我們不在Java中。 除非提供length作為模板參數,否則該怎么辦? 例如:

void sendLine(SOCKET s, const char *buf)
{
}

可以實現這樣的功能嗎?

使用標准字符串:

void sendLine(SOCKET s, const std::string& buf) {
    send (s, buf.c_str(), buf.size()+1, 0); //+1 will also transmit terminating \0.
 }

附帶說明:您的包裝函數將忽略返回值,並且不帶任何標志。

您可以使用strlen(const char *)函數檢索C字符串的長度。 確保所有字符串均以null結尾,並記住以null結尾(長度增加1)

編輯: 我的答案最初只提到std::string 現在,我還添加了std::vector<char>來解決send並不嚴格用於文本數據的情況。


首先,您絕對需要一本C ++書籍。 您正在尋找std::string類或std::vector<char> ,這兩者都是語言的基本元素。

您的問題有點像在Java中,因為從未聽說過java.lang.String ,所以如何避免使用char[] ;或者因為從未聽說過java.util.ArrayList ,因此如何避免一般地使用數組。


對於此答案的第一部分,假設您在此處處理文本輸出,即,在處理中char實際上是文本字符。 那就是std::string用例。

在我看來,提供長度似乎有些奇怪。

這就是字符串在C中的工作方式。AC字符串實際上是指向存儲字符的存儲位置的指針。 通常,C字符串以null終止。 這意味着為該字符串存儲的最后一個字符是'\\0' 它的意思是“字符串在此處停止,如果進一步移動,則輸入非法領土”。

這是一個C風格的示例:

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

void f(char const* s)
{
    int l = strlen(s); // l = 3
    printf(s); // prints "foo"
}

int main()
{
    char* test = new char[4]; // avoid new[] in real programs
    test[0] = 'f';
    test[1] = 'o';
    test[2] = 'o';
    test[3] = '\0';
    f(test);
    delete[] test;
}

strlen只計算內存中指定位置的所有字符,直到找到'\\0'為止。 printf只是將所有字符寫入內存中的指定位置,直到找到'\\0'為止。

到現在為止還挺好。 現在,如果有人忘記了空終止符怎么辦?

char* test = new char[3]; // don't do this at home, please
test[0] = 'f';
test[1] = 'o';
test[2] = 'o';
f(test); // uh-oh, there is no null terminator...

結果將是未定義的行為 strlen將繼續尋找'\\0' 所以printf 這些功能將嘗試讀取它們不應該讀取的內存。 該程序可以執行任何操作,包括崩潰。 邪惡的是,很可能一會兒什么都不會發生,因為一個'\\0' 恰好存儲在內存中,直到有一天你不再那么幸運了。

因此,有時需要明確指定字符數,才能使C函數更安全。 您的send就是這樣的功能。 即使沒有以null終止的字符串,它也可以正常工作。

C字符串就這么多。 現在,請不要在您的C ++代碼中使用它們。 使用std::string 通過提供c_str()成員函數來設計它與C函數的兼容性,該成員函數返回一個以空值結尾的char const *指向字符串的內容,並且它當然具有一個size()成員函數來告訴您沒有以空值結尾的字符的字符數(例如,代表單詞“ foo”的std::stringsize()將為3,而不是4,並且3也是您期望的C函數所期望的,但是您必須查看該函數的文檔以了解它是否需要可見字符數或內存中的元素數)。

實際上,使用std::string可以忘掉整個null終止業務。 一切都很好地實現了自動化。 std::stringjava.lang.String一樣簡單安全。

因此,您的sendLine應該變成:

void sendLine(SOCKET s, std::string const& line)
{
    send(s, line.c_str(), line.size());
}

(通過const&傳遞std::string是在C ++中傳遞大對象的常規方法。這只是為了提高性能,但是這種廣泛使用的約定是,如果您只是傳遞std::string ,您的代碼就會看起來很奇怪。)

除非提供長度作為模板參數,否則該怎么辦?

這是模板工作方式的誤解。 使用模板時,必須在編譯時知道其長度。 那當然不是你想要的。


現在,對於答案的第二部分,也許您並不是在這里真正處理文本。 這不太可能,因為示例中的名稱“ sendLine”聽起來很像文本,但是也許您正在處理原始數據,並且輸出中的char並不表示文本字符,而只是一個值,可以解釋為完全不同的值,例如圖像文件的內容。

在這種情況下, std::string是一個糟糕的選擇。 您的輸出可能包含'\\0'字符,這些字符具有“此處的數據結尾”的含義,但屬於正常內容。 換句話說,您實際上不再具有字符串,而是擁有一系列char元素,其中的'\\0'沒有特殊含義

對於這種情況,C ++提供了std::vector模板,您可以將其用作std::vector<char> 通過提供返回char指針的成員函數,它還可以與C函數一起使用。 這是一個例子:

void sendLine(SOCKET s, std::vector<char> const& data)
{
    send(s, &data[0], data.size());
}

(不尋常的&data[0]語法的意思是“指向封裝數據的第一個元素的指針。C++ 11具有更好的讀取方式,但是&data[0]也可用於較舊的C ++版本。)


注意事項:

  • std::string類似於Java中的String
  • std::vector類似於Java中的ArrayList
  • std::string用於具有文本含義的char范圍, std::vector<char>用於具有原始數據含義的char范圍。
  • std::stringstd::vector旨在與C API一起使用。
  • 不要在C ++中使用new[]
  • 了解C字符串的空終止。

暫無
暫無

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

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