Live data from Hacker News

Main is usually a function – when is it not?

jroweboy.github.io

51–60 of 62 posts

Re: Main is usually a function – when is it not?

#51

I'm impressed with the authors efforts, and share much of his pain when it comes to doing weird things with C. For other obfuscated fun, see this poem/tree printer in 505 bytes[0], or this program: main(G){10 >5*G):10)&&main(2+G);} [0] - https://github.com/lelandbatey/tiny_tree_printer

OK, your one-liner is simple enough that maybe it's worth a few minutes to unpack it here.

Let's begin with that big constant. The values of

  89-(0x1F&2846984999544829581>>5*G)
for successive choices of G are: 76 69 76 65 78 68 66 65 84 69 89 74 87. "Obviously" these are character indices; the characters are L,E,L,A,N,D,B,A,T,E,Y,J,W. (I remark that Leland was the first name of the founder of Stanford University and conjecture that the author of the code works at, or studied at, Stanford. And then I look up at the username of the person who posted it and feel a bit stupid.)

Now, what's the argument to putchar? It has the form

  11^--G ? thing1 : thing2
so it decrements G, and then if the result is anything other than 11 it evaluates thing1; but if the result is 11 it evaluates thing2 instead. In this case, thing1 is our LELANDBATEY generator and thing2 is 10 (= newline).

So far, so good. One more step out. What comes before the && inside main? It looks like this:

  10
and the return value from putchar is (barring I/O failures) the same as what's fed into it. So, putting the above together, when G isn't 12 we'll decrement G, write out a character from LELANDBATEYJW (indexed by G), and this thing will be true; when G is 12 we'll decrement G, write out a newline, and this thing will be false.

Nearly there. Now the whole thing looks like

  main(G) { thing_above && main(2+G); }
so when G isn't 12 it will decrement G, write out a character from LELANDBATEY, and immediately call main with G+2 (which is 1 more than G started out as); but if G is 12 on entry to main it will decrement G, write out a newline, and then return (perhaps to a previous stack frame which will also return, and so on until exit).

OK. So, I guess this program is intended to be invoked with no command-line args. Then G (= argc) will be 1. We'll write out characters 0, 1, 2, ..., 10 of the LELANDBATEY string (getting exactly that far) at which point G will be 12 on entry to the next main call. Newline, exit. Done.

