使用数字作为大小创建整数的字符数组

7

我正在尝试在C语言中创建一个字符数组,用来存储整数的数字,但是这个整数可以有任意位数。

我使用了一个自定义函数叫做getDigits(int num),它返回整数的位数。

char buffer[getDigits(number)] = "";
snprintf(buffer, sizeof(buffer),"%d",number);

但是当我使用gcc编译时,它返回:

error: 可变大小的对象可能无法初始化

我已经尝试了一切。当我将其声明为char fileSizeStr[5] = "";时,它可以正常工作。我可以看到问题在于当我尝试动态声明缓冲区大小时出现,但我真的很想知道是否有一种方法可以实现这一点。


使用C99编译器或使用malloc。 - BLUEPIXY
4
е°қиҮ•з§»й™¤= ""гЂ‚snprintfдёҚењЁд№Һе…¶иң“е‡ғзә“е†ІеЊғе…€е‰Қжњ‰д»Ђд№€е†…е®№гЂ‚ - zwol
gcc prog.c -std=c99 或者替换新版本的gcc。 - BLUEPIXY
谢谢,使用 malloc 很棒! - anairinac
@Zack 我之前尝试过使用 sprintf。但是此时仅声明缓冲区,例如 char fileSizeStr[5] = ""; 或使用 malloc 可以生效。 - anairinac
显示剩余4条评论
5个回答

16

问题就是你的编译器告诉你的那样:你不能初始化可变长度数组。Zack在评论中给出了一个明显的解决方案:删除初始化。在这个答案中,你会找到一些具有初始值的工作示例以及一些不允许初始值的示例。关于此更多信息请参阅评论。以下示例按最明智(依我之见)到最不明智(涉及使用malloc)的顺序排列,用于为表示数字的十进制位数分配存储空间。


我建议使用与确定将int值存储为八进制所使用的相同技巧来确定存储其十进制位数所需的字节数:将int中的总位数除以3并添加任何符号和NUL终止符。 digit_count可以像这样编写一个预处理器宏:

#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#define digit_count(num) (1                                /* sign            */ \
                        + sizeof (num) * CHAR_BIT / 3      /* digits          */ \
                        + (sizeof (num) * CHAR_BIT % 3 > 0)/* remaining digit */ \
                        + 1)                               /* NUL terminator  */

int main(void) {
    short short_number = -32767;
    int int_number = 32767;
    char short_buffer[digit_count(short_number)] = { 0 }; /* initialisation permitted here */
    char int_buffer[digit_count(int_number)];
    sprintf(short_buffer, "%d", short_number);
    sprintf(int_buffer, "%d", int_number);
}

正如你所看到的,一个强大的好处是可以在不修改代码的情况下将 digit_count 用于任何类型的整数: charshortintlonglong long 和相应的 unsigned 类型。

与此相比的一个小缺点是你会浪费一些存储空间,特别是对于像 1 这样的小值。在许多情况下,这种解决方案的简单性远远弥补了这一点;在运行时计算十进制数字所需的代码将占用比此处浪费的空间更多的内存。


如果你准备放弃上面代码的简单性和通用性,并且真的想要计算十进制数字的数量,Zacks 的建议就适用:移除初始化。下面是一个例子:

#include <stddef.h>
#include <stdio.h>

size_t digit_count(int num) {
    return snprintf(NULL, 0, "%d", num) + 1;
}

int main(void) {
    int number = 32767;
    char buffer[digit_count(number)]; /* Erroneous initialisation removed as per Zacks advice */
    sprintf(buffer, "%d", number);
}

针对 malloc 推荐的建议:解决此问题最不可怕的方法是避免不必要的代码(例如调用 malloc 和稍后调用 free)。如果您不必从函数返回对象,则不要使用 malloc!否则,考虑将其存储到调用方提供的缓冲区中(通过参数),以便调用方可以选择使用哪种类型的存储。这很少不是使用 malloc 的合适替代方法。

然而,如果您决定为此使用 mallocfree,请使用最不可怕的方法。避免在 malloc 的返回值上进行类型转换和乘以 sizeof(char)(始终为1). 以下代码是一个例子。使用以上任一方法计算长度:

char *buffer = malloc(digit_count(number)); /* Initialisation of malloc bytes not possible */
sprintf(buffer, "%d", number);

...不要忘记在使用完后调用free(buffer);


翻译:...在你完成使用之后,不要忘记调用free(buffer);

5

尝试类似以下内容:

 char* buffer =(char *)malloc(getDigits(number)*sizeof(char));

malloc和calloc用于动态分配内存。


4
  1. 不要调用 malloc 函数。
  2. 乘以 sizeof(char) 没有意义,因为在 C 语言中 sizeof(char) 总是等于 1。
- autistic
嗯,有趣。根据我刚刚搜索的内容,在C语言中我们确实不必在这种情况下进行强制类型转换。但是在C++程序中,我们需要这样做。感谢提供信息! - Rafael Azevedo

4

我认为有一种解决方案未曾提及,但实际上比以上所有方案都更简单。在Linux和大多数BSD变体中,有一个称为"asprintf"的组合分配版本的sprintf可用。它确定所需的大小,mallocs内存,并将填充好的字符串返回到第一个参数。

char * a;
asprintf(&a, "%d", 132);
// use a
free(a);

使用堆栈分配的数组确实消除了free的需要,但这完全避免了有必要单独计算大小的需要。


3
注意:在没有 asprintf 的系统上,使用 C99 可以很容易地通过两次调用 snprintf 来自行实现。 - M.M

3
以下内容可能有所帮助。
char* buffer;
buffer = (char*)malloc(number * sizeof(char));

2
  1. 不要强制转换malloc。
  2. 在C语言中,没有必要乘以sizeof(char),因为sizeof(char)始终为1。
- autistic
为什么不使用强制类型转换malloc?我认为在旧版C中只有一种分配内存的方法。 - NonStatic
@NigelHarper,我认为我写的是将返回值转换类型。 - NonStatic
@mmsc 是的,你正在转换 malloc 的返回值,根据我分享的答案所述,你不应这样做。 - Nigel Harper

2

你需要使用 malloc 来分配动态内存。

只有在编译时知道大小的情况下,才允许像你这样初始化。


谢谢!我简直不敢相信自己完全忘记了使用malloc。 - anairinac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接