6

Since I've been coding C for well over 20 years, I think it was high time for me to take a test! To see if I've learnt anything at all, or if I'm just some fraud posting free but incorrect advice to beginners on the Internet.

This site (I'm not affiliated) offers free C tests. https://www.tutorialspoint.com/cprogramming/cprogramming_mock_test.htm.

I took test 1 and failed spectacularly with only 34 out of 50! Is this it? Do I have to give up my C programmer career? How good is this Tutorialspoint site and its tests?

Specifically, I failed to answer Q7, Q9, Q10, Q14, Q16, Q17, Q19, Q21, Q27, Q28, Q31, Q32, Q33, Q35, Q38, Q46 with the answer expected by the test. What are the correct answers to those questions?

Also, when I compile the questions on my conforming implementation C compiler (gcc -std=c11 -pedantic-errors) none of them will even pass compilation. Why is that? Am I and/or my compiler broken? Or is this site not very good at all?

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 7
    In my opinion it'd be good to at least cite the questions you are asking about, so that this post is self-contained and stands even if the source link is down. – lukeg Jul 09 '20 at 13:42
  • @lukeg Perhaps, though it will turn very long if I do. This was mostly written as advise to one unlucky soul who took this test. I thought I might as well publish it on SO too, since we stumble upon this tutorial site quite often around here. – Lundin Jul 09 '20 at 13:45
  • 1
    @lukeg Also, the test has copyright, so I'm not sure at what extent I'd be allow to quote large parts of it here. To quote everything that's wrong, I have to quote a third of the test. – Lundin Jul 09 '20 at 13:47
  • `Am I and/or my compiler broken` : do not get that bad but it is certainly you, each time you start to suppose there is a problem in the compiler just change your mind because in at least 99.99999999999 % of the cases this is finally not the compiler – bruno Jul 09 '20 at 13:49
  • 1
    @bruno Indeed - surely it can't be the question that's broken. – Lundin Jul 09 '20 at 13:50
  • @bruno (On a less sarcastic note, I've probably found some 50:ish confirmed compiler bugs over the years. So the 99.99...% is not quite accurate, particularly not when you work with lots of different embedded systems compilers of diverse quality.) – Lundin Jul 09 '20 at 13:54
  • @Lundin yes there are (very few) bugs in compiler, and yes I exaggerated "a little" ^^ is just about how mush time I see an OP saying the compiler is bugged here on S.O. Anyway "each time you start to suppose there is a problem in the compiler just change your mind" is almost always true ;-) – bruno Jul 09 '20 at 13:57
  • @Lundin We don't know the correct ratio unless we also know how many total bugs you have made (to divide by 50). One estimate suggests that programmers make 1 bug per 10 lines of code written. – stark Jul 09 '20 at 14:03

2 Answers2

14

This site is not very good at all.

The questions are written for an old version of the C language which was withdrawn in the year 1999. It allowed you to write main as main() with no return type. This has not been valid C for over 20 years, so that's why it doesn't compile. You need to compile with -std=c90.

Although in old C90 with implicit int before main(), the OS will use the return value of the function main(), so in case there is no return statement as in these examples, this means undefined behavior (C11 6.9.1/12).

Notably, the whole test also suffers from the lack of \n in printf, which means that stdout is not flushed until the program ends. C guarantees that it is flushed upon program termination.

Specifically, these questions are also incorrect:

  • Q7: None of the answers are likely correct. The operands 'A' and 255 are of type int, so the addition (assuming A=65) is guaranteed not to overflow but to result in 65 + 255 = 320. This resulting int is then converted through simple assignment to the type of c which is char. Which in turn may be a signed or unsigned type, depending on the compiler. This affects if the conversion is well-defined as per C11 6.3.1.3/2 or implementation-defined as per 6.3.1.3/3. One likely result is 320 = 140h, truncated: 40h = 64. This prints the character '@' on the gcc x86 compiler for Linux.

  • Q9: The code results in a compiler error since it's a constraint violation of the rules of simple assignment (references). They probably meant to write unsigned x = 5, *y=&x, *p = y+0; in which case the result is unspecified - there is no guarantee that the expression *y=&x is evaluated before the expression *p = y+0. See 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.

    So the whole question is fundamentally wrong no matter how you put it.

  • Q10: A lot of style concerns might be raised regarding whether or not to cast the result of malloc. But apart from that, assuming #include <stdlib.h> is present, the code is ok. In case the include isn't present (as in the question), the code is broken and anything can happen.

  • Q14: It's an infinite loop that prints "Hello" infinitely. It does not print "Infinite loop".

  • Q16: See Q14. Also, a decent compiler (such as gcc -Wall) might have tossed some diagnostic messages here, so answering "compile error" isn't necessarily wrong. Depends on compiler settings.

  • Q17: Assuming 2's complement computer, then -2. Theoretically, it could print -1 or -2 or -(large number), depending on if the computer uses one's complement, two's complement or signed magnitude.

  • Q19: The correct answer is compiler error. Again because of the constraints for simple assignment.

  • Q21: assuming 65 is the symbol table value for 'A', then it can print either 'A' (little endian) or the symbol corresponding to 0 (big endian). The latter may very well look like "garbage".

  • Q27: The correct answer is invalid use of strcmp function, since #include <string.h> is missing. Otherwise it would print 0.

  • Q28: Compile error. Funny how inconsistent the test is. Here it suddenly doesn't allow implicit conversion from integer to pointers, which it merrily (and incorrectly) allowed earlier.

  • Q31: B or C or even D. It depends on the size of int, which is almost certainly either 2 or 4. The compiler is, however, free to add padding at the end of the union, so it might as well print a larger number.

  • Q32: The correct answer is indeed compiler dependent but... why oh why wasn't it compiler dependent in Q31 then?

  • Q33: C allows us to write either short, short int or int short - all equivalent, so the question doesn't make much sense.

  • Q35: There is no output since the code doesn't compile.

  • Q38: The output is 7, not 6.

  • Q46: Only the char member of the union has been assigned, the rest of it contains indeterminate values. The union member x is declared with automatic storage duration and never has its address taken, so it is undefined behavior to access it. https://stackoverflow.com/a/40674888/584518

    If not for that, it would have attempted to print some indeterminate value ("garbage") or even 65 or 0 depending on CPU endianess.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/217637/discussion-on-answer-by-lundin-are-the-c-mock-tests-at-tutorialspoint-correct). – Samuel Liew Jul 11 '20 at 08:16
