【问题标题】:While loop with semicolon [closed]带分号的while循环[关闭]
【发布时间】:2016-04-05 14:04:05
【问题描述】:

这条指令有什么作用? while 循环中使用分号有什么用?

while (scanf("%s", &s[strlen(s)]) == 1);

我正在开发一个程序来检查字符串是否是 panagram。 完整代码如下:

int main() {

char s[100];
while (scanf("%s", &s[strlen(s)]) == 1);
char big[26] = {0};
char small[26] = {0};
for (int i = 0; i < strlen(s); i++) {
    if (s[i] >= 'a' && s[i] <= 'z') {
        small[s[i] - 'a'] = 1;
    }
    else if (s[i] >= 'A' && s[i] <= 'Z') {
        big[s[i] - 'A'] = 1;
    }
}

for (int i = 0; i < 26; i++) {
    if (!(big[i] == 1 || small[i] == 1)) {
        printf("not pangram");
        return 0;
    }
}

printf("pangram");

return 0;

}

【问题讨论】:

  • s 的大小是多少?只是问...
  • @SouravGhosh 我已将数组声明为 char s[100];
  • 请读一本C书。你要求我们辅导你。
  • 这比仅仅覆盖空终止符要糟糕得多。代码说“将整个字符串存储在字符号 strlen(s) 的地址”。它不仅会破坏空终止符并因此在循环的下一圈崩溃并烧毁,而且它也可能会越界。
  • @GayathriRavi 如果这是您的更新,此代码将调用 UB。 s 必须在进入该 while 循环之前终止。不是。

标签: c while-loop scanf


【解决方案1】:

strlen(s) 返回字符串的长度(假设它实际上是一个正确终止的字符串)。

s[strlen(s)] 因此指定字符串的终止空字节。

因此,&amp;s[strlen(s)] 是指向字符串终止符的指针,或者等价地指向s 的零长度尾子字符串。

scanf("%s", &amp;s[strlen(s)]) 因此尝试将标准输入中的空格分隔字符串扫描到s 的尾部。如果s 有足够的未使用空间并且输入确实可用,那么这很可能会成功。如果是,则scanf() 返回1,即成功扫描的输入字段数。

如果在扫描任何非空白之前检测到文件结尾,那么scanf() 将返回零而不向s 添加任何内容。

因此,每次测试while 条件(scanf("%s", &amp;s[strlen(s)]) == 1) 时,它都会尝试将标准输入中的下一个字符串附加到s 的末尾。

循环体是一个空语句(;)。这什么也没做,所以整个循环只是重复测试循环条件,直到它评估为假。前提是s 最初是一个终止的字符串并且它有足够的空间,这具有将标准输入(减去所有空格)连接到s 的总体副作用。但是,如果不满足这些条件,则行为未定义。

由于您没有采取任何措施来确保 s 最初被终止,因此您面临着 UB 的严重风险(尽管行为是否被定义实际上是不确定的)。此外,您的程序很容易受到缓冲区溢出的影响,因为它会将用户提供的输入扫描到固定大小的缓冲区中,而不会验证是否有足够的可用空间。

【讨论】:

  • 那可能是对的,除了 UB。
  • @MartinJames,当我第一次写答案时,并没有确定有任何 UB。我现在更新了以s 被终止为前提的答案。它已经基于不超出s 的界限。你认为 UB 有什么其他基础吗?
【解决方案2】:
while(1); 

一样
while(1)
{
  // Nothing
}

有关您的特定 while 条件的作用,请参阅其他答案。

【讨论】:

    【解决方案3】:

    在 C 中,字符串是由空字符 (0) 终止的字符数组,或者当 char 是单个字节时它们曾经是。 :-)

      s[strlen(s)]    always zero 
      &s[strlen(s)]   a pointer to zero
    

    scanf 调用将从标准输入读取一个字符串,并将其写入您传入的地址。

     scanf("%s", pointer);
    
     // does this...
    
     *pointer = "input string";
    

    由于您传入了该字符串的最后一个字节的地址,它将覆盖当前的字符串结尾。

    换句话说,代码:

    • 从标准输入读取字符串。
    • 将其附加到 s
    • 假设分配给 s 的内存区域足够大,可以容纳用户输入的任何内容(它永远不会,因此您总是可以通过按住某个键来使代码崩溃)。

    【讨论】:

    • 尽管(永远)不需要如此,但在我知道或期望看到的每个现代实现中,C char 仍然是 8 位。
    • 谢谢,这真是令人欣慰……
    【解决方案4】:

    这是 UB。在您添加所有代码之前,它是隐含的 UB,因为您没有证明 's' 是安全地以空值结尾的。添加代码后,它是明确且明确的 UB,因为您证明 s 不是安全地以空值终止的。

    【讨论】:

    • 你能定义UB吗?
    • 这里如果要技术性的话,未初始化的自动变量的内容是indeterminate,因此不确定代码是否有定义的行为。当然,并不是这样更好。
    • 哈哈,我现在记得了。很高兴这些天变量被初始化为零:-)
    • @buffjape:你如何判断变量“这些天”初始化了?
    • @buffjape,C# 不是 C。虽然语法相似,但它的语义与 C 有很大不同。它是作为 Java 的竞争对手而创建的,并且在那里有很多相似之处。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-14
    • 2014-03-22
    • 1970-01-01
    • 1970-01-01
    • 2017-04-11
    • 2013-04-20
    • 2016-03-25
    相关资源
    最近更新 更多