6 ms·
So: int main() { printf("hello, world!\n"); return 0; } C knew it all along :)
by JackSlateur 3y ago
So:
int main() {
printf("hello, world!\n");
return 0;
}
C knew it all along :)
- Dylan16807 3y agoPrintf is fine until you have to deal with the "f". Some compilers can keep format strings type-safe, but they are going above and beyond the standard to make it happen.
- tristan957 3y agoIf you are using a compiler that doesn't support this, developer tooling like clangd and clang-tidy can help catch these issues too.
- Dylan16807 3y agoToday, yes. But it's relevant for a claim that C had it right "all along".
- LordShredda 3y agoMaybe this should've been something built in if automated tooling can catch it?
- kazinator 3y ago> above and beyond the standard That's a very low bar though. There are widely used languages that don't have a standard at all; literally every single thing you can do in them is above and beyond any sort of standard.
- Dylan16807 3y agoFew of those languages corrupt the stack when you print wrong.
- qalmakka 3y ago... Except the fact that printf/scanf use variadics, and the only reason why it stopped being a constant source of crash is the fact that compilers started recognizing it and validating format strings/complaining when you pass a non-literal string as a format. <format> is instead 100% typesafe. If you pass the wrong stuff it won't compile, and as {fmt} shows you can even validate formats at compile time using just `constexpr` and no compiler support.
- jstimpfle 3y agoAs always, people making more fuss around it than necessary. Code calling printf() with a constant format string literal is this class of code that you have to run a single time to know it works. Many C++ programmers have always been using printf() in preference to e.g. std::cout because of ergonomics. And they were right. It's hard to take people seriously that try to talk it down for being a pragmatic solution that's been around for probably 30-40 years.
- repsilat 3y agoSometimes the print statement is in untested sanity-checking error-case branches that don't have test coverage ("json parsing failed" or whatever). It's pretty annoying when those things throw, and not too uncommon. Another case in C++ is if the value is templated. You don't always get test coverage for all the types, and a compile error is nice if the thing can't be handled. "Type coverage" is pretty useful. Not a huge deal here I agree, but nice nonetheless.
- catskul2 3y ago> is this class of code that you have to run a single time to know it works Not sure what you're trying to say here.
- deleted 3y ago[deleted]
- jstimpfle 3y agoTake as an example printf("%d\n", foo->x);. Assuming it compiled but assuming no further context, what could break here at run-time? foo could be NULL. And the type of foo->x could be not an integer. Let's assume you run the code once and observe that it works. What can you conclude? 1) foo was not NULL at least one time. Unfortunately, we don't know about all the other times. 2) foo->x is indeed an integer and the printf() format is always going to be fine -- it matches the arguments correctly. It's a bit like a delayed type check. A lot of code is like that. Furthermore, a lot of that code -- if the structure is good -- will already have been tested after the program has been started up. Or it can be easily tested during development by running it just once. I'm not saying it's impossible to write a bad printf line and never test it, only to have it fail years later in production. It's absolutely possible and it has happened to me. Lessons learned. I'll even go as far as saying that it's easy to have errors slip on refactors if there aren't good tests in place. But people are writing untyped Python or Javascript programs, sometimes significant ones. Writing in those is like every line was a printf()! But many people will through great troubles to achieve an abstract goal of type safety, accepting pessimisations on other axes even when it is ultimately a bad tradeoff. People also like to bring up issues like this on HN like it's the end of the world, when it's not nearly as big of an issue most of the time. Another similar example like that are void pointers as callback context. It is possible to get it wrong, it absolutely happens. But from a pragmatic and ergonomic standpoint I still prefer them to e.g. abstract classes in a lot of cases due to being a good tradeoff when taking all axes into account.
- Sniffnoy 3y agoI mean, the function for straight printing is puts; I don't know why people keep using the much more complicated printf in cases where no formatting is involved. Edit: OK, I guess puts includes a newline, so you'd need to use fputs if you don't want that (although this example includes one). Still, both of those are much less complicated than printf!
- izoow 3y agoConsistency. Having intermixed puts and printfs throughout the code looks pretty bad. Also, every compiler replaces printf of a literal ending with \n with a puts anyway.
- orra 3y ago> int main() { To be fair to C++, this is undefined behaviour in C until C23. Prior to that the () is interpreted as (...), varargs, and not (void). So, some mutually beneficial cross pollination of ideas.