【问题标题】:While reading two array in c through scanf, the second one somehow modify the first [closed]通过scanf读取c中的两个数组时,第二个以某种方式修改了第一个[关闭]
【发布时间】:2017-06-18 10:37:45
【问题描述】:

我在通过scanf 读取两个字符串时遇到以下问题:我插入了第一个字符串,一切正常,但插入第二个字符串后,第一个字符串发生了变化。

#include<stdio.h>
#define N 6
#define K 2

int main(){
    char a[N];
    char b[K];
    int i = 0,j=0;
    printf("first word\n\n\n");

    scanf("%s", a);
    for(i = 0; i <= N; i++){
        printf("%c", a[i]);
    }

    printf("second word \n\n\n");

    scanf("%s", b);
    for(i = 0; i <= N; i++){
        printf("%c", a[i]);
    }
}

第一次正确打印。第二次打印类似的字符串(也许当我插入第二个时,第一个 scanf 仍在获取输入)

【问题讨论】:

  • 你为两个 scanf 输入了什么?
  • “很明显,我在尊重数组维度的情况下尝试的输入。”——这是真的吗? for(i = 0; i &lt;= N; i++) 表明您忘记了空终止符。
  • 或许for(i = 0; a[i] &amp;&amp; i &lt; N; i++){...}
  • @Pras、abcabc 和 abc。
  • 这些输入不尊重数组维度。见@xings suggestion;在将%s 与scanf() 一起使用时,您应该始终指定最大宽度以避免缓冲区溢出。

标签: c string input scanf


【解决方案1】:

首先,您打印数组a 两次;看来您的意思是用第二个循环打印b。但是你的循环有问题。它们超出了数组范围。由于数组在 C 中是零索引的,因此您需要:

for (i = 0; i < N; i++) {}

对于a,并且:

for (i = 0;i < K; i++) {}

为b。

但即使这样也不完全正确,因为输入字符串可能不会完全填满数组。当到达空终止符或到达数组末尾时,您确实需要终止循环:

for (i = 0; a[i] != '\0' && i < N; i++) {}

和:

for (i = 0; b[i] != '\0' && i < K; i++) {}

当然,使用puts() 打印字符串会更简单。

似乎输入 (abcabc and abc) 对于数组来说太大了,导致缓冲区溢出。这可以通过在将%s 转换说明符与scanf() 一起使用时指定最大宽度来避免。

这是已发布代码的修改版本。我将N 和K 的大小增加了1,因为在原始代码中似乎没有考虑空终止符的空间:

#include <stdio.h>

#define N 7
#define K 3

int main(void)
{
    char a[N];
    char b[K];
    int i = 0;

    printf("first word\n\n\n");

    scanf("%6s", a);
    for (i = 0; a[i] != '\0' && i < N; i++) {
        printf("%c", a[i]); 
    }
    putchar('\n');

    printf("second word \n\n\n");

    scanf("%2s", b);
    for (i = 0; b[i] != '\0' && i < K; i++) {
        printf("%c", b[i]);
    }
    putchar('\n');

    return 0;
}

