5 ms·
hello.c is meant as the first program a new student encounters when learning C. At that point, the student has enough to worry about; writing code to a file, ch
by usrbinbash 5y ago
hello.c is meant as the first program a new student encounters when learning C. At that point, the student has enough to worry about; writing code to a file, checking for syntactic errors, basic program structure, using the compiler, executing the binary, understanding what `#include <stdio.h>` means,...
Sure, we could update it:
// hello_v2.0.c
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
printf("Hello, World!\n");
fflush(stdout);
if (errno != 0) {
fprintf(stderr, "error: %s\n", strerror(errno));
return errno;
}
}
But now we have different libraries, a multitude of external identifiers, control structures, blocks, return values, the concept of buffered streams, the concept of file-descriptors, the printf formatting language, program return values, boolean logic & conditionals,...
To someone who is already experienced in another language, that may not seem like a big deal, and isn't, but to someone who encounters the language for the first time, this is heavy stuff.
- nextaccountic 5y agoThis just demonstrates that C is an awful programming language for writing correct programs. Compare this with Rust, where the usual hello world will just do the right thing: $ cat > a.rs fn main() { println!("Hello World"); } $ rustc a.rs $ ./a Hello World $ echo $? 0 $ ./a > /dev/full thread 'main' panicked at 'failed printing to stdout: No space left on device (os error 28)', library/std/src/io/stdio.rs:1187:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace $ echo $? 101
- dralley 5y agoOr Zig which tries to make it easy to actually handle those errors.
- layer8 5y agoThis is much simpler: int n = printf("Hello, World!\n"); return n < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
- usrbinbash 5y agoOn my machine: $ cat main.c #include <stdio.h> int main(void) { int n = printf("Hello, World!\n"); return n < 0 ? 1 : 0; } $ cc main.c $ a.out Hello, World! $ echo $? 0 $ a.out > /dev/full $ echo $? 0 Problem is, printf is buffered. When redirecting to /dev/full writing to the buffer works. The problem only manifests when the buffer is flushed. This works, as write is not buffered; #include <unistd.h> int main(void) { int n = write(1, "Hello, World!\n", 14); return n < 0 ? 1 : 0; } But even then, to find out what exactly went wrong, we would have to check errno. It's easier in Go, because fmt functions do have an error return which will report such errors; func main() { _, err := fmt.Println("Hello, World!") if err != nil { os.Exit(1) } }
- layer8 5y agoI was expecting that printf would auto-flush at the newline, but either it doesn’t, or doesn’t report an error? An explicit fflush would probably work. In any case, hello world examples are used for two purposes, (a) providing a simplest possible program that you can run (with an indication that it worked), and (b) to give a taste of the respective programming language. At least for the latter case, it would be useful to demonstrate the necessary error handling, if any. The above is just showing that this is not entirely trivial in C with its standard library.
- Erwin 5y agoThe file objects in the library can operate as unbuffered, line-buffered or fully (i.e. block) buffered. When your stdout is connected to a terminal, it is line-buffered and flushes on newline. But when you redirect to a file, it becomes block buffered.
- 5y ago
- paavoova 5y agoYour usage of errno can cause failure on success if errno was set any time before the call to fflush. Might need a bump to v3 for robustness...
- gpm 5y agoAccording to cppreference > The value of errno is 0 at program startup, and although library functions are allowed to write positive integers to errno whether or not an error occurred, library functions never store 0 in errno. https://en.cppreference.com/w/c/error/errno https://en.cppreference.com/w/c/error/errno So this program correctly tests whether printf or fflush wrote to errno. You just can't refactor it into a function you call not at startup... I suppose that is unless you're keeping to a convention where you always set errno back to 0 after any call that might have set it...