Live data from Hacker News

Everything I wish I knew when learning C

tmewett.com

371–380 of 401 posts

Re: Everything I wish I knew when learning C

#371

Earlier quoted context omitted.

IIRC this is valid in C99: void foo(size_t n) { int arr[n]; … }

How do you think it works? Does the compiler generate some kind of stack alloc? Stupid question: Does that mean a huge value for 'n' can cause stack overflow at runtime? I recall that threads normally get a fixed size stack size, e.g., 1MB.

Yes, it causes stack overflow at runtime. Compilers warn for it, in particular clang has a warning that you can configure to pop up whenever the stack usage of a function goes beyond some limit you set - I think that setting it to 32k or 64k is a safe and sane default as e.g. macOS thread stack sizes are just 512kb

Re: Everything I wish I knew when learning C

#372

Earlier quoted context omitted.

...Why is the compiler reordering so much? Look. I get it, clever compilers (I guess) make everyone happy, but are absolute garbage for facilitating program understanding. I wonder if we are shooting ourselves in the foot with all this invisible optimization.

People like fast code.

In 2022, is there any other reasons to use C besides "fast code" or "codebase already written in C"?

Re: Everything I wish I knew when learning C

#373
post #367

Earlier quoted context omitted.

They’re not pointers. sizeof a == 4*sizeof(int) , not sizeof(int*) .

They're pointers, just weird ones. The compiler knows it's an array, so it gives the result of the actual amount of space it takes up. If you passed it into a function, and used the sizeof operator in the function, it'd give `sizeof(int *)`. Because sizeof is a compile-time operation, so the compiler still knows that info for your example.

That jest means it decays into a pointer after being passed as a function argument. In the example given however it’s not a pointer. Just like it wouldn’t be inside a struct.

Re: Everything I wish I knew when learning C

#375

Earlier quoted context omitted.

If you truly believe so, then can you give an example of input-conditional UB causing unexpected observable behavior, before the input is actually read? This should be impossible, since otherwise the program would have incorrect behavior if a non-UB-producing input is given.

How this might happen is that one branch of your program may have unconditional undefined behavior, which can be detected at the check itself. This would let a compiler elide the entire branch, even side effects that would typically run.

The compiler can elide the unconditional-UB branch and its side effects, and it can elide the check itself. But it cannot elide the input operation that produces the value which is checked, nor can it elide any side effects before that input operation, unless it can statically prove that no input values can possibly result in the non-UB branch.

Re: Everything I wish I knew when learning C

#376
post #341
post #309

Earlier quoted context omitted.

fopen will/should fail on windows with the unix path syntax. The reason it's indeterminate is because some stdc lib vendors will do path translation on Windows, some won't. I believe cygwin does (because it's by definition a unix-on-windows), but I'm pretty sure the normal stdclib vendors on windows do not. I'm almost positive that MacOS (before MacOS X) will fail with unix path separators, since path separators are…

It will work on Windows, since it inherits the behavior from MS-DOS. It's the shell on Windows (or MS-DOS) where it fails since the shell uses '/' to designate options, so when MS-DOS gained subdirectories (2.0) it used '\' as the file separator on the shell. The "kernel" will accept both. There even used to be an undefined (or underdefined) function in MS-DOS to switch the option character.

Apparently it's true. I wonder when this was implemented?

Canonicalize separators

"All forward slashes (/) are converted into the standard Windows separator, the back slash (\). If they are present, a series of slashes that follow the first two slashes are collapsed into a single slash."

https://learn.microsoft.com/en-us/dotnet/standard/io/file-pa...

Re: Everything I wish I knew when learning C

#377

Earlier quoted context omitted.

> C is often [correctly, but misleadingly] advertised as a cross-platform assembly language, that will compile to the assembly that the author would expect. Because that's what it is. What they don't tell you is that the most heavily-developed two (or more) compilers for it (which you might otherwise assume meant the two best compilers), are malware[0] that actively seek out excuses to inject security vulnerabilities…

Nice way to put down the amazing work of compiler authors. It's not malware you just don't understand how to use it. If you don't want the compilers to do crazy optimisations turn down the optimisation level. If you want then to check for things like null pointers or integer overflow or array bounds at runtime then just turn on the sanitizers those compiler writers kindly provided to you. You just want all of it: fas…

> If you want then to check for things like null pointers or integer overflow or array bounds

I specificly don't want them to check for those things; that is the fucking problem in the first place! When I write:

  x = *p;
I want it compiled to a damn memory access. If I meant:

  x = *p; __builtin_assume_non_null(p);
I'd have damn well written that.

Re: Everything I wish I knew when learning C

#378

Earlier quoted context omitted.

> I wrote: > > Integer division is a slow operation, and under the rules of C, it has no side effects. Yes, you did, and while that's a reasonable approximation in some contexts, it is false in the general case, since division by zero has a side effect in the form of invoking undefined behaviour. (Arguably that means it has every possible side effect, but that's more of a philosophical issue. In practice it has vario…

> it is false in the general case, since division by zero has a side effect in the form of invoking undefined behaviour. They were careful to say “under the rules of C,” the rules define the behaviour of C. On the other hand, undefined behaviour is outside the rules, so I think they’re correct in what they’re saying. The problem for me is that the compiler is not obliged to check that the code is following the rules.…

> They were careful to say "under the rules of C,"

Yes, and under the rules of C, division by zero has a side effect, namely invoking undefined behaviour.

> The problem for me is that the compiler is not obliged to check that the code is following the rules.

That part's actually fine (annoying, but ultimately a reasonable consequence of the "rules the compiler can check" issue); the real(ly bad and insidious) problem is that when the compiler does check that the code is following the rules, it's allowed to do it in deliberately backward way that uses any case of not following the rules as a excuse to break unrelated code.

Re: Everything I wish I knew when learning C

#379

I like it, but the array details are a little bit off. An actual array does have a known size, that's why when given a real array `sizeof` can give the size of the array itself rather than the size of a pointer. There's no particular reason why C doesn't allow you to assign one array to another of the same length, it's largely just an arbitrary restriction. As you noted, it already has to be able to do this when assi…

Another weird property about C arrays is that &arr == arr. The reference of an array is the pointer to the first element, which is what `arr` itself decays to. If arr was a pointer, &arr != arr.

&arr is a pointer to the array. It will happen to point to the same place as the first element, but in fact they have different types, and e.g. (&arr)[0] == arr != arr[0].

Re: Everything I wish I knew when learning C

#380
post #325
post #177

Earlier quoted context omitted.

> There's no particular reason why C doesn't allow you to assign one array to another of the same length, it's largely just an arbitrary restriction. IIRC C has an informal guarantee that no primitive syntax will ever cause the CPU to do more than O(1) work at runtime. Assignment is always O(1), and therefore assignment is limited to scalars. If you need assignment that might do O(N) work, you need to call a stdlib f…

> IIRC C has an informal guarantee that no primitive syntax will ever cause the CPU to do more than O(1) work at runtime. How about CPUs that have no, say, division instruction, so it has to be emulated with a loop?

Fixed-width divisions are O(1), just comparatively expensive (and potentially optimized to run in variable time). Consider that you can do long division on pairs of numerals of, say, up to 20 digits and be Pretty Confident of an upper bound on how long it's going to take you (you know it's not going to take more than 20 rounds), even though it's going to take you longer to do that than it would for you to add them.
Post reply on HN