C编程输入错误

时间:2010-09-17 01:24:07

标签: c input printf scanf

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;
}

6 个答案:

答案 0 :(得分:6)

您的具体问题是input后面没有存储空间。它是一个未初始化的指针,指向内存中的随机点,这不太可能有用。

您可以使用以下内容:

char *input = malloc (100);
// check that input != NULL
// use it
free (input);

或:

char input[100];

但您使用scanf时遇到严重问题(见下文)。


除非您完全控制输入,否则您永远不会<{>} <{1}}(或其任何变体)中使用无界%s。这是一种容易出现缓冲区溢出的危险做法,越早摆脱这种习惯就越好。它以这种方式类似于scanf

从我之前的一个回答中,下面这段代码(以及包含在其中的主要代码)提供了一种获取用户输入的安全方式。您传入一个可选的提示,缓冲区以加载输入,以及缓冲区的大小。

它会将输入返回到缓冲区的大小(如果存在,则删除换行符),然后在必要时清除行的其余部分,以便它不会影响下一个输入操作。它将在文件结尾返回OK或错误指示,或者如果输入太长(如果你想对它做一些事情,你仍然得到输入的第一部分)。

一旦你有了这条线,你就可以安全地gets()到你的内心。但是,在您的情况下不需要这样做,因为您只是尝试获取字符串。只需使用直接返回的缓冲区。

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; } 然后输入200个字符没什么不同,它仍然是缓冲区溢出,应该避免。

下面的讨论基于C的特定实现,不一定是所有实现。

很有可能,你在这里很幸运。编译器可以生成保持堆栈指针对齐的代码,这样即使你要求一个字节,也可以为4个空间分配空间(甚至更多,具体取决于体系结构) - 为了简单起见,我假设大多数类型都是4个字节)。

另外,您可能会发现您还可以安全地覆盖argc整数和argv指针的八个字节(即使您不使用它们,它们可能仍然存在,没有一点有两组不同的启动代码只是为了在堆栈上保存几个字节。)

如果您进一步编写,则最终会将char input[100]的返回地址覆盖到您的启动代码中。 然后你会知道它,因为你的代码会在main退出时进入la-la land。

对于未定义的行为,任何都可能发生。有时任何东西包括它可以完美地工作的可能性(类似于“经常在空中抛出一副牌,它们最终将落入一个漂亮整齐的堆中”但随机性稍差一点)。

这不会使未定义的行为变得更糟糕。

答案 1 :(得分:3)

  char *input;

只是一个指针 - 没有分配数据空间来存储scanf收集的数据。

试试这个

char input[100];

答案 2 :(得分:1)

您可能想要在具有分隔字符的scanf("%c", input)循环内尝试while。您还应该输入一个数组char input[X],其中X是一个足够值的数字,用于保存输入的最可能值。我会先尝试输入一个数组。

答案 3 :(得分:1)

您使用什么编译器?在Turbo C 3.0中它可以工作。 试试这个变种:

#include <stdio.h>
#include <alloc.h>
int main(void) 
{
    char *input = (char*)calloc(100, sizeof(char));
    printf("prompt>");
    scanf("%s", input);
    printf("%s", input);
    free(input);
    return 0;
}

答案 4 :(得分:1)

在使用指针之前忘了分配内存。

试一试:

int main(void) {
    char input[256];
    printf("prompt>");
    scanf("%s", input);
    printf("%s", input);
    return 0;
}

甚至:

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

int main(void) {
    char *input = (char *) malloc(sizeof(char) * 256));
    printf("prompt>");
    scanf("%s", input);
    printf("%s", input);
    return 0;
}

答案 5 :(得分:1)

尝试: -

int main(void) { 
char input[100]; 
printf("prompt>"); 
scanf("%99s", input); 
printf("%s", input); 
return 0; 

}

这会将字符串限制为99个字节。注意“%s”==由空格或换行符分隔的字符串,即。你只得到第一个字!

我认为你真正想要的是:

#include <stdio.h>
int main(void) { 
    char input[99]; 
    printf("prompt>"); 
    fgets(input,99,stdin);
    printf("->%s<-", input); 
    return 0; 
} 

你可能需要添加一些代码来摆脱不需要的新行字符!