tutorialspoint 中的 "C mock tests" 是否正确?

Are the "C mock tests" at tutorialspoint correct?

由于我编写 C 语言已有 20 多年,我认为是时候参加考试了!看看我是否学到了任何东西,或者我是否只是一些欺诈性的帖子,免费但不正确地向 Internet 上的初学者提供建议。

此站点(我不是附属站点)提供免费的 C 测试。 https://www.tutorialspoint.com/cprogramming/cprogramming_mock_test.htm.

我参加了测试 1,但以 50 分中的 34 分惨败!是这个吗?我必须放弃我的 C 程序员职业吗?这个 Tutorialspoint 网站及其测试有多好?

具体来说,Q7,Q9,Q10,Q14,Q16,Q17,Q19,Q21,Q27,Q28,Q31,Q32,Q33,Q35,Q38,Q46没有按考试要求的答案回答。这些问题的正确答案是什么?

此外,当我在符合标准的 C 编译器 (gcc -std=c11 -pedantic-errors) 上编译问题时,其中 none 甚至会通过编译。这是为什么?我 and/or 我的编译器坏了吗?或者这个网站根本不是很好?

这个网站一点也不好。

这些问题是为 1999 年撤回的旧版本的 C 语言编写的。它允许您将 main 写为 main(),没有 return 类型。 20 多年来,这一直不是有效的 C,所以这就是它无法编译的原因。您需要使用 -std=c90.

进行编译

虽然在旧 C90 中在 main() 之前有隐式 int,OS 将使用函数 main() 的 return 值,所以如果没有 return 语句,如这些示例中所示,这意味着未定义的行为 (C11 6.9.1/12)。

值得注意的是,整个测试还存在 printf 中缺少 \n 的问题,这意味着 stdout 直到程序结束才被刷新。 C 保证它在程序终止时被刷新。

具体来说,这些问题也不正确:

  • Q7:None 的答案可能是正确的。操作数 'A'255 的类型为 int,因此加法(假设 A=65)保证不会溢出,但会导致 65 + 255 = 320。这导致 int 然后通过简单的赋值转换为 c 的类型,即 char。这又可能是有符号或无符号类型,具体取决于编译器。这会影响转换是根据 C11 6.3.1.3/2 明确定义还是根据 6.3.1.3/3 实现定义。一个可能的结果是 320 = 140h,截断:40h = 64。这会在 gcc x86 编译器上为 Linux.

    打印字符 '@'
  • 问题 9:代码导致编译器错误,因为它违反了简单赋值规则的约束 ()。他们可能打算写 unsigned x = 5, *y=&x, *p = y+0;,在这种情况下,结果是未指定的——不能保证表达式 *y=&x 在表达式 *p = y+0 之前被求值。参见 C11 6.7.9/23:

    The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.

    所以不管你怎么说,整个问题根本就是错误的。

  • 问题 10:关于是否转换 malloc 的结果,可能会引发很多风格问题。但除此之外,假设 #include <stdlib.h> 存在,代码就可以了。如果不存在包含(如问题中所示),则代码已损坏,任何事情都可能发生。

  • Q14:无限循环,无限打印“Hello”。它不打印“无限循环”。

  • Q16:见Q14。此外,一个不错的编译器(例如 gcc -Wall)可能会在这里抛出一些诊断消息,因此回答“编译错误”不一定是错误的。取决于编译器设置。

  • Q17:假设2的补码计算机,则-2。从理论上讲,它可以打印 -1 或 -2 或 -(大数),具体取决于计算机是使用一个补码、二进制补码还是有符号大小。

  • Q19:正确答案是编译错误。又是因为简单赋值的限制。

  • Q21:假设 65'A' 的符号 table 值,那么它可以打印 'A'(小端)或对应于 0 的符号(大端)。后者很可能看起来像“垃圾”。

  • Q27:正确答案是 strcmp 函数的使用无效,因为缺少 #include <string.h>。否则它会打印 0.

  • Q28:编译错误。有趣的是测试是多么不一致。在这里它突然不允许从整数到指针的隐式转换,它早先愉快地(但错误地)允许了。

  • Q31:B或C甚至D。这取决于int的大小,几乎可以肯定是2或4。但是,编译器可以自由地在末尾添加填充联合,所以它还不如打印一个更大的数字。

  • Q32:正确答案确实依赖于编译器,但是......为什么哦,为什么它在 Q31 中不依赖于编译器?

  • Q33:C 允许我们写 shortshort intint short - 都是等价的,所以这个问题没有多大意义。

  • Q35:代码没有编译,没有输出

  • Q38:输出的是7,不是6。

  • Q46:union中只有char成员被赋值,其余的都是不确定的值。联合成员 x 被声明为具有自动存储持续时间并且其地址从未被占用,因此访问它是未定义的行为。

    如果不是这样,它会尝试打印一些不确定的值(“垃圾”)甚至 65 或 0,具体取决于 CPU 字节顺序。

我对 TutorialsPoint 的 C 模拟测试 #1 中显示的代码有很多保留意见。使用对 C99 无效的代码很奇怪,更不用说 C11 或 C17 了。上个千年的代码不应该仍然教给新程序员——除非作为语言自首次标准化以来发生变化的实物课程。

这个 SO 问题最初讨论了模拟测试的 Q3,但是 SO 问题和主要 已经被修改以删除对那个问题的评论。

Q3 的代码是:

#include<stdio.h>

main() 
{ 
   char s[]="hello", t[]="hello";
   
   if(s==t){
       printf("eqaul strings");
    }
}

数组st必须在不同的位置;它们是独立的数组,由相同的字符串初始化,但仍然是独立的数组,因此存储在不同的地址。条件比较存储数组的地址(字符串比较将使用 strcmp() 或等效项),并且数组存储在不同的地址,因此比较结果为 false。

  • 因此,唯一正确的答案是 C — 无输出。
  • 这是 TutorialsPoint 在他们的密钥中给出的答案。

有人讨论了字符串文字的 SO 以及它们可以存储在同一位置的事实。但是,该讨论被误导了。它不适用于此代码。用于初始化数组的字符串可以并置,但数组本身不能并置。但是,假设定义是指针,而不是数组:

char *s = "hello", *t = "hello";

现在很可能 st 包含相同的地址,尽管它们也可能包含不同的地址。 (st 的地址必须不同;它们是两个独立的指针变量)。

但是问题代码中的数组初始值设定项必须初始化两个单独的数组,并且这些单独的数组必须存储在不同的地址,因此问题中的比较 s == t 必须为假,所以没有已打印。