Live data from Hacker News

There is no 'printf'

netmeister.org

101–110 of 158 posts

Re: There is no 'printf'

#101
Tldr:

Not returning a value from main() is undefined behavior in ANSI C. So compiler will do whatever it likes. It will return 0 or 42 or crash. In this case, gcc just replaces printf with something else it likes more.

Re: There is no 'printf'

#102
post #72

Earlier quoted context omitted.

Hmm you're right that there's definitely more to the first function than coincidence. I dug a bit deeper and surprisingly when n is unsigned the function is still well defined and correct. [0] AFAIK clang will usually just treat a signed overflow as an unsigned overflow when optimizing as it simplifies things. So my bet would be that clang is just casting n to unsigned and performing some inductive reasoning similar…

> AFAIK clang will usually just treat a signed overflow as an unsigned overflow when optimizing as it simplifies things. This was a bit of a surprise to me; I thought that Clang could use the presence of signed overflow to infer bounds, like when it "breaks" naïve overflow checks. I didn't know about the treat-signed-as-unsigned behavior you described. If that's what clang is doing, that's pretty neat! Quite a bit of…

Treat-signed-as-unsigned is what you get "by default" with a two's complement representation of integers, if you just use ADD/SUB opcodes regardless of the type (i.e. the cheapest/fastest way to do it).

Re: There is no 'printf'

#103

"Pop quiz! What will the following program return? int main() { printf("Hello World!\n"); } Easy, right? It's gotta be 0, because since at least ISO/IEC 9899:1999 (aka "C99"), main shall implicitly return 0 if you, the forgetful programmer, didn't bother to explicitly return a value:" As hobbyist programer I write small programs and run them on older computers, running a variety of OS. Among other things, I use -std=…

    "Pop quiz! What will the following program return?
       int main() {
           printf("Hello World!\n");
       }
In C99 or later, it produces a mandatory diagnostic because `printf` is not declared.

In C90, it has undefined behavior because a variadic function is called with no visible prototype.

Re: There is no 'printf'

#104
post #95

Earlier quoted context omitted.

> There is no good reason for a C99 implementation behaving in a C90 compatibility mode to simply drop non-conflicting C99 requirements and revert a behavior such as this. A compatibility with a code which depends on a behaviour of a previous version of the compiler is a reason enough. If compatibility is dropped you now have a very subtle bug which is hard to discover and understand unless you read about that partic…

There's no compatibility requirement here, because the old behavior is UB (both in theory and in practice).

Apparently the compiler writers thought there was, and the compiler writers get to define semantics above and beyond what's in the C spec.

Re: There is no 'printf'

#105

Earlier quoted context omitted.

Not that odd if you know the evolution of the language... and what it actually means for modern programmers. The very first C compilers ran on a PDP-11 in just a few dozen kilobytes of memory. The entire emphasis was on minimalism, and that meant that things like type enforcement was left to the human. One of the things the earliest language was missing was the "void" type. If you didn't give a return type to a funct…

OK, but this doesn’t do wonders as a counterargument that C is just a big pile of historical baggage.

But of course it is; did you expect anything else from a language that's almost 50 years old by now? It's not any different from, say, Common Lisp in that regard.

This particular quirk is a few years older than C itself, actually! C was based on a similar language called B. Now B was originally written for PDP-7 and similar machines, which could only address words in memory, not individual bytes. So it was designed "typeless" - the only and always-implicit type in that language is the machine word, so e.g. (a+b) always adds two ints, and (*a) always dereferences a pointer. Otherwise, the syntax was very similar to C:

   max(a, b) {
      if (a > b)
         return a;
      else
         return b;
   }

   swap(a, b) { 
      auto c = *a;
      *a = *b;
      *b = c;
   }

   main() {
      auto a, b;
      swap(&a, &b);
   }
Since all functions took zero or more words as arguments, and returned a word, there was no need for function prototypes, either, or for function pointer types - if you used () on something, it was treated as a function / function pointer with the corresponding number of parameters. Even labels / goto worked like that.

When they got a PDP-11, which had byte-addressed memory, this simplistic approach no longer worked - they had to distinguish char/int and char*/int*. So C added types and pointers - but kept int as the default type, as well as the keyword "auto" (which is completely redundant in C if you specify the type), so that existing B code could be easily ported. For the same reason, they allowed calling functions without declaring them, assuming int as return type.

This is also presumably why pre-ANSI K&R C had that weird syntax for parameter types:

   max(a, b)
      int a;
      int b;
   { ... }
If you omit the types, they all default to ints, and it becomes identical to the B declaration!

That implicit int rule made it into the ANSI C89 / ISO C90 standard, since it was still common enough in K&R C code floating around at the time, and they were trying to not break things too much. It eventually got removed in C99, except that "short" and "long" still allow to omit the following "int". The useless "auto" is still around, though, although it might eventually get repurposed the same way it was in C++.

Re: There is no 'printf'

#106

Earlier quoted context omitted.

Do you also expect `sqrt(a)` to be `call sqrt` and not `sqrtss xmm0`?

> Do you also expect `sqrt(a)` to be `call sqrt` and not `sqrtss xmm0`? Of course. Who else is going to set errno when I pass negative a number?

Ok, so make the question one of `call sqrt` vs `sqrtss xmm0` + a test that sets errno in case of negative then?

Or even simpler: do you demand that `x = sqrt(4)` entails emitting code that calls `sqrt`?

Re: There is no 'printf'

#107

Earlier quoted context omitted.

Do you also expect `sqrt(a)` to be `call sqrt` and not `sqrtss xmm0`?

> Do you also expect `sqrt(a)` to be `call sqrt` and not `sqrtss xmm0`? Of course. Who else is going to set errno when I pass negative a number?

Nobody has to, if you pass it an unsigned int. Would you still expect it to be a function call then?

Re: There is no 'printf'

#108
post #79

Earlier quoted context omitted.

Why is that a problem? (That's a serious question. I'm not suggesting that it isn't a problem. Apparently it is for you.) If I write printf("Hello, world\n"), all I care about is that those characters are written to the standard output stream when I run the program -- and that's all the language standard specifies. I rarely even look at the assembly or machine code. As someone else mentioned, "gcc -fno-builtin" inhib…

Because it's surprising and breaks our expectations. The author of this article clearly expected a call to printf to be present in the generated code. > all I care about is that those characters are written to the standard output stream when I run the program Sometimes people care about a lot more. Such as the ability to hook into a specific function or ensuring the compiler doesn't generate calls to certain function…

That's exactly why we have specifications and standards - so that you don't have to guess and build mental models on flimsy assumptions, and know exactly what to expect.

Re: There is no 'printf'

#109
post #76
post #67

Earlier quoted context omitted.

The compiler cannot assume that much, because the argument is a signed integer (negative integers will not overflow and do have well-defined behaviour).

The rabbit hole goes deeper than that: https://gcc.godbolt.org/z/Tc1MTa6nj

That is a well defined function. And indeed implements isEven. Because unsigned int has defined overflow semantics.

Essentially, it will eventually overflow and hit the correct base-case for 0.

Re: There is no 'printf'

#110
post #64

Earlier quoted context omitted.

This sounds like a compiler optimizing bug, in that if the expected behavior of main without a return is that it will return the result of the last function call, then the last function in the main does use its return value and that should not be optimized.

As far as I understand, there is no “expected behavior of main without a return” under ANSI; it’s undefined behavior as (according to this version of the C standard) a function with an int return type must return an int. The fact that it happens to return the result of last function call is more-or-less a coincidence because the compiler doesn’t bother to clear out or reuse the return value register. If this seems un…

[deleted]
Post reply on HN