簡體   English   中英

字符串操作和內存分配 - C.

[英]String manipulation & memory allocation - C

我正在學習C.我有一個方法,需要3個字符串並將它們組合起來做一些操作。 以下是我使用GCC編譯器的第一個實現。

void foo(const char *p1, const char *p2, const char *p3)
{
    size_t length = strlen(p1) + strlen(p2) + strlen(p3);
    char combined[length + 1];
    memset(combined, 0, length + 1);
    strcat(combined, p1);
    strcat(combined, p2);
    strcat(combined, p3);
    printf("Result : %s", combined);
}

int main()
{
    foo("hello ", "world ", "how");
    return 0;
}

這很好用。 但是當我使用cc -Wall -pedantic -g foo.c -o foo編譯它時,我開始收到像ISO C90 forbids variable length array 'combined'這樣的警告, ISO C90 forbids variable length array 'combined' MSVC沒有編譯此代碼。 更改了代碼

void foo(const char *p1, const char *p2, const char *p3)
{
    size_t length = strlen(p1) + strlen(p2) + strlen(p3);
    char *combined = (char *) malloc(length + 1);
    memset(combined, 0, length + 1);
    strcat(combined, p1);
    strcat(combined, p2);
    strcat(combined, p3);
    printf("Result : %s", combined);
    free(combined);
}

問題

  1. 這是正確的實施嗎?
  2. 如果可變長度數組不是標准的一部分,為什么GCC實現它? 如果代碼只能在GCC上編譯,那么使用變量數組比使用malloc更好嗎?
  3. 我認為拇指規則是,如果在編譯時需要內存,則使用數組,否則使用malloc來分配所需的內存。 它是否正確?
  4. 我的代碼預計將在GCC和MSVC上編譯。 我通常會在GCC上發展。 那么什么是確保最大可移植性的編譯器標志? 目前我正在使用-Wall -pedantic 我也應該使用-ansi嗎? MSVC中可用的等效標志是什么?
  5. 編寫可移植C代碼時需要考慮的其他常見問題是什么?

這很好用。 但是當我使用cc -Wall -pedantic -g foo.c -o foo編譯它時,我開始收到像ISO C90這樣的警告,禁止變量長度數組“組合”。

嘗試使用-std=c99選項(gcc)進行編譯。

MSVC沒有編譯此代碼。 更改了代碼

如果可變長度數組不是標准的一部分,為什么GCC實現它?

VLA是ISO C99(gcc和g ++(作為擴展)支持VLA)的一部分。 MSVC仍然只支持C89。

我的代碼預計將在GCC和MSVC上編譯。

那么你不應該在代碼恕我直言中使用VLA。

  1. 是的。 那里沒有具體違反標准的行為。 memset是浪費時間,但是因為它無論如何都會被覆蓋(讓你的第一個strcat變成strcpy )。 並且您應該始終檢查malloc返回NULL。 無論!
  2. C89 / 90不是現行標准,C99是。 C1x並不是那么遙遠。 海灣合作委員會正在緊跟前沿。
  3. 如果您不需要它們在函數結束之后存活,則僅使用本地數組。 否則malloc是你最好的選擇,特別是如果你想返回組合字符串。
  4. 我認為gcc有-std=c89標志或類似的東西。 無論如何,MSVC並不總是遵循標准:-)
  5. 經常在兩個平台上編譯和測試它。 這是唯一確定的方法。

我會選擇:

void foo (const char *p1, const char *p2, const char *p3) {
    size_t length = strlen(p1) + strlen(p2) + strlen(p3);
    char *combined = (char *) malloc(length + 1);
    if (combined == NULL) {
        printf("Result : <unknown since I could't get any memory>\n");
    } else {
        strcpy(combined, p1);
        strcat(combined, p2);
        strcat(combined, p3);
        printf("Result : %s", combined);
        free(combined);
    }
}

或者,因為除了打印它之外你實際上沒有對字符串做任何事情:

void foo (const char *p1, const char *p2, const char *p3) {
    printf("Result : %s%s%s", p1, p2, p3);
}

:-)

我見過的另一個策略是“只有你必須分配”策略:

void foo (const char *p1, const char *p2, const char *p3) {
    char str1k[1024];
    char *combined;
    size_t length = strlen (p1) + strlen (p2) + strlen (p3) + 1;
    if (length <= sizeof(str1k))
        combined = str1k;
    else
        combined = malloc (length);
    if (combined == NULL) {
        printf ("Result : <unknown since I couldn't get any memory>\n");
    } else {
        strcpy (combined, p1);
        strcat (combined, p2);
        strcat (combined, p3);
        printf ("Result : %s", combined);
    }
    if (combined != str1k)
        free (combined);
}

如果組合的字符串適合,它使用堆棧存儲,如果不適合,則僅分配內存。 如果大量字符串組合到低於限制,這通常可以顯着提高速度。

可變長度陣列不是第一個ISO C標准(不同地稱為“C89”,“C90”或“ANSI C”)的一部分。 但是,它們最新ISO C標准(稱為“C99”)的一部分。

GCC可以用幾種模式編譯你的代碼,包括“嚴格C90”,“C90-with-GNU-C-extensions”和“C99”(盡管它沒有完全實現C99,但它足夠接近大多數實際用途)。

默認情況下,GCC使用“C90-with-GNU-C-extensions”,這就是為什么您的代碼無需投訴即可編譯的原因。 使用-pedantic告訴它通過相關標准(在本例中為C90)發出所有必需的警告,並且代碼需要這樣的警告。 如果你給GCC -std=c99 -pedantic標志,告訴它根據C99基本標准編譯並發出所有必需的警告,你的代碼編譯得很好。

如果要確保代碼與基本C90標准兼容,則使用-std=c90 -pedantic (或-ansi -pedantic-ansi是編譯C代碼時-std=c90的同義詞)。 請注意,MSVC不支持C99。

解決這些問題的一個非常常見的習慣是讓調用者管理內存。 因此,不是自己分配內存(使用堆棧上的可變長度數組或通過malloc的東西,或其他任何東西),而是希望調用者提供內存。 考慮一下:

int foo(const char *p1, const char *p2, const char *p3, char *buf, size_t bufsize)
{
    size_t requiredSize = strlen(p1) + strlen(p2) + strlen(p3) + 1;
    if (!buf)
        return requiredSize;
    if (requiredSize > bufsize)
        return -1;
    buf[0] = '\0';
    strcat(buf, p1);
    strcat(buf, p2);
    strcat(buf, p3);
    return requiredSize;
}

int main()
{
  /* simple case: caller knows that the buffer is large enough. */
  char buf[ 1024 ];
  foo( "Hello", "World", "Bar", buf, sizeof(buf) );
  printf("Result : %s\n", buf);

  /* complicated case: caller wants to allocate buffer of just the right size */
  size_t bufsize = foo( "Hello", "World", "Bar", NULL, 0 );
  char *buf2 = (char *)malloc(bufsize);
  foo( "Hello", "World", "Bar", buf2, bufsize );
  free( buf2 );
}

這種方法的優點是foo永遠不會泄漏。 除此之外,調用者可以使用簡單的基於堆棧的數組,以防它適用於他。 如果他想知道確切的大小,他可以調用foo並傳遞NULL作為第四個參數。

暫無
暫無

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

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