字符串操作&内存分配 - C.

时间:2010-07-07 08:19:29

标签: c string portability

我正在学习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’这样的警告。 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代码时需要考虑的其他常见事项是什么?

4 个答案:

答案 0 :(得分:8)

  

这很有效。但是当我使用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 :(得分:7)

  1. 是的,确实如此。那里没有具体违反标准的行为。 memset是浪费时间,但是因为它会被覆盖(将你的第一个strcat变成strcpy)。你应该总是检查malloc返回NULL。 无论如何!
  2. C89 / 90不是现行标准,C99是。 C1x并不是那么遥远。 GCC正在紧跟前沿。
  3. 如果您不需要它们在函数结束之后存活,则仅使用本地数组。否则malloc是您最好的选择,特别是如果您想返回组合字符串。
  4. 我认为gcc有-std=c89标志或类似的东西。无论如何,MSVC并不总是遵循标准: - )
  5. 经常在两个平台上编译和测试它。这是唯一确定的方法。
  6. 我会选择:

    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);
    }
    

    如果组合的字符串适合,则使用堆栈存储,如果不适合则仅分配内存。如果大量字符串合并到小于限制范围内,这通常可以显着提高速度。

答案 2 :(得分:3)

可变长度数组不是第一个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标准兼容,那么在编译C时使用-std=c90 -pedantic(或-ansi -pedantic-ansi-std=c90的同义词码)。请注意,MSVC不支持C99。

答案 3 :(得分:0)

解决这些问题的一个非常常见的习惯是让调用者管理内存。因此,不是自己分配内存(使用堆栈上的可变长度数组或通过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作为第四个参数。