Live data from Hacker News

Everything I wish I knew when learning C

tmewett.com

391–400 of 401 posts

Re: Everything I wish I knew when learning C

#391

I was born in '74 so the last generation to start with C and go to other, higher-level, languages like Python or JavaScript. Going in this direction was natural. I was amazed by all the magic the higher-level languages offered. Going the other direction is a bit more difficult apparently. "What do you mean it does not do that?". Interesting perspective indeed!

This was my experience with learning programming as well, however, I am 2x younger :)

Re: Everything I wish I knew when learning C

#392

Earlier quoted context omitted.

Pointers are by far the most insidious thing about C. The problem is that nobody who groks pointers can understand why they had trouble understanding them in the first place. Once you understand, it seems so obvious that you cannot imagine not understanding what a pointer is, but at the beginning, trying to figure out why the compiler won't let you assign a pointer to an array, like `char str[256]; str = "asdf"`, is…

From my memory, the syntax of pointers really tripped me up. E.g., the difference between * and & in declaration vs dereferencing. I think this is especially confusing for beginners when you add array declarations to the mix.

I don't remember C using & in declarations. Is it a recent addition?

Re: Everything I wish I knew when learning C

#393

Earlier quoted context omitted.

Another one corner of the language where arrays actually being arrays is important is multidimensional array access: int arr[5][7]; arr[3][5] = 4; // equivalent to *(*(arr + 3) + 5) = 4; This works because (arr + 3) has type "pointer to int[7]", not "pointer to int". The resulting address computation is (char*)arr + 3 * sizeof(int[7]) + 5 * sizeof(int) == (char*)arr + 26 * sizeof(int) That's also another reason why t…

Really, are multidimensional arrays an important part of the language? The above code looks like it's indexing into an array of pointers. If you want a flat array, make a few inlined helper functions that do the multiplying and adding. Your code will be much cleaner and easier to understand.

The code above does not mention any pointer types, so why would you assume that it's indexing into an array of pointers?

I can't think of any reason why get(a, i, j) is more readable than a[i][j].

Re: Everything I wish I knew when learning C

#394

Earlier quoted context omitted.

Perhaps I am missing something in the spec - but trying this in various compilers, it seems that you *can* assign structs holding arrays to one another, but you *cannot* assign arrays themselves. This compiles: struct BigStruct { int my_array[4]; }; int main() { struct BigStruct a; struct BigStruct b; b = a; } But this does not: int main() { int a[4]; int b[4]; b = a; } That seems like an arbitrary restriction to me.

Essentially ‘b = a’ in the second example is equivalent to ‘b = &a[0]’ or assigning an array to a pointer. This is because if you use an array in an expression, it’s value is (most of the time) a pointer to the array’s first element. But the left element is not an expression, therefore it is referring to b the array. Example one works because no arrays are referred to in the expression side, so this shorthand so to s…

The left side of assignment in C is an expression. it's just not in a context where array-to-pointer decay is triggered.

Re: Everything I wish I knew when learning C

#395

Earlier quoted context omitted.

People like fast code.

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

No, and, in fact, the first one isn't valid - you can use C++ (or a subset of it) for the same performance profile with less footguns.

So really the only time to use C is when the codebase already has it and there is a policy to stick to it even for new code, or when targeting a platform that simply doesn't have a C++ toolchain for it, which is unfortunately not uncommon in embedded.

Re: Everything I wish I knew when learning C

#396
post #98

When I first learned C - which also was my first contact with programming at all - I did not understand how pointers work, and the book I was using was not helpful at all in this department. I only "got" pointers like three or four years later, fortunately programming was still a hobby at that point. Funnily when I felt confident enough to tell other people about this, several immediate started laughing and told me w…

Pointers are by far the most insidious thing about C. The problem is that nobody who groks pointers can understand why they had trouble understanding them in the first place. Once you understand, it seems so obvious that you cannot imagine not understanding what a pointer is, but at the beginning, trying to figure out why the compiler won't let you assign a pointer to an array, like `char str[256]; str = "asdf"`, is…

For this example:

   char str[256]; str = "asdf"
both str and "asdf" are not pointer-type expressions; they're both arrays (which is exposed by sizeof). The reason why this doesn't work is because C refuses to treat arrays as first-class value types - which is not an obvious thing to do regardless of how well you understand pointers or not. Other languages with arrays and pointers generally haven't made this mistake.

Re: Everything I wish I knew when learning C

#397

Earlier quoted context omitted.

Well there’s a little bit more to it. There is a type involved, and then there’s pointer arithmetic.

Well yes, but those aren't hard. Pointer arithmetic is merely knowing that any addition/subtraction done to a pointer is multiplied by the size of the type being pointed to. So if you're pointing to a 64-byte struct, then "ptr++;" adds 64 to the pointer.

Typed pointers interact with aliasing in "interesting" ways.

Re: Everything I wish I knew when learning C

#398
post #27

Earlier quoted context omitted.

... which is why I never understood why this is the convention rather than int* x, y. Does somebody know?

Spaces are ignored (except to separate things where other syntactical things like * or , aren't present), and * binds to the variable on the right, not the type on the left. I actually got this wrong in an online test, but I screenshotted every question so I could go over them later (! I admit, a dirty trick but I learned things like this from it, though I still did well enough on the test to get the interview). int*…

I think the question is why it binds to the variable rather than the type. It's obviously a choice that the designers have made; e.g. C# has very similar syntax, but:

   int* x, y;
declares two pointers.

I think the syntax and the underpinning "declaration follows use" rule are what they got when they tried to generalize the traditional array declaration syntax with square brackets after the array name which they inherited directly from B, and ultimately all the way from Algol:

   int x, y[10], z[20];
In B, though, arrays were not a type; when you wrote this:

   auto x, y[10], z[20];
x, y, and z all have the same type (word); the [] is basically just alloca(). This all works because the type of element in any array is also the same (word), so you don't need to distinguish different arrays for the purposes of correctly implementing [].

But in C, the compiler has to know the type of the array element, since it can vary. Which means that it has to be reflected in the type of the array, somehow. Which means that arrays are now a type, and thus [] is part of the type declaration.

And if you want to keep the old syntax for array declarations, then you get this situation where the type is separated by the array name in the middle. If you then try to formalize this somehow, the "declaration follows use" rule feels like the simplest way to explain it, and applying it to pointers as well makes sense from a consistency perspective.

Re: Everything I wish I knew when learning C

#399
post #141

Earlier quoted context omitted.

I agree with the factual things that you said (e.g. "entire program execution was meaningless"). Some stuff was hyperbolic ("time-travel back to the start of the universe, delete it"). > [compilers] will make transformations to the program whose effects could manifest before the UB-having code executes [...] It's monumentally rude to us poor programmers who have bugs in our programs. The first statement is factually…

> Integer division is a slow operation, and under the rules of C, it has no side effects. Then C isn't following this rule - crashing is a pretty major side effect.

Side effects are a type of defined behavior. Crashing is not a "side effect" in C terms.

Re: Everything I wish I knew when learning C

#400

Earlier quoted context omitted.

> 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) proble…

Undefined behavior is not a side effect to be "invoked" by the rules of C. If UB happens, it means your program isn't valid. UB is not a side effect or any effect at all, it is the void left behind when the system of rules disappears.
Post reply on HN