【问题标题】:Are the "C mock tests" at tutorialspoint correct?教程点的“C 模拟测试”是否正确?
【发布时间】:2020-07-09 13:39:11
【问题描述】:

自从我编写 C 代码 20 多年以来,我认为是时候参加考试了!看看我是否学到了任何东西,或者我只是在网上向初学者发布免费但不正确的建议。

这个网站(我不附属)提供免费的 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) 上编译问题时,它们甚至都无法通过编译。这是为什么?我和/或我的编译器坏了吗?还是这个网站一点都不好?

【问题讨论】:

  • 在我看来,最好至少引用您提出的问题,以便这篇文章是独立的,即使源链接已关闭。
  • @lukeg 也许吧,不过如果我这样做的话,它会变得很长。这主要是为了给一个参加这个测试的不幸的灵魂提供建议。我想我也可以在 SO 上发布它,因为我们经常在这里偶然发现这个教程网站。
  • @lukeg 另外,该测试有版权,所以我不确定在多大程度上我可以在这里引用它的大部分内容。要引用所有错误,我必须引用三分之一的测试。
  • Am I and/or my compiler broken :不要那么糟糕,但肯定是你,每次你开始假设编译器有问题时,改变你的想法,因为在至少 99.99999999999 % 的情况下,这是终于不是编译器了
  • @bruno 确实 - 这肯定不是问题所在。

标签: c c89


【解决方案1】:

这个网站一点都不好。

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

虽然在 main() 之前带有隐式 int 的旧 C90 中,操作系统将使用函数 main() 的返回值,因此如果这些示例中没有 return 语句,这意味着未定义的行为 (C11 6.9. 1/12)。

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

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

  • Q7:没有一个答案可能是正确的。操作数'A'255int 类型,所以加法(假设A=65)保证不会溢出,但结果是65 + 255 = 320。然后通过简单的转换得到int分配给c 的类型,即char。这又可能是有符号或无符号类型,具体取决于编译器。这会影响转换是根据 C11 6.3.1.3/2 定义良好还是根据 6.3.1.3/3 实现定义。一个可能的结果是 320 = 140h,截断:40h = 64。这会在 Linux 的 gcc x86 编译器上打印字符 '@'

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

    初始化列表表达式的求值顺序不确定 相对于彼此,因此任何副作用发生的顺序是 未指定。

    因此,无论你如何提出,整个问题基本上都是错误的。

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

  • Q14:这是一个无限循环打印“Hello”的无限循环。它不打印“无限循环”。

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

  • Q17:假设 2 的补码计算机,则为 -2。理论上,它可以打印 -1 或 -2 或 -(大数),具体取决于计算机使用的是反码、补码还是带符号的量级。

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

  • Q21:假设65'A'的符号表值,那么它可以打印'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 声明为具有自动存储持续时间,并且永远不会占用其地址,因此访问它是未定义的行为。 https://stackoverflow.com/a/40674888/584518

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

【讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • @SamuelLiew 请停止删除有关此答案内容的主题 cmets。删除有关已解决问题的过时 cmets 是可以的,但从站点中删除有价值的内容则不行。有很多东西作为答案发布是没有意义的。
【解决方案2】:

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

此 SO 问题最初讨论了模拟测试的 Q3,但 SO 问题和主要 answer 已被修改,以删除对该问题的评论。

第三季度的代码是:

#include<stdio.h>

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

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

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

关于字符串字面量的 SO 以及它们可以存储在同一位置的事实进行了一些讨论。然而,这种讨论被误导了;它不适用于此代码。用于初始化数组的字符串可以放在一起,但数组本身不能放在一起。但是,假设定义是指针,而不是数组:

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

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

但是问题中代码中的数组初始化器必须初始化两个单独的数组,并且这些单独的数组必须存储在不同的地址,因此问题中的比较s == t必须是假的,所以什么都不会打印。

【讨论】:

  • 你可能是对的,但我不相信。什么说st必须分配,如果是,什么说它们必须分配在不同的地址?
  • 为什么不分配st?因为它们会被优化(从数组更改为指向文字的指针)?
  • 即使允许优化最小代码(到main() { }),编译器也必须将st 视为存储在不同地址的独立数组。在我看来,这是抽象机器 C11 §5.1.2.3 Program execution 行为的“好像”规则的结果。
  • 好的,所以现在我有三个不同的 C 大师不同意我的观点,所以我想我会屈服 :) 但是,我非常确定我已经看到字符串池编译器按所述优化了代码。也许我看到的代码只是使用char* 而不是char[]。我将从我的答案中删除关于 Q3 的部分。
  • @Lundin - 是的,char *s = "blah" 可以与"blah" 的其他用途一起使用字符串池,大概除非您提供了一个编译器选项,它说使字符串可写或显式禁用字符串池。不过,我确实更喜欢const char *s
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-17
  • 2017-02-11
  • 1970-01-01
  • 2021-12-04
  • 2017-03-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多