簡體   English   中英

C函數返回sprintf字符串

[英]c function return sprintf string

只是嘗試制作一個簡單的toString函數,但是我很難弄清楚如何創建sprintf樣式字符串而不必創建臨時變量。

即如果以下工作有效

#myfile.c
bool status;
int state;
...
const char* myfileToString(void) {
    return sprintf("myfile::status=%s;state=%d;", \
                   status ? "true" : "false", \
                   state);
}

這將不起作用,因為sprintf需要您首先創建字符串,然后將其傳遞給函數。

即我相信以下作品

#myfile.c
bool status;
int state;
...
const char* myfileToString(void) {
    char ret[40];
    sprintf(ret, \
            "myfile::status=%s;state=%d;", \
            status ? "true" : "false", \
            state);
    return ret;
}

是否有另一個函數可以代替sprintf使用,或者我可以對sprintf做一些奇怪的事情,例如傳遞一些指針直接返回sprintf(*return, "...", ...);

好的...

第1部分:

  • 除非編寫宏,否則在未完成的行的末尾不需要反斜杠。
  • sprintf()需要一個可變緩沖區作為第一個參數。 您沒有通過,所以這里會出現各種不良行為。

第2部分:

  • 您正在返回一個指向堆棧分配對象的指針。 這將導致未定義的行為。

您應該做什么:

在此功能之外分配內存。 所以:

void myfileToString(const char *str, size_t len) {
    snprintf(str, len, "myfile::status=%s;state=%d;",
        status ? "true" : "false",
        state);
    str[len - 1] = '\0';
}

您可以這樣做:

char box[40];
myfileToString(box, sizeof(box) /* 40 */);

使它更可靠:

sprintf()函數系列返回寫入緩沖區的字符數。 如果寫入的字符數大於緩沖區的大小,則可以使用它來發回錯誤(例如返回真值或假值)。

最后,您可以使用堆:

這不是最佳實踐,因為它可能會與人們的期望混淆。 但是您可以在堆上為此數據分配內存。 您將需要記住,將來會對其調用free()

const char *myFileToString() {
    char *str = malloc(40); 
    snprintf(str, 40, "myfile::status=%s;state=%d;",
        status ? "true" : "false",
        state);
    str[40 - 1] = '\0';
}

在第一個版本中,您將返回sprintf返回的內容,因為它實際上未返回字符串,所以將不起作用(請閱讀鏈接的參考資料)。 在該函數的第二個版本中,您返回一個指向局部變量的指針,這會導致未定義的行為,因為當函數返回並且指針不再指向有效內存時,局部變量將超出范圍。

有兩種主要解決方案:

  1. 動態分配字符串,例如使用malloc ,並返回該指針。 主要缺點是您必須free返回的指針。

  2. 將字符數組作為參數傳遞給函數,並將其用作字符串的目標。

而且,您應該寧願使用snprintf而不是plain sprintf ,因為這樣您就可以控制向目標字符串寫入的數量,因此不會導致緩沖區溢出。 在使用Visual C ++編譯器的Windows上,它稱為_snprintf

您不想使用局部變量來構建您的字符串。返回后,該變量將丟失並且返回值將是不確定的。 相反,您可以使用malloc()在例程中分配內存,也可以將指向預分配內存的指針傳遞到例程中。

無論哪種情況,都應使用snprintf()而不是sprintf()來確保不會溢出緩沖區。

您不能在函數中返回char [],因為它是靜態分配的(在您的堆棧上),並在函數末尾銷毀。 對於toString函數,必須使用malloc才能返回動態分配的char *(在堆中)

您的第一種方法是完全錯誤的! 您不應該那樣使用! 可以使用snprintf代替sprintf

snprintf(ret,sizeof(ret),"myfile::status=%s;state=%d;",status ? "true" : "false",state);

我使用這個(非常討厭)技巧:

  struct strang
  {
    char  s[100] ;     /* this is arbitrary, but has to be enough ! */
  } ;

並通過示例將struct sockaddr轉換為合適的字符串:

  struct strang
  sockaddr2strang(struct sockaddr* sa)
  {
    struct strang str ;
    struct sockaddr_in* sin ;
    ....

    switch (sa->sa_family)
    {
       AF_INET:
         sin = (struct sock_addr_in*)sa ;
         inet_ntop(AF_INET, &sin->sin_addr, str.s, sizeof(str)) ;
         break ;
       ....
     } ;

    return str ;
  } ;

狡猾的部分是您可以在表達式中使用結果或將其用作另一個函數的參數,例如:

  printf("....%s..%s..", ...., sockaddr2strang(&sa_local).s, 
                                       ...., sockaddr2strang(&sa_peer).s) ;

請注意,這完全是pthread安全的,不需要調用者設置和傳遞任何輔助緩沖區-特別是(如此處所示),如果同時需要多個字符串,則調用者不必管理兩個(或更多)緩沖區等。

顯然,這不適用於任意大小的返回字符串。

暫無
暫無

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

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