【问题标题】:How do I get strcpy destination string just big enough?如何获得足够大的 strcpy 目标字符串?
【发布时间】:2015-07-17 04:36:30
【问题描述】:

在离开了几年之后,我又恢复了 C 编码的乐趣。

我给自己做了一个练习,使用fgets() 将文本从标准输入安全地复制到字符串,然后复制到一个字符串足够大,即只有足够的容量来容纳数字。我实际输入的字符数,最终是为了从头开始制作列表、堆栈等,换句话说,就是玩指针。

我在控制流的后期为strcpy() 定义目标字符串变量时,唯一的方法是管理这种kludge 的味道。有没有更优雅/更动态的方式来做到这一点?

#inlcude <stdio.h>
#include <string.h>

#define MAXLENGTH 20

int main(int argc, char *argv[]) {

    char message[MAXLENGTH];

    printf("Enter a string: \n");
    fgets(message, MAXLENGTH, stdin);

    /* various tests here, omitted for brevity */

    char destinationString[strlen(message)];

    /*
     *    Just testing to prove that
     *    the strlen() of the destination
     *    string is LESS than MAXLENGTH
     */
    printf("Here's the strlen() of destinationString: %lu\n", strlen(destinationString));
    printf("Here's the sizeof() destinationString: %lu,\n" sizeof(destinationString));
    printf("Here's the contents of the copy: %s", destinationString);

    return 0;
}

【问题讨论】:

  • 您可能在destinationString 中有缓冲区溢出(我假设您在printf 语句之前的某处有一个str[n]cpy)。您需要一个额外的字符来存储最后一个终止字符。
  • @tangrs:我同意,尽管如果代码检查换行符并消除它可能没问题。但是,如果行长于 18 个字符(不是很长),则缓冲区中不会有换行符,则可能会发生溢出。
  • 自从我用 C 编写代码以来已经有一段时间了。在早期的 C 版本中,您只能在函数的开头声明变量。您的方法对我来说看起来很实用,并且在使用变量之前声明一个变量会缩小该变量的范围,IMO 这是一件好事。
  • destinationString 是一个可变长度数组,VLA,它几乎肯定不适合您的需求。
  • 在类 UNIX 系统上,函数 strdup() 用于此类任务。

标签: c string strcpy


【解决方案1】:

您当然可以使用malloc 动态地执行此操作。

考虑这样的事情:

int main(int argc, char *argv[]) {
    char *destinationString;
    /* ... */

    /* Don't forget to allocate one extra byte for the termination character */
    destinationString = malloc(strlen(message) + 1);

    if (!destinationString)
        return -1;

    strcpy(destinationString, message);
    /* Note: Normally, you should probably use strncpy to avoid overflow
       but here, we're sure that there's enough space so strcpy is acceptable */

    /* ... */

    free(destinationString); /* When you're done using it */

    /* ... */
}

我也在 cmets 中指出了这一点,但要重新迭代,您实际上需要在目标字符串缓冲区中分配 strlen(message) + 1 字节,否则它将溢出。额外的字符是在 C 字符串的末尾存储空终止字符。

【讨论】:

    【解决方案2】:

    代码有多种选择。这里有 2 个:

    1. malloc() 和后来的free() 大小合适的内存同样由@tangrs 回答。请注意,sizeof() destinationString 将是指针的大小。

      size_t size = strlen(message) + 1;
      char *destinationString = malloc(size);
      memcpy(destinationString, message, size);
      
    2. 使用可变长度数组,VLA,在 C99 和 C11 中可用。

    带有代码清理的 VLA 方法

    #include <string.h>
    #define MAXLENGTH 20
    
    int main(int argc, char *argv[]) {
        char message[MAXLENGTH];
    
        printf("Enter a string: \n");
        if (fgets(message, sizeof message, stdin) == NULL) { 
          return -1;
        }
    
        // Use type `size_t`
        size_t size = strlen(message) + 1;
        char destinationString[size];
        memcpy(destinationString, message, size);
    
        // Notice "%zu"
        // `sizeof destinationString` is the size of an array
        printf("Here's the strlen() of destinationString: %zu\n", strlen(destinationString));
        printf("Here's the sizeof() destinationString: %zu,\n" sizeof destinationString);
        printf("Here's the contents of the copy: \"%s\"", destinationString);
        return 0;
    }
    

    输入"Hello!" 输入

    Here's the strlen() of destinationString: 8
    Here's the sizeof() destinationString: 9,
    Here's the contents of the copy: "Hello!
    "
    

    在我的系统上,输入以"\r\n" 结尾。要清除缓冲区中那些 潜在 讨厌的字符,请使用:

    fgets(message, sizeof message, stdin);
    buffer[strcspn(message, "\r\n")] = '\0';
    size_t size = strlen(message) + 1;
    ...
    

    【讨论】:

    • 非常感谢!你是如何定义 size_t 类型的?我在修改后的代码之上没有看到任何定义...
    • @gregreedee size_t&lt;stddef.h&gt;(和其他头文件)中定义。它是一些无符号整数类型,范围为 0 到 SIZE_MAXSIZE_MAX 至少 65535。
    • 再次感谢,现在我知道 size_t 了。和更合适的 printf() 格式!我运行了你的版本,它成功了!我的研究告诉我,我可以不使用 stdlib.h ,因为它引用了 stddef.h 。重新剥离“\r\n”字符中的缓冲区[] ?char 数组/字符串,它在什么上下文中使用? fgets( ) 不会直接进入这个缓冲区,而不是 message[ ]?
    • fget() 之后的典型代码想要清除缓冲区中的行尾字符。这通常是 "\n" ,但其他操作系统使用 "\r\n" or "\r". buffer[strcspn(message, "\r\n")] = '\0';` 很好地摆脱了这些 潜在 字符。这使得 s 代码更具可移植性。
    • 它有效,我是这样实现的:message[strcspn(message, "\r\n")] = '\0';你是个传奇。谢谢。
    猜你喜欢
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-06
    相关资源
    最近更新 更多