【讨论】:

    【解决方案2】:

    您正在打印两次a,请将第二次printf("%c", a[i]); 更改为printf("%c", b[i]);。

    【讨论】:

    • 也最好更改为:for(i = 0; i &lt; N; i++) for a,以及:for(i = 0; i &lt; K; i++) for b 以避免未定义的行为。
    • 你说得对,我没注意到。
    【解决方案3】:

    您遇到的问题是因为您调用了未定义的行为,因为您未能确保字符串是 nul-terminated 并且使用了关于 b 的错误限制。例如,你

    #define N 6
    #define K 2
    ...
        char a[N], b[K];
    

    a 总共可以容纳5-chars + nul 终止符,总共可以容纳6-chars。另一方面,b 只能持有1-char + nul-terminator 总共2-chars。

    当您随后使用for(i = 0; i &lt;= N; i++) 循环a 和b 时,不仅可以保证访问数组边界之外的元素,例如a[6](有效索引是 0-5),您还通过尝试从未初始化的值(在 word 中最后一个有效字符之后的那些未初始化的数组元素)读取少于 6 个总字符的任何 a比如,3-chars) 当你调用 Undefined Behavior 时,你的代码的执行从那一刻起就不可靠了。

    在您的情况下,您可以通过使用 field width 修饰符来限制 scanf 本身放置在数组中的字符数来消除未定义的行为,例如

        if (scanf ("%5s", a) != 1) {
            fprintf (stderr, "error: invalid input - a.\n");
            return 1;
        }
    

    您验证返回 scanf 以确保发生了正确数量的转换,或者如果没有发生错误,您可以处理错误。

    您可以通过将读取和输出字符循环限制为仅数组中的有效字符来防止读取超出数组的范围。您通过检查要打印的字符不是 nul-terminating 字符来做到这一点,并且当达到 nul-terminating 字符时,您退出循环而不尝试打印它。

    将这些部分放在一起,您可以执行类似于以下的操作(注意 j 在您的代码中未使用,因此已被注释掉):

    #include <stdio.h>
    
    #define N 6
    #define K 2
    
    int main (void ) {
    
        char a[N], b[K];
        int i = 0/*, j = 0*/;
    
        printf ("enter first word: ");
        if (scanf ("%5s", a) != 1) {
            fprintf (stderr, "error: invalid input - a.\n");
            return 1;
        }
    
        for (i = 0; a[i] && i < N; i++)
            printf ("%c", a[i]);
        putchar ('\n');
    
        printf ("enter second word: ");
    
        if (scanf ("%1s", b) != 1) {
            fprintf (stderr, "error: invalid input - b.\n");
            return 1;
        }
    
        for (i = 0; b[i] && i < N; i++)
            printf ("%c", b[i]);
        putchar ('\n');
    
        return 0;
    }
    

    使用/输出示例

    $ ./bin/twowords
    enter first word: cats
    cats
    enter second word: dogs
    d
    

    我强烈提醒您考虑使用像fgets 这样的面向行的输入函数来读取面向行的输入。这为新程序员消除了许多陷阱。使用fgets 时唯一的额外步骤是调用它读取的内容,包括结尾的'\n',因此您需要从读取的字符串中删除'\n'。

    如果您有其他问题,请查看并告诉我。

    【讨论】:

      【解决方案4】:

      第二个printf你必须写

      printf("%c", b[i]);
      

      【讨论】:

        【解决方案5】:

        根据您提到的输入(abcabc 和 abc),您提供给 scanf 导致变量 a 和 b 都溢出, 您应该为第一个输入长度为 5 的字符串,为第二个输入长度为 1 的字符串,为两个字符串保留\0 的空间

        编辑:还将循环条件从i &lt;= N 更改为i&lt;N 或i&lt;sizeof(a)。 请注意,如果字符串的长度恰好小于 5,则循环将打印超过字符串长度的垃圾字符

        【讨论】:

        • 这无关紧要,因为 OP 循环无论如何都会超出数组范围......
        • 是的,为此添加和编辑
        【解决方案6】:

        我看到你可以在这里做两件事:

        1. 使用另一个大缓冲区数组,将数据加载到其中,并将数据复制到特定数组。比你将在 a[] 中获得最多 6 个第一个单词的字母,在 b[] 中获得最多 2 个第二个单词的字母。但是你可以加载 2 个单词。

        2. Scanf 特定数量的字符,如评论中提到的 @xing。

        scanf("%6s",word) //一般用途,OP的情况下没有

        当您的单词长度超过您设置的大小时,您将把剩余的输入作为第二个单词的输入。

        【讨论】:

        • 这不是 OP 的 #define N 6 的正确宽度说明符。
        • 这只是一般示例,不适用于 OP 的情况。我什至没有使用“a”作为论据。
        • 嗯,这只是令人困惑......
        • 我认为根本不是。可能是我使用了他代码中的参数名称。
        • 这很令人困惑,因为 OP 使用 6 表示 N。为什么要随便选6?
        猜你喜欢
        • 2013-10-16
        • 2020-11-23
        • 1970-01-01
        • 1970-01-01
        • 2021-12-22
        • 2022-11-12
        • 1970-01-01
        相关资源
        最近更新 更多