我希望能够让用户输入他们想要做的事情。还有其他选项,但目前我正在处理"插入"。另外两个选项是"搜索"one_answers"删除"。
int main() {
char *input = malloc(sizeof(char) * 6);
printf("%s", "WELCOME TO USER'S SKIP LIST!nnWhat do you wish to do? ");
scanf("%6s", input);
if (strcmp(input, "insert") == 0) {
printf("%s", "What is the item? ");
input = realloc(input, (size_t)scanf("%s", input));
}
}
我最初为input
分配了足够的内存,使其具有6个字符。这是因为其他两个选项也只有6个字符。在他们输入insert
之后,他们必须输入一个包含任意数量字符的项目,所以我想根据他们为项目输入的内容为input
重新分配内存。因此,如果他们输入Nintendo Switch
,将重新分配15个字符。我对realloc()
的实现是正确的方法吗?
我想你主要是在问这个问题:
input = realloc(input, (size_t)scanf("%s", input));
否,这不是realloc()
的正确使用。仅这条线路就至少有四个明显的问题,包括:
-
scanf()
返回成功扫描和记录的输入项目数,如果发生错误,则返回EOF
。"输入项"对应于字段指令,例如%s
,因此特定的scanf()
调用永远不会返回大于1的值。但是,它可能返回0或EOF
(通常为-1)。 -
即使
scanf()
确实返回了读取的字符数,-
任何重新分配都为时已晚。数据由
scanf()
存储到所提供的空间中,如果空间不够大,则其边界将在任何重新分配的机会之前被溢出。 -
没有为字符串终止符保留空间。
-
realloc()
可能会失败,在这种情况下,它会返回NULL
。即使您通过在realloc()
之后检查input
来检查这一点,您也将不再有指向原始空间的指针,因此它会泄漏。
-
仔细阅读scanf
、malloc
和realloc
的文档,因此请多次阅读。你也可以参考C11标准n1570,其中提到了它们(在§7.22.3中)
这是realloc()的正确实现吗?
您不是在实现realloc
。以下是它的一个笑话实现(另请参阅malloc
的这个笑话实现):
void *realloc(void*ptr, size_t siz) {
errno = ENOMEM;
return NULL;
}
当然,您实际上会使用realloc
的严重实现(而不是上面的笑话),它是由您的C标准库实现提供的(例如,上面的操作系统原语或Linux上的系统调用,如mmap(2))。
因此,不,您没有realloc
的任何实现(并且您将使用已经在C标准库中实现的realloc
)。(至少在Linux上)你可以研究realloc
的实现,因为它通常是在一些自由软件(例如musl-libc或GNUglibc)中实现的。这里(在其文件src/malloc/malloc.c
的第369行)是realloc
的musl-libc
实现。
您错误地使用realloc
然后,您将使用scanf
作为scanf("%s", input)
。但scanf
在失败时返回EOF
,在成功时返回成功输入值的数目。通常EOF
为-1。因此,在您的情况下,scanf("%s", input)
可以返回-1(失败时)、0(如果没有处理输入值)或1(如果它在input
中放入了一些东西)。
size_t
类型是一些无符号积分类型。在我的Linux/x86-64系统上,它是一个无符号的64位数字,与unsigned long
相同。因此(size_t)(-1)
变成了一个巨大的数,即264-1。如果给(size_t)(-1)
,那么realloc
肯定会失败(因为我的系统没有那么多内存),所以realloc(input, (size_t)-1)
应该给NULL
。
如果realloc
被赋予(size_t)0
,那么它在Linux上被记录下来(请参阅realloc(3)),以执行free
所做的事情:释放给定的内存。但C标准并不要求这种行为。
如果realloc
被赋予(size_t)1
,则If将(或至少被允许)收缩内存区域(仅容纳一个字节,这不足以满足您的需求)。
所以你的程序是完全错误的
顺便说一句,你需要处理realloc
的故障,所以编码input = realloc(input, newsize);
是非常幼稚的。
此外,您的scanf("%6s", input);
是错误的(对于正好六个字节的输入,可能会出现缓冲区溢出),因为您需要终止NUL字符的空间。
因此,将您的程序扔进垃圾桶休息一下(或找点乐子)。阅读标准函数的文档(以及C动态内存分配的Wiki页面)。想一想。并且完全重写您的程序
然后用GCC编译带有所有警告和调试信息的程序:gcc -Wall -Wextra -g
。改进您的代码以避免出现任何警告。确保您的程序处理(scanf
、malloc
、realloc
等的)故障情况。阅读如何调试小程序。使用gdb
调试器和valgrind。害怕不明确的行为。
对于您的程序(您从头开始重写的程序),您可能有兴趣使用fgets(甚至在Linux上,使用getline(3)或readline(3…)用于输入。有时,您可能希望在进行实际输入之前使用memset清除输入缓冲区。
请注意,stdio是缓冲的,stdout
通常是行缓冲的。因此,养成以n
结束printf
格式字符串的习惯,或者适当地使用fflush
。
PS。valgrind
在Linux上可用(但在Windows上不可用),这也是使Linux成为一个非常适合开发人员和学生使用的操作系统的众多原因之一。因此,我建议使用一些Linux发行版来学习C编程。