int main(void) {
char *input;
printf("prompt>");
scanf("%s", input);
printf("%s", input);
return 0;
}
提示>输入
RUN FAILED(退出值138,总耗时:3s)
代码有什么问题?必须是 scanf() 或第二个 printf()。输入的长度未知。很多人都说过简单地创建一个长度为“X”的字符数组来保存输入。只是想知道为什么这段代码有效。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
/* prompt */
char input;
printf("prompt>");
scanf("%s", &input);
printf("%s", &input);
return 0;
}
最佳答案
您的具体问题是input
后面没有存储空间。它是一个未初始化的指针,指向内存中的一个随机点,不太可能在任何地方有用。
你可以使用类似的东西:
char *input = malloc (100);
// check that input != NULL
// use it
free (input);
或:
char input[100];
但是您在使用 scanf
时遇到了严重的问题(见下文)。
您应该永远不要在scanf
(或其任何变体,除非您完全控制输入)中使用无限制的%s
。这是一种容易导致缓冲区溢出的危险做法,越早改掉这个习惯越好。它在这方面类似于 gets()
。
根据我之前的回答,下面的这段代码(连同您的主要代码并入其中)提供了一种获取用户输入的安全方式。您传入一个可选提示、将输入加载到的缓冲区以及缓冲区的大小。
它将返回最大缓冲区大小的输入(如果有,则去除换行符)然后在必要时清除该行的其余部分,这样它就不会影响下一个输入操作。如果文件结束或输入太长,它将返回 OK 或错误指示(如果您想对其执行某些操作,您仍会获得输入的第一部分)。
一旦您拥有线路,您就可以sscanf
它,安全地,随心所欲。但是,这在您的情况下不是必需的,因为您只是想获取一个字符串。只需使用直接返回的缓冲区即可。
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
int main(void) {
char input[10];
int rc = getLine ("prompt> ", input, sizeof (input));
switch (rc) {
case NO_INPUT: printf ("\nNo input recieved\n"); break;
case TOO_LONG: printf ("Too long, truncated input below:\n");
default: printf("Your input was [%s]\n", input);
}
return 0;
}
试一试,它比单独使用 scanf("%s")
更健壮。
至于你的更新询问为什么这有效:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
/* prompt */
char input;
printf("prompt>");
scanf("%s", &input);
printf("%s", &input);
return 0;
}
这是未定义的代码。时期。您只为一个字符分配空间,但您扫描一个字符串。由于字符串是所有字符的字符数组后跟一个零字符,因此您唯一可以安全输入的字符串将是一个空字符串。
任何其他内容都会同时写入字符 和 恰好与堆栈中的字符相邻的任何内容。
这与分配 char input[100]
然后输入 200 个字符没有什么不同,它仍然是缓冲区溢出,应该避免。
下面的讨论基于 C 的特定实现,不一定是所有实现。
很可能,您在这里很幸运。编译器可能会生成使堆栈指针保持对齐的代码,这样即使您要求一个字节,您也可能会获得分配给四个字节的空间(甚至更多,具体取决于体系结构——为简单起见,我假设大多数类型都是四个字节) ).
此外,您可能会发现您还可以安全地覆盖 argc 整数和 argv 指针的八个字节(即使您不使用它们,它们可能仍然存在,拥有两组不同的启动是没有意义的代码只是为了在堆栈上保存几个字节)。
如果你写得比这更远,你最终会把 main
的返回地址覆盖到你的启动代码中。 然后你就会知道它,因为当 main
退出时你的代码将进入 la-la land。
对于未定义的行为,任何事情 都可能发生。有时,任何 都包含它完美运行的可能性(类似于“经常将一副纸牌抛向空中,它们最终会落在整齐有序的堆中”,但随机性稍差一些).
这并没有使未定义的行为成为一件坏事。
关于C编程输入错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3732084/