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…
Everything I wish I knew when learning C
331–340 of 401 posts
Re: Everything I wish I knew when learning C
#332Earlier quoted context omitted.
> I really wish that int arr[5] adopted the semantics of struct { int arr[5]; } You and me both. In fact, D does this. `int arr[5]` can be passed as a value argument to a function, and returned as a value argument, just as if it was wrapped in a struct. It's sad that C (and C++) take every opportunity to instantly decay the array to a pointer, which I've dubbed "C's Biggest Mistake": https://www.digitalmars.com/artic…
That would be a nice little "gcc addition" to the C standard, honestly. To bad they spend most their time doing whatever it is they do.
Even the few "security" attempts that they have made, still require separate pointer and length arguments, thus voiding any kind of "security" that the functions might try to achieve.
However even a Macro Assembler is safer than modern C compilers, as they don't remove your code when one steps into a UB mine.
Re: Everything I wish I knew when learning C
#333I 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…
> There's no particular reason why C doesn't allow you to assign one array to another of the same length Actually, there is a particular (though not necessarily good) reason, since that would require the compiler to either generate a loop (with conditional branch) for a (unconditional) assignment or generate unboundedly many assembly instructions (essentially a unrolled loop) for a single source operation. Of course,…
Re: Everything I wish I knew when learning C
#334I 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…
I really wish that int arr[5] adopted the semantics of struct { int arr[5]; } -- that is, you can copy it, and you can pass it through a function without it decaying to a pointer. Right now in C: typedef uint32_t t1[5]; typedef struct { uint32_t arr[5]; } t2; void test(t1 a, t2 b) { t1 c; t2 d; printf("%d %d %d %d\n", sizeof(a), sizeof(b), sizeof(c), sizeof(d)); } will print 4, 20, 20, 20. I understand that array typ…
sizeof(b.arr) != sizeof(b)
Consider:
#include
#include
typedef struct Array Array;
struct Array {
int32_t data[8];
};
void foo(Array const* arr) {
size_t sz = sizeof(arr->data);
}Re: Everything I wish I knew when learning C
#335I 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…
int32_t (* bar(void))[4] {
static int32_t u[4] = {1, 0, 1, 0};
return &u;
}
Return a pointer to a function taking a char: void f(char a) {
// ...
}
void (* baz(void))(char) {
return f;
}Re: Everything I wish I knew when learning C
#336Earlier quoted context omitted.
I remember reading a blog post a couple of years back on undefined behavior from the perspective of someone building a compiler. The way the standard defines undefined behavior (pun not intended), a compiler writer can basically assume undefined behavior never occurs and stay compliant with the standard. This offers the door to some optimizations, but also allows compiler writers to reduce the complexity in the compi…
Yeah a decent chunk of UB is about reducing the burden on the compiler. Null derefs being an obvious such example. If it was defined behavior, the compiler would be endlessly adding & later attempting to optimize-away null checks. Which isn't something anyone actually wants when reaching for C/C++. Similarly with C/C++ it's not actually possible for the compiler to ensure you don't access a pointer past the end of th…
Re: Everything I wish I knew when learning C
#337Earlier quoted context omitted.
I would go one step farther: The documentation will say it is undefined behavior but the compiler doesn't have to. Here's an example from the man page for sprintf sprintf(buf, "%s some further text", buf); If you miss that section of the manual, your code may work, leading you to think the behavior is defined. Then you will have interesting arguments with other programmers about what exactly is undefined behavior, e.…
I'll tell you what happens when someone writes: sprintf(buf, "%d %d", f(i), i++); They get told to rewrite it.
Re: Everything I wish I knew when learning C
#338Earlier quoted context omitted.
At the CPU level, division by zero can behave in a number of ways. It can trap and raise an exception. It can silently return 0 or leave a register unchanged. It might hang and crash the whole system. The C language standard acknowledges that different CPUs may behave differently, and chose to categorize division-by-zero under "undefined behavior", not "implementation-defined behavior" or "must trap". I wrote: > Inte…
> 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…
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. It puts so much extra weight on the shoulders of the programmer, though I appreciate that using only rules which can be checked by the compiler is hard too, especially back when C was standardised.
Re: Everything I wish I knew when learning C
#339One C idiom that I found pretty useful is Xmacros [1]. For instance, it's used extensively in GCC code-base (.def files). [1] https://www.drdobbs.com/the-new-c-x-macros/184401387
Have any more of "C Idioms" collection?
Re: Everything I wish I knew when learning C
#340‘ You can’t extend structs or do anything really OO-like, but it’s a useful pattern to think with’ That’s not quite true. If you define 2 structs so that they start the same (eg: both with “int x; int y” in your example), pointers can be passed to functions with either struct type. You can use this to add fields (eg: int z) to structures, and extend a 2d vector into a 3d one… With a bit of creative thought, and const…
The defined way to do something like this is to have the smaller struct as the first member of the larger one. The first member is guaranteed to have the same address as the outer object.
The common initial sequence trick is guaranteed to work with unions in limited circumstances.