5 ms·
C array types are weird
- IncreasePosts 5mo agoPaging walter bright
- glouwbug 5mo agoC's biggest mistake. But in other news most don't know that a[3] == 3[a]
- throwaway27448 5mo agoEven more irrelevant than the array type
- parlortricks 5mo agoI didn't understand why a[3] == 3[a], but i found this stackoverflow that explains it. https://stackoverflow.com/a/16163840 https://stackoverflow.com/a/16163840 In C a[i] is converted to *(a+i) internally. i[a] is converted to *(i+a). Array names also act as pointers in c. so (a+i) or (i+a) give an address (using pointer arithmetic) that is dereferenced using
- parlortricks 5mo agoIn C a[i] is converted to *(a+i) internally. i[a] is converted to *(i+a). Array names also act as pointers in c. so (a+i) or (i+a) give an address (using pointer arithmetic) that is dereferenced using *
- glouwbug 5mo agohttps://www.digitalmars.com/articles/C-biggest-mistake.html https://www.digitalmars.com/articles/C-biggest-mistake.html
- WalterBright 5mo agoAt your service! D fixed it, and I'm sorry C users have suffered as the array-to-pointer decay blasted their kingdom. Fixing it in C is easy and should be the #1 priority.
- aa-jv 5mo agoAt this point, C's #1 priority is not breaking things in existing implementations, though, Walter...
- WalterBright 5mo agoMy proposal does not break existing code. https://www.digitalmars.com/articles/C-biggest-mistake.html https://www.digitalmars.com/articles/C-biggest-mistake.html
- aa-jv 5mo agoYes it does: >All it needs is a little new syntax
- bmn__ 5mo agoYou seem to be under the impression that adding new syntax necessarily breaks existing code. But that just isn't true. If you can't fathom how it's done, then look at the use VERSION statement in Perl.
- aa-jv 5mo agoIt doesn't just break existing code, it breaks existing tooling for checking syntax .. (Perl is hardly a decent model.)
- bmn__ 5mo agoThis answer that is both so stupendously confident and wrong is only possible if you in actuality decided to not read and understand how this problem is solved in Perl.
- uecker 5mo agoIn practice, the [static n] notation can give you useful warnings and bounds checking. https://godbolt.org/z/PzcjW4zKK https://godbolt.org/z/PzcjW4zKK And while the (*array_ptr)[3] notation take a moment to get used to, it is very logical. If you have a pointer to an array, you dereference it first and then indx into it. Again, useful for bounds checking: https://godbolt.org/z/ao1so9KP7 https://godbolt.org/z/ao1so9KP7
- dnautics 5mo agoWhat is **int[3][5]
- ori_b 5mo agoA syntax error. You need a variable name, not a type name, in the middle.
- thrance 5mo agoA pointer to a pointer to a pointer to a pointer of integers.
- kazinator 5mo agoIn C declaration syntax, there is a "stem" called declaration specifiers consisting of specifiers and qualifiers. That's where int can appear. After that, there is a declarator. In some cases, multiple declarators separated by a comma, which share the same "stem". int a, b, *c; // one stem consisting of "int", three declarators. The * is declarator syntax for deriving a pointer type. It never appears such that a type specifier would come after it somewhere to the right. Some languages have extended the C declaration syntax such that the type derivators can be moved from the declarator part to the "stem". For instance, as an alternative to: int a[10]; you can write int[10] a; This is how we could get **int[3] as a declarator stem indicating an array of 3 pointers to pointers to int. But it's not in C.
- fatty_patty89 5mo agothere's no array type in c
- colejohnson66 5mo agoYes it does. It just decays to a pointer at the slightest touch.
- throwaway27448 5mo ago[flagged]
- dwattttt 5mo agoBecause doing a dance to avoid it decaying conveys better information to both the compiler and downstream users of your code.
- throwaway27448 5mo ago[flagged]
- Mikhail_Edoshin 5mo agoThere are differences. E.g. va_xxx functionality may be implemented either with a pointer or an array. The difference becomes visible if you try to pass a va_list to another variadic function and then extract it later with va_arg. About half of compilers will happily do that, and another half will refuse to compile the naive version. (There's a more sophisticated proper way.) https://stackoverflow.com/questions/79897621 https://stackoverflow.com/questions/79897621
- ocreat 5mo agoThere is a big difference between: struct A { int size; char data[]; } struct B { int size; char *data; }
- the__alchemist 5mo agoThis is one of the things that I feel is an inappropriate abstraction that is around for historical reasons. When I do FFI to call C from rust, I usually wrap the generated API (Which is pointer based) into rust's &[] array syntax. Arrays/lists/Vecs etc in most non-C languages feel like an abstraction over a collection of items; I feel like C's exposing the pointer directly is taking a low-level memory/MMIO operation and inserting it into business logic. Conceptually, I like to keep them separate; pointers for writing drivers, accessing registers, writing to flash memory etc. Arrays/lists/vecs for higher level operations on collections. Tangent: I have a pet theory that part of Zig's raison d'etre is to fix some of the problems with C, while accommodating its pointer-based data structures, and the resulting patterns.
- vulcan01 5mo agoThis talk – "Programming without pointers" – by Andrew Kelley may be interesting to you. https://www.hytradboi.com/2025/05c72e39-c07e-41bc-ac40-85e8308f2917-programming-without-pointers https://www.hytradboi.com/2025/05c72e39-c07e-41bc-ac40-85e83...
- throwaway27448 5mo agoLearning to program with pointers is enormously useful. It's simply bad software engineering to not use typing to enforce constraints on access to pointers (or addresses, or however you'd like to term them)
- doyougnu 5mo agoIIRC that talk of about using indices (u32) to represent data in an array. That is orthogonal to representing that information in the type system since you can just type the index
- deleted 5mo ago[deleted]
- throwaway27448 5mo ago
- throwaway27448 5mo ago[flagged]
- cartoonfoxes 5mo agohttps://www.tiobe.com/tiobe-index/ https://www.tiobe.com/tiobe-index/
- mianos 5mo agop.s. in case you don't want to follow the link, number 2 on the list
- pdpi 5mo agoAs always, the TIOBE Index is of dubious value. The fact that it ranks Delphi above both Go and Rust should give you an idea of why.
- keyle 5mo agoYeah it's rather odd, also jumps like this in ADA makes you wonder https://www.tiobe.com/tiobe-index/ada/ https://www.tiobe.com/tiobe-index/ada/
- flohofwoe 5mo agoAda is being discussed a lot on HN as some sort of 'lost utopia', and AFAIK Tiobe doesn't count actual usage but (more or less) mentions.
- throwaway27448 5mo agoC is hardly any path forward
- flohofwoe 5mo agoIt's not much different from any other high level programming language. Just a different set of compromises to accept.
- kazinator 5mo agoThere is a history to it; in one of the predecessor languages, like B, Ritchie actually had arrays that had a hidden pointer to their start. The "array to pointer decay" was actually a real operation that loaded an address from memory, and it was possible to twiddle the bits to relocate an array. One problem with it was no way to initialize such a pointer field that would allow an array to live in dynamically allocated storage (no constructors in the language). So in short, the bad design (array values produce pointers) was informed by conceptual compability with an earlier design in which that was literally happening.
- xenadu02 5mo agoNot just this it is important to remember that there was no "aha!" moment where C was created whole-cloth by writing the first compiler in B then cross-compiling. The language B was evolved in-place by adding new features, then editing the compiler source to make use of those new features, then repeating. They simply started calling it "New B". At some point the language had evolved sufficiently that they decided to call it C. The semantics of arrays were inherited from B and simply never changed. Part of me suspects this was also because it was seen as "clever" at the time. Look ma, we let arrays turn into pointers! Isn't that clever? When you look at pre-ANSI C function prototypes you wonder "where are the parameter types?" because there are none. The compiler didn't bother to check. Part of that was perhaps for implementation reasons but a big part of that was the feeling or culture inherited from B: in that language you just had words of memory. You were free to interpret any word of memory as any data type you liked. So duh of course it is up to you to decide how many parameters your function received and of what type. If the caller supplied a different number or different types? Don't do that. If you are coming from that sort of world clever tricks like arrays decaying to pointers or automatically converting between data types and sizes seems perfectly natural. Anything C offers above and beyond that is an improvement from B after all.
- im3w1l 5mo agoThose decisions also make a lot of sense from the C-as-macro-assembler point of view (passing parameters puts values in the places defined by the calling convention, and taking parameters pulls them out) that has of course gradually faded over the year, being replaced by a rigorously defined (and undefined) abstract machine.
- fooker 5mo agoC array types are weird because C doesn't really need arrays. It's not what C was about. But if you designed a language in the era where Fortran, THE array language, reigned supreme, nobody would use your language. The mindshare Fortran had is difficult to convey now, half a century later. Think of it like making a chatbot today and not mentioning AI or LLMs, that's what making a language without arrays would have felt like in 1970.
- alexey-salmin 5mo agoPeople who do HPC in C actually wish C had proper arrays like Fortran. If your function takes two pointers as inputs instead of two arrays they can alias the same memory and in fact they may alias any other pointer of the same type. Writing into one of them invalidates all the values you have in registers so you have to load them again. The "restrict" keyword was invented to solve this but it still has weaker semantics than original Fortran arrays. It can still solve a big share of problems, but it never got proper adoption and never even made it into C++.
- xmcqdpt2 5mo agoSometimes you have to use C but really they should be doing HPC in fortran. It has C FFI, it can compile to static programs and to dynamic libraries, it has C-like performance, etc. (It's not as portable as C though, and the compilers have more bugs.)
- genewitch 5mo agoi learned FORTRAN in an accelerated tech program in 1996-ish in high school. i used fortran recently to see how "slow" python is, i did matrix multiplies by hand in .c, and .py. Now i didn't write the fortran, the AI did, but i remember enough that i verified what it did was sane, also the other two i wrote did agree with results. fortran 1 unit of time C 1.7 unit of time python 2.2 unit of time for the same matmuls. anyhow, 1996-ish. crazy.
- fooker 5mo agoFortran compilers will happily tile your loops. C compilers don't really do this.
- mlmonkey 5mo agoIt still cracks me up that 3[x] and x[3] mean the same thing in C.
- jason_s 5mo agoyeah that's what I thought the article was going to be about.
- Animats 5mo agoThe real lack is that C doesn't have slices. Slices can do most of what pointers into arrays can do, with sane semantics. Slices were invented surprisingly late. They were implementable in the 1970s, but didn't really show up until the 1990s. Now that we have slices, the demand for pointers into the middle of an array has much decreased. I had a go at retrofitting C with slices over a decade ago.[1] Too much political hassle. [1] https://www.animats.com/papers/languages/safearraysforc43.pdf https://www.animats.com/papers/languages/safearraysforc43.pd...
- abnercoimbre 5mo agoMeaning it died at committee?
- jcranmer 5mo agoFrom what I can see in the WG14 document log [1], it never made it to the committee in the first place. [1] https://www.open-std.org/jtc1/sc22/wg14/www/wg14_document_log https://www.open-std.org/jtc1/sc22/wg14/www/wg14_document_lo...
- Animats 5mo agoNever pushed it that far. Mentioning safety in a C or C++ context was viewed very negatively back then.
- uecker 5mo agoWe are working towards this though and a lot of this already works: https://godbolt.org/z/EP3cP3qGs https://godbolt.org/z/EP3cP3qGs
- uecker 5mo agoIt is a struggle though to get the improvements through the committee. Especially the C++ folks from the Clang side fight very hard against it, this is - for example - why we not have forward declarations where I already had weak consensus, but the clang area team made it clear they will never implement it.
- alexey-salmin 5mo agoInterestingly the article doesn't mention two-dimensional arrays and they're curios because they bring a certain asymmetry with them. It always tripped me over the most in C because I otherwise find the language very "symmetrical". It often feels like in design of this language the beauty of expressing certain things took priority over readability or safety which I admire in a way. But somehow not in the case of the two-dimensional arrays. If you see a[i][j] it could mean two completely different things: 1) "a" is a continuous chunk of memory of N*M bytes, so it behaves as char*; a[i][j] == *(a + i*M + j) 2) "a" is an array of char* pointers that point to N completely distinct memory chunks of size M, so it behaves as char**; a[i][j] == *(*(a + i) + j) With flat arrays the difference between an array as a variable and a pointer to the first element is literally negligible because you won't even see the difference in the assembly. This is why the automatic decay-to-pointer makes a lot of sense. But that breaks completely with multiple dimensions. You definitely see the difference in the assembly because the memory layout is so different.
- ActorNightly 5mo agoI mean, just like with 1 dimensional arrays, it depends on the context. Array memory is on the stack. The size of that array is actually not known at run time, its only known at compile time, where any reference to that length gets resolved by the compiled. If your 2d array sits on the stack, then inferring memory layout is pretty easy. If you are dealing with pointer that was passed to a function, then you can't assume anything about data size or limits, which is why many functions that take pointers take a size parameter as well.
- alexey-salmin 5mo ago> If your 2d array sits on the stack, then inferring memory layout is pretty easy. If you are dealing with pointer that was passed to a function, then you can't assume anything about data size or limits, which is why many functions that take pointers take a size parameter as well. Right, but 2d arrays come into this picture with their own quirks again. You're not just passing the size as the parameter, you can pass it as a "special" parameter that influences how the compiler will interpret other parameters. E.g. in C99 you can do this: void do(size_t x, size_t y, int a[][y]); Here "y" plays the critical role because it will be used to compute offsets in the a[i][j] expression. For 1d arrays this doesn't happen. Of course it's still generalizable as "all but the outermost dimensions should be known" and for 1d array the outermost dimension is the only dimension. Still, this whole thing always felt a bit odd to me.
- jason_s 5mo agoFrom the title, I thought they were going to point out that `a[2]` and `2[a]` have identical meaning in C.
- RobotToaster 5mo agoIt's still weird to me that you can declare an array with the register keyword. Then it (understandably) becomes UB to attempt to get the pointer. (It also probably isn't stored in a register, since the keyword is just asking the compiler nicely.)
- flohofwoe 5mo agoThe meaning of the 'register' keyword has changed over time to just "it's illegal to take the address of this item": https://www.godbolt.org/z/TKq9rWzP1 https://www.godbolt.org/z/TKq9rWzP1 Don't know what's the idea behind not allowing to take the address of a value though.
- randomNumber7 5mo agoIt was always only a suggestion to the compiler, to hold this variable in a register. Compilers got so good at optimization that there is little point using it. If a variable is held in a register you can't access it with a pointer. So if your intention is it should be in a register you can't take the address.
- tardedmeme 5mo agoIt once told the compiler to hold the value in a register because the compiler wasn't very smart at all.
- RobotToaster 5mo agoA register isn't in external memory, so isn't addressable as such. That part makes sense since if the compiler actually follows your suggestion it can't be addressed. Thinking about it, storing arrays in registers would possibly make sense on systems like the 8051 where you actually have a bunch of general purpose register banks, but those don't exist in x86.
- wasmperson 5mo agoIt matters in single-pass compilers. You can't allocate a variable in a register if its address is ever taken, but by the time a single-pass compiler knows that information it has already spit out all of the assembly for the function.
- moktonar 5mo agoint x[n] and int *x are very different things when it comes to defining memory layout tho. In one case you end up with n int sized slots of memory, in the second with one register sized slot. That makes all the difference when defining structs for example.
- danborn26 5mo agoC's array decay into pointers still catches me off guard sometimes. It is definitely one of those quirks you just have to memorize.
- danborn26 5mo agoThe way C handles array decay to pointers always trips up beginners, but it's exactly what makes passing data around so lightweight. Good writeup on a classic quirk.
- fredrikholm 5mo agoAgreed, I even find it surprisingly ergonomic. Thinking of data as offsets into memory is unusual coming from almost every other language, but once you grokk it it's actually quite nice. I love C more than I should.
- K0nserv 5mo agoBut remember, C is simple.