7

I share many reservations about the code shown in the mock test #1 for C at TutorialsPoint. Using code that is not valid for C99, let alone C11 or C17, is weird. Code from the last millennium should not still be being taught to new programmers — except as object lessons in how the language has changed since it was first standardized.

This SO question originally discussed Q3 of the mock test, but the SO question and primary answer have since been amended to remove the commentary on that one question.

The code for Q3 is:

#include<stdio.h>

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

The arrays s and t must be at different locations; they are separate arrays, initialized by the same string, but still separate arrays and hence stored at separate addresses. The conditional compares the addresses at which the arrays are stored (string comparison would use strcmp() or equivalent), and the arrays are stored at separate addresses so the result of the comparison is false.

  • Consequently, the only correct answer is C — No Output.
  • This is the answer given by TutorialsPoint in their key.

There was some discussion on SO of string literals and the fact that they can be stored in the same location. However, that discussion was misguided; it doesn't apply to this code. The strings used to initialize the arrays may be colocated, but the arrays themselves cannot be colocated. However, suppose that the definitions were of pointers, not arrays:

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

Now it is quite possible that s and t contain the same address, though it is also possible that they contain different addresses. (The addresses of s and t must be different; they're two separate pointer variables).

But the array initializers in the code in the question must initialize two separate arrays, and those separate arrays must be stored at separate addresses, and hence the comparison s == t in the question must be false, so nothing is printed.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • You might be correct, but I'm not convinced. What says that `s` and `t` must be allocated, and if they are, what says that they must be allocated at different addresses? – Lundin Jul 09 '20 at 14:25
  • 1
    Why wouldn't `s` and `t` be allocated? Because they'd be optimized out (changed from an array to a pointer to a literal)? – Fiddling Bits Jul 09 '20 at 14:27
  • Even granting that the minimal code could be optimized (to `main() { }`), the compiler has to treat `s` and `t` as separate arrays stored at separate addresses. That, it seems to me, is a consequence of the 'as if' rule for the behaviour of the abstract machine [C11 §5.1.2.3 Program execution](http://port70.net/~nsz/c/c11/n1570.html#5.1.2.3). – Jonathan Leffler Jul 09 '20 at 14:35
  • Ok so now I have three different C gurus disagreeing with me, so I suppose I'll yield :) However, I'm pretty darn sure I have seen string pooling compilers optimize the code as described. Maybe the code where I saw it was just using `char*` and not `char[]` though. I'll remove that part about Q3 from my answer. – Lundin Jul 09 '20 at 14:44
  • 1
    @Lundin - yes, `char *s = "blah"` can be string pooled with other uses of `"blah"`, presumably unless you've provided a compiler option that says to make strings writable or to explicitly disable string pooling. I do prefer `const char *s` for this though. – Steve Friedl Jul 09 '20 at 14:52