Правильны ли "C пробных тестов" на обучающей программе? - PullRequest
6 голосов
/ 09 июля 2020

Поскольку я кодирую C уже более 20 лет, думаю, мне пора пройти тест! Чтобы узнать, узнал ли я вообще что-нибудь, или я просто публикую бесплатные, но неверные советы новичкам в Inte rnet.

Этот сайт (я не аффилирован) предлагает бесплатные C тестов. https://www.tutorialspoint.com/cprogramming/cprogramming_mock_test.htm.

Я сдал тест 1 и впечатляюще проиграл только 34 из 50! Это все? Должен ли я отказаться от карьеры программиста C? Насколько хорош этот сайт Tutorialspoint и его тесты?

В частности, я не смог ответить на Q7, Q9, Q10, Q14, Q16, Q17, Q19, Q21, Q27, Q28, Q31, Q32, Q33, Q35, Q38, Q46 с ответом, ожидаемым тестом. Каковы правильные ответы на эти вопросы?

Кроме того, когда я компилирую вопросы по моей соответствующей реализации C compiler (gcc -std=c11 -pedantic-errors), ни один из них даже не пройдет компиляцию. Это почему? Я и / или мой компилятор сломаны? Или этот сайт вообще не очень хорош?

Ответы [ 2 ]

13 голосов
/ 09 июля 2020

Этот сайт совсем не очень хороший.

Вопросы написаны для старой версии языка C, который был снят в 1999 году. Это позволяло вам писать main как main() без возвращаемого типа. Это не было действительным C более 20 лет, поэтому оно не компилируется. Вам нужно скомпилировать с -std=c90.

Хотя в старом C90 с неявным int перед main() ОС будет использовать возвращаемое значение функции main (), поэтому в случае отсутствия оператора возврата как в этих примерах это означает неопределенное поведение (C11 6.9.1 / 12).

Примечательно, что весь тест также страдает от отсутствия \n в printf, что означает, что stdout не сбрасывается до завершения программы. C гарантирует, что он будет сброшен при завершении программы.

В частности, эти вопросы также неверны:

  • Q7: Вероятно, ни один из ответов не является правильным. Операнды '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. Это печатает символ '@' в компиляторе g cc x86 для Linux.

  • Q9: Код приводит к ошибке компилятора, поскольку это нарушение ограничения правил простого присваивания ( ссылок ). Вероятно, они хотели написать unsigned x = 5, *y=&x, *p = y+0;, и в этом случае результат не указан - нет гарантии, что выражение *y=&x будет вычислено перед выражением *p = y+0. См. C11 6.7.9 / 23:

    Вычисления выражений списка инициализации неопределенно упорядочены по отношению друг к другу, и, следовательно, порядок, в котором возникают любые побочные эффекты, не указан.

    Таким образом, весь вопрос в корне неверен, как бы вы его не сформулировали.

  • Q10: Может возникнуть множество проблем со стилем относительно того, следует ли приводить результат malloc. Но кроме этого, если присутствует #include <stdlib.h>, код в порядке. В случае, если include отсутствует (как в вопросе), код не работает и может случиться что угодно.

  • Q14: это бесконечное l oop, которое печатает "Hello" бесконечно. Он не печатает «Бесконечное l oop».

  • Q16: См. Q14. Кроме того, приличный компилятор (например, gcc -Wall) мог бы выдать сюда некоторые диагностические сообщения c, поэтому ответ «ошибка компиляции» не обязательно неправильный. Зависит от настроек компилятора.

  • 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 позволяет нам писать short, short int или int short - все равнозначно, поэтому вопрос не имеет особого смысла.

  • Q35: Нет вывода, так как код не компилируется.

  • Q38: На выходе будет 7, а не 6.

  • Q46: Был назначен только член объединения char, остальная часть содержит неопределенные значения. Член объединения x объявляется с автоматическим c сроком хранения и никогда не получает адреса, поэтому доступ к нему является неопределенным. { ссылка }

    Если бы не это, он бы попытался вывести какое-то неопределенное значение («мусор») или даже 65 или 0 в зависимости от порядка байтов процессора.

6 голосов
/ 09 июля 2020

Я разделяю многие оговорки по поводу кода, показанного в пробном тесте №1 для C в TutorialsPoint . Использование кода, недопустимого для C99, не говоря уже о C11 или C17, выглядит странно. Код из прошлого тысячелетия все еще не следует преподавать новым программистам - за исключением наглядных уроков о том, как язык изменился с момента его первой стандартизации. Вопрос SO и основной ответ были изменены, чтобы удалить комментарий к этому единственному вопросу.

Код для Q3:

#include<stdio.h>

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

Массивы s и t должны находиться в разных местах; они представляют собой отдельные массивы, инициализируемые одной и той же строкой, но все же отдельные массивы и, следовательно, хранящиеся по разным адресам. Условное выражение сравнивает адреса, по которым хранятся массивы (для сильного сравнения будет использоваться strcmp() или эквивалент), а массивы хранятся по отдельным адресам, поэтому результат сравнения ложный.

  • Следовательно , единственный правильный ответ: C - Нет вывода.
  • Это ответ, данный TutorialsPoint в их ключе.

Было некоторое обсуждение SO строковых литералов и факт, что они могут храниться в одном месте. Однако это обсуждение было ошибочным; это не относится к этому коду. Строки, используемые для инициализации массивов, могут быть размещены вместе, но сами массивы не могут быть размещены вместе. Однако предположим, что определения были указателями, а не массивами:

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

Теперь вполне возможно, что s и t содержат один и тот же адрес, хотя также возможно, что они содержат разные адреса . (Адреса s и t должны быть разными; это две отдельные переменные-указатели).

Но инициализаторы массива в коде в вопросе должны инициализировать два отдельных массива, и эти отдельные массивы должны храниться по разным адресам, поэтому сравнение s == t в вопросе должно быть ложным, поэтому ничего не печатается.

...