(In case it isn't clear: it outputs "LELANDBATEY\n".)

No offence, I hope, but this is really tame compared with typical short-short IOCCC entries. Here's an example from the 22nd IOCCC. It's short enough to tweet.

  char a;float b,c;main(d){for(;d>2e3*c?c=1,scanf(" %c%f",&a,&c),d=55-a%32*9/5,b=d>9,d=d%13-a/32*12:1;a=2)++d
See http://www.ioccc.org/2013/endoh3/hint.html for more about this; it interprets tunes written in a simple musical notation and generates raw audio data.

Re: Main is usually a function – when is it not?

#53
post #43
post #3

I wonder if one could write a header file containing definitions that would make the following possible. char main[] = { movl(1, eax), movl(1, ebx), movl(message, esi), movl(13, edx), syscall(), movl(60, eax), xorl(ebx, ebx), syscall(), }; Obviously there are some technical difficulties like handling literal values and code sections, but it could be a fun hack, and I've love to see what someone could come up with.

With enough macros you can do anything. I'm not aware of anyone doing this in C, but this is a cool example of doing approximately the same thing in a more easily extensible language: http://wall.org/~lewis/2013/10/15/asm-monad.html

Being able to compile assembly directly into a C program is a very useful tool for allowing optimizations across embedded assembly. One way around it is use of intrinsics: http://en.wikipedia.org/wiki/Intrinsic_function which is basically what we're talking about.

Re: Main is usually a function – when is it not?

#54
post #45

Earlier quoted context omitted.

If that is the case then the education system is thoroughly broken. It is obvious that a person who took the time to do that learned far more than they would than had they copied one of millions of Hello World examples from the internet. That being said, I wouldn't rule out a bored TA opening the file not seeing a printf and assigning a grade based on submitting something.

Not at all; TAs are paid poorly, have other more interesting work (i.e., their research) to do, and spending the time to deal with 'clever' is not a good usage of time. Sure the student learnt more, but that doesn't mean this is what they should have handed in. Have fun on your own time, but don't waste the TAs time.

We might have to agree to disagree. If TAs aren't interested in students diving deeper into the course material then I'm not sure they should be paid at all.

But perhaps the less radical approach would be to pay them enough such that they can take the time to judge the work on it's merit.

Re: Main is usually a function – when is it not?

#55
post #51

I'm impressed with the authors efforts, and share much of his pain when it comes to doing weird things with C. For other obfuscated fun, see this poem/tree printer in 505 bytes[0], or this program: main(G){10 >5*G):10)&&main(2+G);} [0] - https://github.com/lelandbatey/tiny_tree_printer

OK, your one-liner is simple enough that maybe it's worth a few minutes to unpack it here. Let's begin with that big constant. The values of 89-(0x1F&2846984999544829581>>5*G) for successive choices of G are: 76 69 76 65 78 68 66 65 84 69 89 74 87. "Obviously" these are character indices; the characters are L,E,L,A,N,D,B,A,T,E,Y,J,W. (I remark that Leland was the first name of the founder of Stanford University and c…

The one-liner I posted is really meant to be just understandable enough to someone who's taken a solid first year computer science course with C and is looking for more. At least that's where I was approximately when I first found it. So yes, it's mean to be tame, more of a brainteaser than indecipherable.

Also, I wrote a program to generate these. So here's one for you:

    main(O){10>5*O):10)&&main(2+O);}
In fact, I originally found this style of tiny C program on Hacker News nearly two years ago. Here's the link to the original: https://news.ycombinator.com/item?id=5524428

Re: Main is usually a function – when is it not?

#57
post #49

I remember something similar that enabled you to embed machine code in VB6 programs, which involved a magic constant that was sort of an entry point into a string containing opcodes. I don't remember the specifics, it's been a while, but nowadays it's very likely a horrible no-go anyway because you're executing data. Still, was a nifty trick that allowed you to get more performance out of a language that wasn't very…

I remember QuickBasic (the precursor of VB: VB 1.0 was effectively QB 8.0) had a CALL ABSOLUTE statement, which let you execute machine code in a string. I used it to great effect in graphics demos, once upon a time.

Re: Main is usually a function – when is it not?

#58
post #10

> My problem solving process is typically the same thing I imagine most programmers do. I'm afraid I may be a victim of Poe's law here, but this attitude is... distressing. It completely discounts the value of problem solving, which, once upon a time, was the entire point of the profession.

It does not. It just moves what "the problem" is. An example of that is the article itself which leverages the gained information from SO to solve the real problem. Also arcane knowledge is not the same thing as problem solving.

> If not solved, try a different query and repeat.

I'm not sure if you even bothered to read the article... since that was the very next sentence. I, too, wanted to give the author the benefit of the doubt and assume it was mere information gathering, which is a good thing, but the next sentence shows that the intent is to solve the problem from start to finish with Google.

Re: Main is usually a function – when is it not?

#59
post #45

Earlier quoted context omitted.

Not at all; TAs are paid poorly, have other more interesting work (i.e., their research) to do, and spending the time to deal with 'clever' is not a good usage of time. Sure the student learnt more, but that doesn't mean this is what they should have handed in. Have fun on your own time, but don't waste the TAs time.

We might have to agree to disagree. If TAs aren't interested in students diving deeper into the course material then I'm not sure they should be paid at all. But perhaps the less radical approach would be to pay them enough such that they can take the time to judge the work on it's merit.

>But perhaps the less radical approach would be to pay them enough such that they can take the time to judge the work on it's merit.

I assume you have not TAed: I'd like to agree with you, but the realities are it is poor pay for crappy work. Most students don't care about their work. Most students seem to be unable to follow basic instructions. I go out of my way to help students if they care and stay late at labs for these students, but I have no patience for wasting my time because someone wants to show off and deliberately make my life harder. As is, when I'm being paid for 6hrs of lab + marking/week, and I am already spending 8+hrs a week because students can't format their assignments properly, don't write in complete sentences, etc, I have zero interest in patting this guy on the back. Instead, I have to do more work for no pay so he can feel clever.

Re: Main is usually a function – when is it not?

#60
post #11

Earlier quoted context omitted.

It's possible, but once you're not obfuscating the code - and already depending on OS details when you assume that global data can be executable, whether const or not - I think you may as well just use the inline assembler feature of your compiler. Well, if there is one... MSVC bizarrely doesn't support on x86-64 what was a perfectly good feature on x86.

It's not bizarre: inline asm hurts performance and requires more effort from compiler authors than is worth it in our modern age of intrinsics.

I admit I'm not too familiar with the old MSVC inline assembly system, but the way GCC/Clang do it certainly allows emitting identical code to what you can get with intrinsics, although using naive constraints might hurt you. However, the main reason I personally use inline asm is not for performance, but to access instructions which are not provided as intrinsics or require special register handling. For example, I recently wrote some code that did syscalls directly (because it was patching memory so the normal syscall functions might not be accessible); I could have linked a separate .S file, but inline assembly made the output look much nicer. Or, while I haven't written this myself, it can be used to write out nops which zero-cost tracing facilities will then patch, something which separate assembly files cannot do.

As for effort from compiler authors, I'd like to hear more about why it is supposedly so hard. (MSVC is also the compiler which has to prioritize their choice of C++11-17 features to implement over the years, while the competitors have complete C++11 and 14 implementations. I guess that's somewhat ad hominem on the team, but ...)

Post reply on HN