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

发布于 2024-09-08 03:54:57 字数 1343 浏览 1 评论 0原文

我正在学习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);
}

问题

  1. 这是正确的实现吗?
  2. 如果可变长度数组不是标准的一部分,为什么 GCC 实现它?如果代码预计只能在 GCC 上编译,那么使用变量数组会比使用 malloc 更好吗?
  3. 我认为经验法则是,如果在编译时就知道所需的内存,则使用数组,否则使用 malloc 来分配所需的内存。这是正确的吗?
  4. 我的代码预计可以在 GCC 和 MSVC 上编译。我通常会在 GCC 上进行开发。那么确保最大可移植性的编译器标志是什么?目前我正在使用-Wall -pedantic。我也应该使用 -ansi 吗? MSVC 中可用的等效标志是什么?
  5. 编写可移植 C 代码时需要考虑的其他常见事项是什么?

I am in the process of learning C. I have a method that takes 3 strings and combines them to do some operation. Following was my first implementation using a GCC compiler.

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

This works well. But when I compiled this using, cc -Wall -pedantic -g foo.c -o foo, I started getting warnings like ISO C90 forbids variable length array ‘combined’. MSVC was not compiling this code. Changed the code like

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

Questions

  1. Is this the correct implementation?
  2. If variable length arrays are not part of standard, why GCC implemented it? If the code is expected to compile only on GCC, using variable arrays will be a better alternative than using malloc?
  3. I think the thumb rule is, if memory required is knows at compile time, use arrays else use malloc to allocate required memory. Is this correct?
  4. My code is expected to compile on GCC and MSVC. I will be developing on GCC usually. So what are the compiler flags that ensures maximum portability? Currently I am using -Wall -pedantic. Should I use -ansi too? What would be the equivalent flags available in MSVC?
  5. What are the other common things to consider when writing portable C code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

混浊又暗下来 2024-09-15 03:54:57

这个效果很好。但是当我使用 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(恕我直言)。

This works well. But when I compiled this using, cc -Wall -pedantic -g foo.c -o foo, I started getting warnings like ISO C90 forbids variable length array ‘combined’.

Try compiling with -std=c99 option (gcc).

MSVC was not compiling this code. Changed the code like

If variable length arrays are not part of standard, why GCC implemented it?

VLAs are part of ISO C99(gcc and g++(as an extension) support VLAs). MSVC still only supports C89.

My code is expected to compile on GCC and MSVC.

Then you should not use VLAs in your code IMHO.

撩动你心 2024-09-15 03:54:57
  1. 是的。那里没有具体违反标准的情况。然而,memset 是浪费时间,因为它无论如何都会被覆盖(将您的第一个 strcat 变成 strcpy)。并且您应该始终检查malloc是否返回NULL。 无论如何!
  2. C89/90 不是当前标准,C99 才是。 C1x 距离我们并不遥远。 GCC 紧跟最前沿。
  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);
}

如果组合,则使用堆栈存储string 将适合,并且仅在不适合的情况下分配内存。如果大量字符串组合起来小于限制,这通常会导致速度的显着提高。

  1. Yes, it is. There's no specific violations of the standard there. The memset is a waste of time however since it's al going to be overwritten anyway (make your first strcat into a strcpy). And you should always check for malloc returning NULL. No matter what!
  2. C89/90 is not the current standard, C99 is. And C1x is not that far away. GCC is keeping up with the bleeding edge.
  3. Only use local arrays if you don't need them to survive beyond the end of the function. Otherwise malloc is your best bet, particularly if you want to return the combined string.
  4. I think gcc has the -std=c89 flag or something similar. In any case, MSVC does not always follow the standard :-)
  5. Compile and test it on both platforms frequently. That's the only way to be sure.

I would opt for:

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

or, since you're not actually doing anything with the string except printing it:

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

:-)

Another strategy I've seen is the "only allocate if you have to" strategy:

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

which uses stack storage if the combined string will fit and only allocates memory if it won't. This can often lead to a substantial speed improvement if the bulk of strings combine into less than the limit.

删除会话 2024-09-15 03:54:57

可变长度数组不是第一个 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< /code> 是编译 C 代码时 -std=c90 的同义词)。请注意,MSVC 不支持 C99。

Variable-length arrays were not part of the first ISO C standard (variously referred to as "C89", "C90" or "ANSI C"). However, they are a part of the latest ISO C standard (known as "C99").

GCC can compile your code in several modes, including "strict C90", "C90-with-GNU-C-extensions", and "C99" (although it does not fully implement C99, it is close enough for most practical purposes).

By default, GCC uses "C90-with-GNU-C-extensions", which is why your code compiles without complaint. Using -pedantic tells it to emit all required warnings by the relevant standard (in this case, C90), and such a warning is required by your code. If you give GCC the -std=c99 -pedantic flags, to tell it to compile against the C99 base standard and emit all required warnings, your code compiles fine.

If you want to ensure your code is compatible with the base C90 standard, then use -std=c90 -pedantic (or -ansi -pedantic: -ansi is a synonym for -std=c90 when compiling C code). Note that MSVC does not support C99.

山有枢 2024-09-15 03:54:57

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

A very common idiom to work around these issues is to let the caller manage the memory. So instead of allocating memory yourself (either using a variable-length array on the stack or by malloc'ing something, or whatever) you expect the caller to provide memory. Consider this:

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

The advantage of this approach is that foo will never leak. In addition to that, the caller can use a simple stack-based array in case it works for him. If he wants to know the exact size he can call foo and pass NULL as the fourth argument.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文