简体   繁体   English

在c数组的单个元素中输入字符串时,为什么以下代码给出显示的输出?

[英]While entering a string in an single element of array in c why does the following code gives the shown output?

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i, n, m;

    scanf("%d %d", &n, &m);

    char s[m][n];

    for (i = 0; i < m; i++) {
        printf("the string --\n");
        scanf("%s", s[i]);
    }

    for (i = 0; i < m; i++) {
        printf("the strings are %s \n",s[i]);
        printf("\n");
    }

    return 0;
}

The output is: 输出为:

2 2  
the string --
10  
the string -- 
11  

the strings are 1011 

the strings are 11

Why is the first string 1011 instead of 10 ? 为什么第一个字符串1011代替10

In C, strings are represented as a sequence of char values, terminated by a null character ( 0 or '\\0' ). 在C语言中,字符串表示为一系列char值,以一个空字符( 0'\\0' )结尾。 This means that to store a two-character string, you need space for three characters: the two characters of string content, plus the null terminator character. 这意味着要存储两个字符的字符串,需要三个字符的空间:字符串内容的两个字符以及空终止符。

Here, you've only allocated enough space for two characters in each string, but you need space for three. 在这里,您只为每个字符串中的两个字符分配了足够的空间,但是您需要为三个字符分配空间。

So, it reads the first string into the array s[0] , but the null terminator doesn't fit, and so it overflows into the second array s[1] . 因此,它将第一个字符串读入数组s[0] ,但空终止符不适合,因此它溢出到第二个数组s[1] Now your array of arrays s looks like this: {{'1', '0'}, {'\\0', ... }} . 现在,您的数组s看起来像这样: {{'1', '0'}, {'\\0', ... }}

Then, when it reads the second string into the array s[1] , it overwrites the overflowed null terminator from before. 然后,当它将第二个字符串读入数组s[1] ,它将覆盖之前的溢出空终止符。 And the null terminator for the second string doesn't fit into its own array, so it overflows again into the rest of the stack. 第二个字符串的null终止符不适合自己的数组,因此它再次溢出到堆栈的其余部分。 The program might crash here, or corrupt other data, because you're overflowing past the end of the array. 该程序可能会在此处崩溃,或破坏其他数据,因为您超出了数组末尾的位置。

So now your array of arrays s ends up looking like this: {{'1', '0'}, {'1', '1'}} , followed by a '\\0' somewhere after the end of the array. 因此,现在您的数组s的数组看起来像这样: {{'1', '0'}, {'1', '1'}} ,然后在数组结尾之后的某个位置加上'\\0'

When printf goes to read your first string, it prints characters until it finds a null terminator. printf读取第一个字符串时,它会打印字符,直到找到空终止符。 But it doesn't find one in the first string, so it keeps going, and hits the second string. 但是它没有在第一个字符串中找到一个,因此它继续前进,并命中了第二个字符串。 It doesn't find one there either, and continues past the end of the array. 它也找不到一个,并继续经过数组的末尾。 In your case, luckily a null terminator was right there, but for all we know there could be something else. 就您而言,幸运的是,那里有一个空终止符,但就我们所知,可能还有其他事情。

To fix this, you need to allocate an extra character per string on line 9, for the null terminator: 要解决此问题,您需要在第9行的每个字符串上为空终止符分配一个额外的字符:

    char s[m][n+1];

There's another problem here, however. 但是,这里还有另一个问题。 What if your input gives you the wrong length? 如果您输入的长度不正确怎么办? For example, what if your input says 2 3 , ie that the following strings will have a length of 3, but gives you the string foobar , which is 6 characters? 例如,如果您输入的内容为2 3 ,即以下字符串的长度为3,但给您的字符串foobar6个字符,该怎么办? Your code right now would overflow the buffer when it read that string, because it doesn't ensure it's the right length. 现在,您的代码在读取该字符串时会溢出缓冲区,因为它不能确保长度正确。

One way to avoid this would be to use gets_s instead of sscanf() for reading the strings on line 13: 避免这种情况的一种方法是使用gets_s而不是sscanf()来读取第13行的字符串:

        gets_s(s[i], n+1);

This will read at most n characters, so avoid crashing your program or creating a security issue. 这将最多读取n字符,因此避免崩溃程序或造成安全问题。 However, gets_s is a C11 function, so you may not be able to use it. 但是, gets_s是C11函数,因此您可能无法使用它。

You must set column size to 3 if you insert 2 characters per string, 4 for 3 characters per string and so on. 如果每个字符串插入2个字符,每个字符串插入3个字符,则必须将列大小设置为3,以此类推。 This because string in C have a termination character ('\\0') in the last position. 这是因为C中的字符串在最后一个位置具有终止字符('\\0')

     #include <stdio.h>

int main(int argc, char *argv[])
{
    int i, n, m;

    scanf("%d %d", &n, &m);

    char s[m][n+1];

    for (i = 0; i < m; i++) {
        printf("the string --\n");
        scanf("%s", s[i]);
    }

    for (i = 0; i < m; i++) {
        printf("the strings are %s \n",s[i]);
        printf("\n");
    }

    return 0;
}

%s expects null terminated strings as an argument. %s期望以空终止的字符串作为参数。 When first string is read by scanf , there is not enough space for the null terminator within the allocated memory of first string. scanf读取第一个字符串时,分配给第一个字符串的内存中没有足够的空间用于空终止符。 It will goes to the space next to the allocated space. 它将转到已分配空间旁边的空间。 Writing to unallocated space invokes undefined behavior. 写入未分配的空间会调用未定义的行为。

While printing the strings with %s specifier, printf write the string character by character till it finds a null terminator '\\0' . 在使用%s说明符打印字符串时, printf逐字符写入字符串,直到找到空终止符'\\0'为止。 Here it may be the case that both the strings 10 and 11 are stored one after another in memory, so printf writes the first string till it read the null character of second string. 在这种情况下,字符串1011可能一个接一个地存储在内存中,因此printf写入第一个字符串,直到读取第二个字符串的空字符为止。
Input n as 3 and you will get the desire results. 输入n3 ,您将获得期望的结果。

In my opninion using scanf to read strings is just pure evil. 在我看来,使用scanf读取字符串纯粹是邪恶的。 That said the array s[m][n] is just s[m*n] of course. 那就是说数组s[m][n]当然只是s[m*n] That said that evil thing scanf is going to load on *s[0] 10\\n\\0 and on *s[1*n] or *s[2] 11 and *s will be 1011\\n\\0 就是说邪恶的东西scanf将加载到*s[0] 10\\n\\0*s[1*n]*s[2] 11*s将是1011\\n\\0

And this is a monument to bad C coding. 这是不良C编码的纪念碑。 I guess it's just an example but if I was asked this question i would say: "Come on, get me real things" 我想这只是一个例子,但是如果我被问到这个问题,我会说:“来吧,给我带来真实的东西”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM