Re: Another spinoza challenge
- From: spinoza1111 <spinoza1111@xxxxxxxxx>
- Date: Mon, 24 Aug 2009 08:58:00 -0700 (PDT)
On Aug 24, 12:52 pm, Richard Heathfield <r...@xxxxxxxxxxxxxxx> wrote:
spinoza1111said:
<snip>
http://irc.essex.ac.uk/www.iota-six.co.uk/c/a5_basic_quiz.asp
(an apparently jejune test) I got 86% (13/15). I do not know how
"good" it is.
Having dug out a browser (and indeed a computer) on which it is
possible to view the test, I am now in a position to comment on it.
How "good" it is depends on what it is supposed to be testing and how
well it achieves that purpose.
The test consists of 15 questions. Before I start in on them, let me
just first point out that, in answering the test as I went along in
order to have subsequent questions revealed, I was able in all cases
to predict which result the test's author believes to be correct, so
my "score" was 15/15. As we shall see, this tells the reader nothing
of significance about my knowledge of C.
In this discussion I am going to discount the questions that have
nothing to do with knowing the language. These are Qs 1, 2, 3, 4, 5,
and 8. This leaves 6, 7, 9, 10, 11, 12, 13, 14, 15.
Question 6 is a True/False question: "C is not case sensitive". This
is a reasonable question, but it is much on the same level as
"Subtraction is commutative (true/false)", and constitutes such a
tiny part of knowledge about C that to attribute it such a high
proportion of the marks seems very strange.
Question 7 asks us to choose one valid comment syntax out of:
/* comment */
// comment
' comment
/* /* comment */ */
<!-- comment -->
Since two of these are (de jure) valid comments, there is no single
correct answer, and yet we were expected to provide one. This
question fails the test of being possible to answer without guessing
the whim of the test author.
Question 9 uses the following core code:
#include <stdio.h>
int main() {
printf("Hello World");
return 0;
}
with minor variations, and asks which variations are "valid". We first
observe that the program produces a text stream on stdout without
terminating the final line. We also note that main is defined and
declared, but not prototyped.
Variation A names the header stdio.c.
Variation B calls print rather than printf.
Variation C has no opening brace.
Variation D is the core code.
This is quite a common technique. Apart from the two nits, I'd say
this is a good question. (Other good typos: colon instead of
semicolon on printf call; absent semicolon on return statement;
single quotes around Hello World.) None of the options is inherently
ludicrous, so someone with no C knowledge has a one in four chance of
getting this right.
Q10: #include is a ??? command:
A: Rootprocessor
B: Preprocessor
C: Foodprocessor
D: Postprocessor
E: Subprocessor
In fact, it's a directive rather than a "command". The third option is
clearly a joke, leaving the educated guesser to choose one from four.
Q11: To use printf you need the ??? file.
A: studio.c
B: stdio.c
C: idiots.h
D: stdio.h
E: studio.h
Since standard headers need not be files, this question again suggests
a certain lack of precision on the part of the questioner. The third
option is again ludicrous, leaving the educated guesser four choices.
Q12: All C programs must have a ???
A: min function
B: man function
C: main function
D: root function
E: goto function
People should be *so* careful with absolute words like "all". Not all
C programs need have a main function. Freestanding implementations
are free to define their own entry point (example: Visual C uses
WinMain as the entry point - or rather, /an/ entry point - for
Windows C programs).
Q13: Which variable names are valid?
This question has already been discussed elsethread. The word "valid"
is ambiguous for _MAX_POWER, so the question is poorly worded. Do we
mean "valid for implementations" or "valid for programs"? Although
this is only one variable name out of ten, failing to guess the mind
of the test author for this one subquestion will lose you the whole
mark.
Question 14: Complete the sentence: 30 is a ??? constant
A: Symbolic
B: Snazzy
C: String
D: Literal
E: Special
Clearly we can ignore B immediately. After a little thought, even
someone who knows nothing about programming in general or C in
particular could eliminate E and perhaps C, leaving the choice
between A and D. (At this point, the non-programmer may well err by
jumping at "Symbolic".) Reasonable question. Shame about B, though.
Q15: Which of the following are data types?
fraction - no
float - yes
double - yes
str - no
char - yes
car - no
string - no
single - no
int - yes
triple - no
Flawed question. We can turn any of the non-native types into a type
using struct and typedef. With a little re-wording, however, it can
be made into a reasonable question.
So the C-related part of the test boils down to:
6) True or false: C is not case sensitive
7) Single-choice-from-multiple with no single correct answer
9) Here are four variants of a short program - which one has no typos?
10) can you remember the word "preprocessor"?
11) which header do we use for printf?
12) no correct answer provided
13) guess the test author's intent
14) what kind of constant is 30?
15) basic numeric data types
Eliminating the completely broken questions, we are left with
6) True or false: C is not case sensitive
9) Here are four variants of a short program - which one has no typos?
10) can you remember the word "preprocessor"?
11) which header do we use for printf?
14) what kind of constant is 30?
15) basic numeric data types
None of these questions requires more than a very very limited
knowledge of C. We deduce, then, that the purpose of the test is to
encourage newbies rather than rate professionals.
If it were my test, I would split out the non-C stuff and put it in
its own test. I would fix the problems mentioned above, and also
throw out all the "cute" fake answers, replacing them with plausible
alternatives. And I'd make the number of questions at least a hundred
- fifteen simply isn't enough, especially when half a dozen are not
related to the subject and another three are badly broken.
Like your sense of humor. Good comments, though.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
This line unintentionally left unblank
....and now for something very different...
Here's a better test. It is an ESSAY test. I answered all questions
without testing anything or looking anything up. This way everybody
can grade everybody else. Should be a clusterf**k.
Questions are prefixed by five asterisks. My responses are unprefixed.
Go to the site, copy the test into a text file, write your answers,
post them and then comment, if you like, on mine (Richard and everyone
else should do this)
http://web.engr.oregonstate.edu/~pancake/cs551/Ctest.html
***** Function parameters
***** Passing parameters by-reference and by-value: What do the two
terms mean?
My reply: When a function parameter is passed by value, its value in
the calling procedure is copied and this is passed. Changes to this
value by the function are "lost" in the sense that the calling
procedure will never "see" those changes. Many C programmers regard
value parameters as their own work areas.
When a function parameter is passed by reference, the value of its Von
Neumann address is passed to the function. Changes to this value are
also lost, but it can be "dereferenced" using asterisk as a prefix
operator to access the calling procedure's copy of the parameter to
change this copy.
***** What is the difference between the way you declare an integer
that is passed by reference and
***** one that is passed by value?
In declaring an integer as passed "by reference", one places an
asterisk between the type and the name, as if to tell a story read
from right to left: for example, in "int *ptr", the "story" is "when
ptr is dereferenced, you will see an int".
***** How does each appear in the statement that invokes your
function?
Value parameters (parameters declared as <type> <name>) are just the
name of a variable, or a numeric or character literal. Reference
parameters (that is, "value" parameters whose "value" is a von Neumann
address) are normally passed as the name of a variable prefixed with
the inverse operator of asterisk, the ampersand, which means "take the
address".
***** Argument coercion: Suppose your routine receives an unsigned
long as its argument, but you
***** need to use it as an integer. How would you coerce it?
If safety is not a concern I would use "coercion" aka "casting" as in
(int)ulngValue. If safety is a concern I would test the coercion like
this:
intValue = (int)ulngValue;
if ((unsigned long)intValue != ulngValue) printf("Error\n");
***** Sending functions as arguments: Suppose your routine receives a
pointer to a function as its
***** first argument, as in the example
***** routine (foo, n);
***** int foo ();
***** int n;
***** { ... code ... }
***** How will the code invoke that function, passing it the integer
value as its single argument?
(*fooInstance)(n); // Don't know if this is right. Have added possibly
redundant parentheses.
***** Declarations and typedefs
***** Typedefs: What is the purpose of a typedef?
To succinctly name a complex type.
***** How would you define a type num_ptr, that is a pointer to an
integer?
typedef num_ptr int *;
***** How would you define a type num_array, that is a pointer to an
array of 100 integers?
typedef num_array int[100] *;
***** How would you define a type complex, that is a structure for a
complex number (containing two
***** part, real and imag, each of which is a double)?
struct complex
{
double realPart;
double imagPart;
} [instance name is optional here if you want to declare an instance
here] ;
***** Forward declarations: Under what circumstances do you need to
make a forward declaration of a
***** function? Any other data type?
You need to do a FD when a function refers to another function in the
same compilation unit, and for some sillyass reason the function
implementation needs to follow the reference. Unlike many decent
languages, C requires the programmer to define all symbols before they
are used; this in itself shows that C is an antique, since memory in
older machines was at a premium. If the text of the function follows
its use, the header of the function must be coded as a semicolon-
terminated C statement before the first use. This is because the
correct parsing of a function use requires knowledge of its
declaration, as in the case of a function with a variable number of
arguments.
***** C declarators: Typedefs and declarations are two instances of C
statements that
***** use "declarators"; typecasts also do. What is x, according to
each of the following
***** declarators?
int *x; a pointer to an integer
int *x(); a pointer to a function that returns an integer
int **x; a pointer to a pointer to an integer
int **x(); a pointer to a pointer to a function that returns an
integer
int *x[10](); a pointer to a function that returns an array of ten
integers
***** (Hint: don't be too sure you know, unless you are aware of the
so-called right-left rule for C ***** declarators.) If you're really
confident, what is
int *(*(*(*x)()) [10]) ();
I don't have the confidence to figure this one out. As code it sucks.
But you know that.
Skipped.
***** This is from Harbison/Steele, p. 83.
***** Structures and unions
***** Structure names: Is it wrong for a to appear twice in this
declaration? Explain
***** struct a {int num1, num2;} a;
No, since the structure name a is prefixed when it is used by
"struct". C knows that "a" is a structure when it is used as a
structure because it must be prefixed by "struct". Of course, it is
very poor practice to do this. Disciplined use of Hungarian notation
(TYP for the structure, USR for a structure instance) avoids this.
***** Padding of structures: Why is the size of the following two
structures different? How would
***** you prove it?
***** struct y {double big;
***** float med; int small;
***** char tiny; char tiny2};
***** struct z {double big;
***** char tiny; float med;
***** char tiny2; int small;};
The sizes are different because in the second, "med" is aligned on a
word (32 bit) boundary, resulting in three extra pad bytes after tiny,
and small is aligned on a halfword binary, resulting in an extra byte
after tiny2.
Prove it by coding and running this:
struct y yInstance;
struct z zInstance;
printf("Struct y len=%d: struct z len=%d\n", &(structyInstance.tiny2)
+ sizeof(char) - &yInstance,&(structzInstance.small) + sizeof(int) -
&yInstance);
***** Unions: What is the difference between the definition for y
above, and this one?
***** union y {double big;
***** float med; int small;
***** char tiny; char tiny2};
big, med, small, tiny and tiny2 all start at the same address in the
union, whereas they follow each other in the corresponding struct
***** How big is the union, in bytes?
It is eight bytes wide
***** Preprocessing features
***** Macros versus functions: Why is a macro preferable to a function
for simple operations?
A macro is preferable because it is replaced by inline code, with no
overhead for a subroutine call to code anywhere else.
***** Under what circumstances is it necessary to use a function
rather than a macro?
In most circumstances: (1) the code to be implemented may be large,
and for this reason it would cost too much storage to use a macro for
inline expansion, (2) the function may need for clarity to be strongly
typed such that the type of parameters should be checked at compile
time: (3) complex functionality is tricky to implement in macros: (4)
complex parameters are tricky to implement in macros and since
evaluation is "by name", unexpected results can often occur
***** Macro names: By convention, how do macro names usually differ
from variable or function names?
Slick ace programmers like me normally code macro names in upper case
for recognition
***** Conditional compilation: How is this specified, and what is it
for?
Conditional compilation uses #ifdef (which is true if the following
symbol is defined false otherwise) and #if (which evaluates #define
symbols according to the C convention: that 0 is false and anything
else is true) along with #else and #endif to support if..then..else
processing. However, as opposed to languages contemporary with C
including IBM assembler and PL/I, C's macro processor was not Turing
complete because it provided no way of coding loops, therefore general
processing at compile time was and is not feasible with C.
Whereas IBM mainframe assembler at the time of C and today provided
assembly time go to, which provided looping and therefore made it
possible to write complete assembly time programs. I used this feature
to generate a table builder for the operating system of an early
(1979) cell phone. IBM shipped its VM/370 operating system in source
code with complex macros to do specific builds at customer sites.
IBM PL.I provided a fully general, and extremely confusing, facility
to do almost anything doable in PL/I at compile time. Princeton had a
lot of legacy administrative code written by brilliant Princeton
students of the 1970s which were impossible for 1980s programmers
apart from your correspondent to maintain because of heavy use of
macro processing.
Most of the facilities of macro processing are handled in OO by
inheritance. The rest are handled by "generic" parameters in OO.
.
- Follow-Ups:
- Re: Another spinoza challenge
- From: *** T. Winter
- Re: Another spinoza challenge
- From: Ben Bacarisse
- Re: Another spinoza challenge
- From: Richard Heathfield
- Re: Another spinoza challenge
- References:
- Another spinoza challenge
- From: spinoza1111
- Re: Another spinoza challenge
- From: Richard Heathfield
- Another spinoza challenge
- Prev by Date: Re: Clarification on type punning.
- Next by Date: Re: Another spinoza challenge
- Previous by thread: Re: Another spinoza challenge
- Next by thread: Re: Another spinoza challenge
- Index(es):
Loading