Difficulty: ★ ★ ★ ★
Loops are fun. They’re easy to learn. But not everything that repeats in a C program is constructed as a loop.
Consider the following output, my solution to this month’s Exercise:
10
9
8
7
6
5
4
3
2
1
Yes, this output isn’t that complicated: Values from 10 to 1 appear — a countdown. But there’s a catch.
You must generate this output without using any looping statements, including goto. Further, the program must have only one function, main(). Use the printf() function to output the values. Can you do it?
Oh, and using printf() to output values 10 through 1 directly isn’t a valid solution.
Click here to view my insane solution.
I suspect this is officially a Very Bad Thing to Do but it doesn’t seem to do anything catastrophic.
#include <stdio.h>
#include <math.h>
int n = 10;
int main()
{
if(n>=1)
{
printf(“%d\n”, n–);
main();
}
else
{
return 0;
}
}
Not sure how math.h got there. It’s not used obviously.
This works, despite being completely stupid on many levels.
int a = 1;
printf(“%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n”, a, a++, a++, a++, a++, a++, a++, a++, a++, a++);
Starting at 10 and minusing in printf outputs the numbers in ascending order so the expression is obviously parsed and evaluated right-to-left. Strange.
Your first solution is more along the lines of what I had in mind. The second one is also very clever. Thank you!
While calling main() from with main() is not allowed in C++ (§ 3.6.1. The function main shall not be used within a program), this—luckily—is a blog about ‘C’ where such shenanigans are allowed… as noted in the GCC documentation:
“You can call main from C code, as you can call any other function, though that is an unusual thing to do.”
Anyway, since I wanted to give users the option to specify a starting value for the output (and at least perform some error handling), I came up with the following:
#include <stdio.h> /* printf() */
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE */
#include <iso646.h> /* not, and, or, xor, … */
int main (int argc, char *argv [])
{ int /* argv valid? try parsing 1ˢᵗ argument (with a fallback to 10) */
counter = argv? (argc == 2? atoi(argv [1]) : 10) : argc;
if (counter <= 0 || not printf ("%d\n", counter))
return (EXIT_FAILURE);
return ((counter > 1)? main (counter-1, NULL) : EXIT_SUCCESS);
}
Admittedly, the added error handling code somewhat obscures the logic of the program…
Still, it’s pretty cool!
Thank you, I tried my best—that being said, I am already curious to see what you came up with!
Sorry to clutter up the comments with something irrelevant but as I mentioned I discovered in my solution that printf evaluates expressions right-to-left which I thought was strange. I put together a bit of code to demo and confirm this.
#include <stdio.h>
int first(void);
int second(void);
int third(void);
int main()
{
printf(“%d\n%d\n%d\n”, first(), second(), third());
}
int first(void)
{
puts(“I am the first function”);
return 1;
}
int second(void)
{
puts(“I am the second function”);
return 2;
}
int third(void)
{
puts(“I am the third function”);
return 3;
}
The output is:
I am the third function
I am the second function
I am the first function
1
2
3
which confirms the rtl. Most of the time it wouldn’t matter and nobody would even notice. I’d consider calculations, even just a ++ or –, or calling functions within printf to be bad practice anyway. Is there a specific reason and are there any circumstances when it’s advantageous? Has it ever caused anyone an annoying and hard to spot bug?
(btw I’m using GCC on Linux Mint Ubuntu version in case it varies across compilers & OSs.)
@Chris Webb
I thought I might as well try to provide an explanation for the observed right-to-left behavior of function arguments. While the order of evaluation for function arguments is—as the C standard notes—unspecified⁽¹⁾, itʼs not really surprising that (most) compilers on x86 would choose a right-to-left evaluation order.
Thatʼs because the stack, unto which these arguments are pushed before a function call, grows downwards on x86 (i.e. the value in the
RSP
stack pointer register is decremented on everyPUSH
).When the
__cdecl
calling convention is in effect, the compiler will (regardless of evaluation order) thereforePUSH
all argument values right-to-left so that they will be available with an increasing offset—relative to RSP—upon entering the function body.For example, letʼs say the following function is given:
int __cdecl min (int a, int b) { ⋯ }
⋮
min (value1, value2);
The C compiler will most likely emit the following sequence of assembly language instructions when this min() function is called:
PUSH dword [value2]
PUSH dword [value2]
CALL min
Within ‘min:’, the passed arguments can then be accessed as follows:
min: ; (note that [RSP + 0] contains the return address.)
mov EAX, [RSP + 8] ; value1
cmp EAX, [RSP + 12] ; value 2
⋮
By
PUSH
-ing them in the wrong order, they are now in the “right” one when accessed within the functions body.Of course, there is nothing really saying that it has to be this way. Interestingly, this is one of the differences between
__cdecl
(Cʼs calling convention) and__pascal
(PASCALʼs calling convention): in PASCAL, the caller is supposed toPUSH
the arguments onto the stack in left-to-right order.Hope this helps to clarify⁽²⁾ the above behavior!
⁽¹⁾ C99 §6.5.2.2p10: “The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.”
⁽²⁾ Further details can be found in Agner Fog’s excellent Assembly Language resources, especially his calling_conventions.pdf.
Thank you. I assumed there was a rational explanation.
Glad to be of help!
I donʼt do so much nowadays, but there was a time in my life where I wrote quite a bit of assembly language code. Itʼs not too often really, but every once in a while this knowledge comes in quite handy.