Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C语言中的字符串输入和输出_C_String - Fatal编程技术网

C语言中的字符串输入和输出

C语言中的字符串输入和输出,c,string,C,String,我有一段代码: char* receiveInput(){ char *s; scanf("%s",s); return s; } int main() { char *str = receiveInput(); int length = strlen(str); printf("Your string is %s, length is %d\n", str, length); return 0; } 我收到以下输出: Your

我有一段代码:

char* receiveInput(){
    char *s;
    scanf("%s",s);

    return s;
}

int main()
{
    char *str = receiveInput();
    int length = strlen(str);

    printf("Your string is %s, length is %d\n", str, length);

    return 0;
}
我收到以下输出:

Your string is hellàÿ", length is 11
我的意见是:

helloworld!

有人能解释一下为什么,以及为什么这种编码方式不好,提前感谢scanf并没有为您分配内存

您需要为传递给
scanf
的变量分配内存

你可以这样做:

char* receiveInput(){
    char *s = (char*) malloc( 100 );
    scanf("%s",s);
    return s;
}
// s must be of at least 100 chars!!!
char* receiveInput( char *s ){
    scanf("%99s",s);
    return s;
}
int main()
{
    char str[100];
    receiveInput( str );
    int length = strlen(str);

    printf("Your string is %s, length is %d\n", str, length);

    return 0;
}
但警告:

  • 调用
    receiveInput
    的函数将获得返回内存的所有权:在
    main
    中打印后,必须
    free(str)
    。(以这种方式放弃所有权通常不被视为良好做法)

    一个简单的解决方法是将分配的内存作为参数

  • 如果输入字符串长于
    99
    (在我的例子中),您的程序将遭受缓冲区溢出(这就是它已经发生的情况)

    一个简单的解决方法是将缓冲区的长度传递给
    scanf

    scanf("%99s",s);
    
  • 固定代码可以是这样的:

    char* receiveInput(){
        char *s = (char*) malloc( 100 );
        scanf("%s",s);
        return s;
    }
    
    // s must be of at least 100 chars!!!
    char* receiveInput( char *s ){
        scanf("%99s",s);
        return s;
    }
    int main()
    {
        char str[100];
        receiveInput( str );
        int length = strlen(str);
    
        printf("Your string is %s, length is %d\n", str, length);
    
        return 0;
    }
    

    必须首先在receiveInput()方法中将内存分配给s对象。例如:

    s = (char *)calloc(50, sizeof(char));
    

    有几个问题已经解决了你做错了什么以及如何修复它,但你也说了(我的重点):

    有人能解释一下为什么,以及为什么这种编码方式不好吗

    我认为
    scanf
    是一种读取输入的糟糕方式。它与
    printf
    不一致,很容易忘记检查错误,很难从错误中恢复,并且与普通(更容易正确执行)读取操作(如
    fgets
    和company)不兼容

    首先,请注意,
    “%s”
    格式将是只读的,直到它看到空白为止。为什么是空白?为什么
    “%s”
    打印出整个字符串,但以如此有限的容量读入字符串

    如果您想阅读整行内容,就像您经常做的那样,
    scanf
    提供。。。使用
    “%[^\n]”
    。什么?那是什么?这是什么时候变成Perl的

    但真正的问题是这两个都不安全。它们都可以自由溢出,没有边界检查。要检查边界吗?好的,你明白了:
    %10s”
    (而且
    %10[^\n]“
    看起来更糟了)。这将只读取9个字符,并自动添加一个终止nul字符。所以这很好。。。当我们的阵列大小永远不需要更改时

    如果要将数组的大小作为参数传递给scanf,该怎么办
    printf
    可以执行以下操作:

    char string[] = "Hello, world!";
    printf("%.*s\n", sizeof string, string); // prints whole message;
    printf("%.*s\n", 6, string); // prints just "Hello,"
    
    想用scanf做同样的事情吗?以下是方法:

    static char tmp[/*bit twiddling to get the log10 of SIZE_MAX plus a few*/];
    // if we did the math right we shouldn't need to use snprintf
    snprintf(tmp, sizeof tmp, "%%%us", bufsize);
    scanf(tmp, buffer);
    
    没错-
    scanf
    不支持
    “%.*s”
    变量精度
    printf
    支持,因此要使用
    scanf
    进行动态边界检查,我们必须在临时缓冲区中构造自己的格式字符串。这是各种各样的坏主意,即使这里实际上是安全的,但对任何一个来访的人来说,这都是一个非常糟糕的主意

    同时,让我们看看另一个世界。让我们看看
    fgets
    的世界。下面是我们如何使用
    fgets
    读取一行数据:

    fgets(buffer, bufsize, stdin);
    
    无限少的头痛,没有浪费处理器时间将整数精度转换为字符串,该字符串只能由库重新解析为整数,所有相关元素都在一行上,让我们看看它们是如何协同工作的

    当然,这可能不会读取整行。如果行短于
    bufsize-1
    个字符,它将只读取整行。下面是我们如何阅读整行文字:

    char *readline(FILE *file)
    {
        size_t size  = 80; // start off small
        size_t curr  = 0;
        char *buffer = malloc(size);
        while(fgets(buffer + curr, size - curr, file))
          {
            if(strchr(buffer + curr, '\n')) return buffer; // success
            curr = size - 1;
            size *= 2;
            char *tmp = realloc(buffer, size);
            if(tmp == NULL) /* handle error */;
            buffer = tmp;
          }
        /* handle error */;
    }
    
    curr
    变量是一种优化,它可以防止我们重新检查已经读取的数据,并且是不必要的(尽管在读取更多数据时很有用)。如果您愿意,我们甚至可以使用
    strchr
    的返回值来去掉
    结尾的“\n”
    字符

    还要注意
    size\u t size=80作为起始位置完全是任意的。我们可以使用81、79或100,或者将其作为用户提供的参数添加到函数中。我们甚至可以添加一个
    int(*inc)(int)
    参数,并更改
    size*=2
    size=inc(大小),允许用户控制阵列的增长速度。当重新分配成本高昂且需要读取和处理大量数据行时,这些方法有助于提高效率

    我们可以使用scanf
    scanf
    编写相同的代码,但是想想我们需要重写多少次格式字符串。我们可以将其限制为常量增量,而不是上面实现的加倍(很容易),并且不必调整格式字符串;我们可以让步,只存储数字,用上面的方法进行计算,每次重新分配时使用
    snprintf
    将其转换为格式字符串,以便
    scanf
    可以将其转换回相同的数字;我们可以通过手动调整格式字符串的方式限制增长和起始位置(比如,只增加数字),但这可能会在一段时间后变得棘手,可能需要递归(!)才能干净地工作

    此外,很难将读取与
    scanf
    和读取与其他功能混合在一起。为什么?假设您想从一行中读取一个整数,然后从下一行中读取一个字符串。你可以试试这个:

    int i;
    char buf[BUSIZE];
    scanf("%i", &i);
    fgets(buf, BUFSIZE, stdin);
    
    这将读取“2”,但是
    fgets
    将读取空行,因为
    scanf
    没有读取换行符!好的,拿两个:

    ...
    scanf("%i\n", &i);
    ...
    
    你认为这会消耗换行符,确实如此——但它也会消耗下一行的前导空格,因为
    scanf
    无法区分换行符和其他形式的空格。(另外,事实证明您正在编写Python解析器,在行中引入空格非常重要。)要实现这一点,您必须调用
    getchar
    或在换行符中读取的内容并将其丢弃:

    ...
    scanf("%i", &i);
    getchar();
    ...
    
    这不是很傻吗?如果在函数中使用
    scanf
    ,但是