【问题标题】:Allocating memory for a char array to concatenate a known piece of text and an integer为 char 数组分配内存以连接已知文本和整数
【发布时间】:2010-02-25 13:06:54
【问题描述】:

我想连接一段文本,例如“答案是”与一个有符号整数,输出“数字是 42”。

我知道一段文本有多长(14 个字符),但我不知道数字的字符串表示形式会有多少个字符。

我假设最坏的情况,最大的有符号 16 位整数有 5 位,如果它是负数,再加一位,那么下面的代码是正确的方法吗?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *message;

    message = malloc(14*sizeof(char)+(sizeof(int)*5)+1);

    sprintf(message, "The answer is %d", 42);

    puts(message);

    free(message);
}

【问题讨论】:

    标签: c memory-management malloc char concatenation


    【解决方案1】:

    用途:

    malloc(14*sizeof(char) /*for the 14 char text*/
           +(sizeof(char)*5) /*for the magnitude of the max number*/
           +1 /* for the sign of the number*/
           +1 /* for NULL char*/
          );
    

    由于数字将被表示 作为 char 你必须使用 sizeof(char) 而不是 sizeof(int)。

    【讨论】:

    • 我要补充一点,因为 sizeof(char) 在 C 标准中是 1,所以只需 14+5+1+1 就可以了
    • @qrdl:是的,这可行,但使用 sizeof(char) 可以清楚地知道分配给它的是什么。
    • 您必须将 sizeof(char) 应用于您需要的 char 数的总和。在 2 字节/字符系统上,您的解决方案将不起作用。
    • @Niklaos,一个字节不是一个八位字节。 ISO 标准将字节定义为与 char 相同的大小,无论是多少位。对于 malloc,您从不需要 sizeof(char)。
    • @Niklaos,你能举一个2字节/字符系统的例子吗?据我所知,C 标准将 char 定义为一个字节。
    【解决方案2】:

    不完全是,您只需要一些字符,因此不需要sizeof(int)。

    但是,对于易于维护和可移植的代码,您应该具有以下内容:

    #define TEXT "The answer is "
    #undef CHARS_PER_INT
    #if INT_MAX == 32767
        #define CHARS_PER_INT 6
    #endif
    #if INT_MAX == 2147483647
        #define CHARS_PER_INT 11
    #endif
    #ifndef CHARS_PER_INT
        #error Suspect system, I have no idea how many chars to allocate for an int.
    #endif
    
    int main (void) {
        char *message;
    
        message = malloc(sizeof(TEXT)+CHARS_PER_INT+1);
        sprintf(message, TEXT "%d", 42);
        puts(message);
        free(message);
        return 0;
    }
    

    这有很多优点:

    • 如果更改字符串,则只更改一件事且仅更改一件事。 malloc 的参数会自动调整。
    • 表达式sizeof(TEXT)+CHARS_PER_INT+1 在编译时计算。涉及strlen 的解决方案会产生运行时成本。
    • 如果您尝试在整数可能导致溢出的系统上编译代码,您会被告知(修复代码)。
    • 您实际上应该为该数字分配一个额外的字符,因为最大的 16 位数字(就字符数而言)是-32768(六个字符长)。您会注意到我仍然在末尾有一个+1 - 那是因为您需要为字符串空终止符留出空间。

    【讨论】:

    • 感谢您的回答-您说表达式是在编译时计算的,但我看不出这与上面的答案有任何区别-它们肯定是在同一点计算的,即在malloc() 表达式。我错过了什么?
    • @Slappy,它们都是编译时表达式。我将它与具有运行时成本的“strlen()”解决方案进行比较,更新以明确这一点。
    【解决方案3】:

    一种方法(不一定推荐)使用stdio 函数本身为您提供字符数字的确切大小。

    例如,如果您在分配内存之前打印数字(在某处,无论出于何种原因),您可以将%n 格式标识符与printf 一起使用。 %n 不打印任何东西;相反,您向它提供一个指向int 的指针,printf 用到目前为止已写入的字符数填充它。

    另一个例子是snprintf,如果你有的话。您将希望它写入字符串的最大字符数传递给它,它会返回它应该写入的字符数,不包括最终的 nul。 (或 -1 错误。)因此,使用 1 字节的虚拟字符串,snprintf 可以准确地告诉您您的号码有多少个字符。

    使用这些函数的一大优势是,如果您决定更改数字的格式(前导 0、填充空格、八进制输出、长整数等),您的内存不会溢出。

    如果您有stdio 的GNU 扩展,您可能需要考虑使用asprintf。这和sprintf 完全一样,只是它会为您分配内存!无需组装。 (尽管您确实需要自己释放它。)但是您不应该依赖它来便携。

    【讨论】:

      【解决方案4】:

      malloc((14 + 6 + 1) * sizeof(char));

      • 字符串为 14 个字符
      • 6 代表 de 数字 + 符号
      • 1 代表“\0”

      注意:Sizeof(int) 以字节为单位为您提供类型的大小。 Sizeof(int) == 4 如果 int 是 32 位,8 如果是 64 位。

      【讨论】:

      • 有一个错字:4个字符的字符串,应该是14。
      • 请停止延续一个字节是 8 位的神话。 ISO C/C++ 标准中的一个字节和一个字符由 limits.h 中的 CHAR_BITS 定义 - 如果 CHAR_BITS 为 32(即 32 位字节/字符),则 sizeof(int) 对于 32 位 int 将为 1。
      • @paxdiablo 太棒了:p 我刚刚阅读了维基百科字节页面。所以是的,这看起来像是一个神话。
      • 对于像我这样不是“语言律师”的人来说,这是一个鲜为人知的事实。由于这种误解,我实际上以为我曾经在标准中发现了一个错误。但可惜我和我之前(和之后)的许多其他人一样,错了:-)
      • Ultra-language-lawyer: sizeof(int)*CHAR_BITS 只为您提供存储要求。可能值的范围当然不大于1&lt;&lt;bits,但可能更小。例如。在将整数存储为指数设置为 0 (Crays) 的浮点数的系统中,表示 INT_MIN 所需的位数非常重要。
      【解决方案5】:

      我认为获得整数十进制表示的最大长度的正确公式是 (floor(log10(INT_MAX))+1);你也可以通过这种方式滥用预处理器:

      #include <limits.h>
      #define TOSTRING_(x) #x
      #define TOSTRING(x) TOSTRING_(x)
      /* ... */
      #define YOUR_MESSAGE "The answer is "
      char message[]=YOUR_MESSAGE "+" TOSTRING(INT_MAX);
      sprintf(message+sizeof(YOUR_MESSAGE),"%d", 42);
      

      ,这也避免了堆分配。您可能希望使用 snprintf 以获得更好的安全性,尽管使用这种方法应该没有必要。

      另一个类似的技巧是创建一个这样的函数:

      size_t GetIntMaxLenght()
      {
          const char dummy[]=TOSTRING(INT_MAX);
          return sizeof(dummy)+1;
      }
      

      如果编译器足够聪明,它可以完全清除编译代码中的 dummy var,否则最好将该 var 声明为 static 以避免每次调用函数时重新初始化它.

      【讨论】:

      • 如果它是负数,不要忘记可选符号的字节。
      • 事实上它是 INT_MAX 而不是 MAX_INT :-)
      • 已更正,谢谢。我还在第二个函数中添加了一个 const ,它可以帮助编译器优化 var (IIRC,如果不使用 const 对象,编译器不需要为它们分配内存)。
      • 这不起作用有两个原因。首先,除非您添加另一个级别的宏,否则TOSTRING(INT_MAX) 只会给您字符串"INT_MAX"。其次,INT_MAX 不一定被定义为一个数字——它只需要是一个编译时常量。一个实现完全可以拥有#define INT_MAX __imax 之类的东西。
      • 嗯,你说得对,我总是忘记双重宏求值技巧(实际上我通常使用 C++,所以我很少需要宏 hack)。对于您的第二点,实际上我从未见过任何编译器这样做;不过,理论上是可以的。
      【解决方案6】:

      签名int 的安全近似值是(包括潜在- 符号的位数):

      (CHAR_BIT * sizeof(int) + 1) / 3 + 1
      

      unsigned 的等价物是:

      (CHAR_BIT * sizeof(unsigned) + 2) / 3
      

      这将计算数字的数量 - 如果为空终止字符串分配空间,则将它们都加一以说明终止符。

      这将略微高估非常长的类型所需的空间(并且在 int 具有填充位的特殊情况下也会高估),但它是一个很好的近似值,并且具有以下优点:是编译时常量。 CHAR_BIT 由&lt;limits.h&gt; 提供。

      【讨论】:

      • 我认为您需要为空终止符添加字节。
      • @xanatos:如果您想将其用作将存储空终止字符串的数组大小,可以。我在这里给出的公式是明确的数字位数,包括数字本身的潜在- 符号。
      • 我什至还要补充一点,这些数字仅适用于 16、32 和 64 位(以及更高位)。我的意思是“标准”位长度。这与使公式看起来“通用”的 CHAR_BIT 的使用相冲突。例如,理论上 20 位的无符号输入最多可以包含 1048575,因此 7 位数字,但根据您的公式,6 个字符就足够了。 21 位有符号整数也会发生同样的情况。最大值为 1048575,所以 7 位数字 + 符号,但只有 7 个字符。
      • @xanatos:你说得对,除法应该向上舍入,所以必须事先添加 2 - 我已经更正了它,并明确要求空终止符需要一个额外的字节。
      猜你喜欢
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 2019-11-12
      • 2017-09-13
      • 2011-06-08
      • 1970-01-01
      相关资源
      最近更新 更多