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

字符串操作&;内存分配-C,c,string,portability,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); strc

我正在学习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禁止可变长度数组“组合”的警告。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);
}
问题

  • 这是正确的实现吗
  • 若可变长度数组不是标准的一部分,为什么GCC要实现它?如果预期代码只能在GCC上编译,那么使用变量数组将比使用malloc更好
  • 我认为经验法则是,如果在编译时知道所需的内存,那么使用数组或者使用malloc来分配所需的内存。这是正确的吗
  • 我的代码预计将在GCC和MSVC上编译。我通常会在GCC上开发。那么,确保最大可移植性的编译器标志是什么?目前我正在使用
    -Wall-pedantic
    。我应该也使用
    -ansi
    吗?MSVC中可用的等效标志是什么
  • 在编写便携式C代码时,还有哪些常见的事情需要考虑? 这很有效。但当我使用cc-Wall-pedantic-gfoo.c-ofoo编译此文件时,我开始收到类似ISO C90禁止可变长度数组“组合”的警告

    尝试使用
    -std=c99
    选项(gcc)进行编译

    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);
    }
    
    若可变长度数组不是标准的一部分,为什么GCC要实现它

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

    我的代码预计将在GCC和MSVC上编译


    那么您不应该在代码IMHO中使用VLA。

    可变长度数组不是第一个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

  • 是的。那里没有违反标准的具体情况。但是
    memset
    是浪费时间,因为它无论如何都会被覆盖(将您的第一个
    strcat
    转换为
    strcpy
    )。您应该始终检查
    malloc
    是否返回NULL。不管怎样
  • C89/90不是当前的标准,C99是。而C1x也不是那么遥远。GCC正在紧跟潮流
  • 仅当不需要本地数组在函数结束后继续存在时才使用它们。否则,
    malloc
    是您的最佳选择,特别是如果您希望返回组合字符串
  • 我认为gcc有
    -std=c89
    标志或类似的东西。无论如何,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);
        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) {
        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);
    }
    
    void foo(常量字符*p1、常量字符*p2、常量字符*p3){
    char-str1k[1024];
    字符*组合;
    尺寸长度=strlen(p1)+strlen(p2)+strlen(p3)+1;
    
    如果(长度在这些问题中工作的一个非常常见的习惯用法是让调用方管理内存。因此,不要自己分配内存(无论是使用栈上的可变长度数组还是通过代码> MALOC 什么,或是什么),你都希望调用方提供内存。
    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
    作为第四个参数传递。

    请参见(1)是的,正如您在第3(2)点中提到的那样因为gcc还支持ISO C90之外的其他标准。(5)动态分配VLA总是更好的,因为您可以检查(m/c)的返回值alloc查看分配是否成功或失败not@itisravi-你应该回答,而不是在评论中回答。由于评论长度的限制,你对VLAs与动态分配的建议有点稀疏。我同意这一点,我知道你的意思,但不清楚,因为评论太短,无法回答。这就是为什么我们有答案。;)太大(依赖于平台)的VLA也可能导致堆栈溢出。对于第二个变体,这看起来是正确的,但我会使用
    calloc
    进行分配,或者通过分配
    combined[0]来避免
    memset
    ='\0'
    。这将足以使第一个strcat
    正常工作。不需要强制转换返回
    void*
    的库函数。只需使用
    char*combined=malloc(length+1);
    。此外,还需要检查
    malloc()
    的返回。顺便说一句,
    snprintf()
    在这种情况下会更好,imo。回答得很好。谢谢