Live data from Hacker News

There is no 'printf'

netmeister.org

111–120 of 158 posts

Re: There is no 'printf'

#111

When you call printf you will actually issue a system call to write to stdout. Does stdlib directly issue system calls or does it use some inline assembly?

What do you feel the difference is between "directly issuing system calls" and "some inline assembly"?

Re: There is no 'printf'

#112
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…

> As far as I understand, there is no “expected behavior of main without a return” under ANSI; it’s undefined behavior

You are right. There are still a lot of so-called developers who cannot understand the concept of _undefined_ behavior.

Re: There is no 'printf'

#113
post #64

Quick Summary: The C compiler optimizer replaces printf("Hello World!\n") with puts("Hello World!\n") and the implicit return from main() changes from 13 (the return value of printf) to 10 (the return value of puts)

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.

> the expected behavior

Which part of "undefined behavior" is not clear to you?

Re: There is no 'printf'

#114
post #18

Earlier quoted context omitted.

Yeah, but I think the question was why EOF and "\n". It could as easily just return 1 or -1 for example, and it would make more sense I think.

puts() always adds a line termination so success means that '\n' is the last char for that implementation.

That's more of an implementation detail, probably a BC remnant from when it called putchar in a loop and you’d get the result if the last putchar.

Re: There is no 'printf'

#115

There is 'printf'. It's just that printf (and the rest of the standard library) is technically as much a part of the C language as the language grammar itself, and C compilers are welcome to use innate knowledge of those functions for optimizations. The other place you typically see this is calls to functions like memcpy/memset being elided to inline vector ops or CISC copies, or on simpler systems, large manual zero…

> C compilers will typically have an escape hatch for envs like deeply embedded systems and kernels like gcc's -ffreestanding and -fno-builtin that says "but for real though, don't assume std lib functions exist or you know what they are based on the function's name". GCC requires standard library functions even on freestanding environments! It will still emit calls to those functions in certain circumstances. https:…

This comment isn't meant to disagree with what you said but is more like a related ramble.

At least in C++, memcpy is special because it can be used to reinterpret bits of one object as another (in C you can use a union, and as of C++20 you can use the std::bit_cast function but it either just calls memcpy or it still needs its own magic)

    double d = 1.0;
    // type punning with union, allowed in C but not C++
    union {
        double d;
        int64_t i;
    } u;
    u.d = d;
    int64_t y1 = u.i; // undefined behaviour
    // cast not allowed, not even reinterpret_cast
    int64_t y2 = *reinterpret_cast(&x.d); // undefined behaviour
    // memcpy - allowed! (if sizeof() params is the same)
    int64_t y3;
    static_assert(sizeof(y3) == sizeof(d));
    memcpy(&y3, &d, sizeof(y3));  // well defined!
    // C++20 bit_cast (probably just a convenience wrapper around memcpy)
    int64_t y4 = std::bit_cast(d);  // well defined
It seems wasteful to create a separate object in memory and copy bits from an old object, when all you want to do is reinterpret the bits of the existing chunk of memory. But memcpy doesn't do that (or at least might not) because it's optimised into a simple cast under the hood, but without the undefined behaviour. So memcpy looks like a standard library function but in a way it's more like a compiler instrinic. It would be possible to create a fully conformant version of memcpy, even in C++, by casting both parameters to char* which is special with respect to the aliasing rules, and copying char-by-char. But I suppose that would be harder to optimise into a simple cast.

Re: There is no 'printf'

#116
post #65

Earlier quoted context omitted.

Regardless of how you think a modern compiler in C99 mode should behave, the fact is that "gcc -std=c90" generates code that returns an arbitrary value from main if no return or exit is executed. clang doesn't do this as far as I can tell -- though it's difficult to be sure, since returning a status of 0 is valid C90 behavior.

I would say that is a bug. More than 22 years ago, ISO C introduced this requirement for a reason. The reason was almost certainly the desire to fix that issue for as many C programs as possible, and that there was no intent that there be an exceptions for C programs that happen to request C90 compatibility in their accompanying Makefile. An indeterminate termination status causes real problems like, oh, if those pro…

But there aren't a lot of good reasons to compile with C90 or older these days (for portability you can just restrict yourself to the portable subset). I suspect the flag is there to support old unmaintained programs and it makes sense not to change their behaviour (because nobody is going to fix bugs in them), as long as it is not a big burden on the compiler. In fact, think of old K&R C instead of C90.

I wouldn't be surprised if there are old unix tools that relied on propagating the result of the last operation executed into main. In fact sometimes gcc has added explicit flags to enable this kind of "traditional" behaviour.

Clang was written well after C99, so it didn't make sense to implement the old traditional behaviour there in the first place.

Re: There is no 'printf'

#117

When you call printf you will actually issue a system call to write to stdout. Does stdlib directly issue system calls or does it use some inline assembly?

What do you feel the difference is between "directly issuing system calls" and "some inline assembly"?

Using some system interrupts vs calling some driver.

Re: There is no 'printf'

#118

> But what if you're not using C99 or newer? If you're using C90, but under an implementation that supports C99, that implementation should obey all the new rules in all areas where there is no conflict between C90 and C99. The ISO C90 standard is obsolescent, so the fact that dropping off the end of main with no return value is an unspecified termination status is an obsolescent requirement (or non-requirement). It…

I don't know how it works for C, but in C++ some (but not all!) fixes are recognized as "defects" in the standard and implementers are encouraged to implement the fixes in previous standard versions too.

Re: There is no 'printf'

#119

My compilers call printf just fine until you enable optimizations. -O0 adds references to printf, -O1 and higher switches to puts. I was kind of surprised about the fact that there was no warning about the missing return from main(). Normally, I'd expect the compiler to complain that a supposed int returning function does not return anything, because that would normally be undefined behaviour.

> I was kind of surprised about the fact that there was no warning about the missing return from main(). Normally, I'd expect the compiler to complain that a supposed int returning function does not return anything, because that would normally be undefined behaviour. If main() returns without a return statement, it is defined to return 0 by the specification. (§5.1.2.2.3 of http://www.open-std.org/jtc1/sc22/wg14/www/…

The article already explains this. What you said is only true in C99 (and they indeed show the return value of the overall program is 0 when compiled in that mode). In C89 it's undefined behaviour, so a warning when you do this would be very sensible.

Re: There is no 'printf'

#120
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