Live data from Hacker News

A convenient untruth: Array notation in C is a lie

blog.feabhas.com

121–130 of 176 posts

Re: A convenient untruth: Array notation in C is a lie

#121

Earlier quoted context omitted.

I mean, int[5] and int[7] would be different types, and lots of bugs happen from passing something besides the appropriate array type to a function. That being said, most languages which disambiguate between int[5] and int[7] provide some kind of polymorphism (and usually store it as a struct of size + data, to enable that). For example: you can define a first function that goes from t[N]->t pretty easily, and it wou…

Right, in a language with dependent types there is a type-level difference between int[5] and int[7], but c is not such a language, therefore using a syntax that encourages the mistaken notion that there is a type-level difference between int[5] and int[7] would be misleading.

There is a small type difference in C, sizeof() of those two types is different.

Re: A convenient untruth: Array notation in C is a lie

#122
post #102
post #68

Earlier quoted context omitted.

> [...] everywhere else, multiple declarations in one statement all have the exact same type, but for some reason pointers get special treatment so that you can declare variables of type X and variables that are pointers to type X in the same statement, which just seems odd to me. It's not just pointers: int a, *b, c();

Jesus and you guys say JavaScript is bad...

oh, don't worry, we also say that C is bad. C++ also gets an honourable mention.

Re: A convenient untruth: Array notation in C is a lie

#123

Earlier quoted context omitted.

Some of the first things that people learn in C is the fallacy that "arrays and pointers are equivalent". An array is a series of contiguously laid-out objects that has a size known at compile time (except C99 VLAs). A pointer, on the other hand is merely a "single cell" that is supposed to contain an address and can be added/subtracted to, also dereferenced. The truth is that array names decay to pointers except whe…

Right, the fact that arrays aren't pointers is academic when they decay to pointers at the drop of a hat. In fact it's so easy to decay the array to a pointer that it is generally best to always treat it as a pointer lest you get burned later on during a code refactor. This mostly means never using sizeof() to get the size of an array.

Even if we simplify it this way, arrays are still different because they cannot be assigned to and their decayed pointer can never be NULL (they always have a backing storage provided by the compiler):

    int arr[5];
    int *ptr;

    // "arr" has a fixed backing storage of sizeof(int) * 5 bytes
    // "ptr" may point to anything or be NULL, depending on control flow

Re: A convenient untruth: Array notation in C is a lie

#124

Earlier quoted context omitted.

Right, the fact that arrays aren't pointers is academic when they decay to pointers at the drop of a hat. In fact it's so easy to decay the array to a pointer that it is generally best to always treat it as a pointer lest you get burned later on during a code refactor. This mostly means never using sizeof() to get the size of an array.

Even if we simplify it this way, arrays are still different because they cannot be assigned to and their decayed pointer can never be NULL (they always have a backing storage provided by the compiler): int arr[5]; int *ptr; // "arr" has a fixed backing storage of sizeof(int) * 5 bytes // "ptr" may point to anything or be NULL, depending on control flow

Best to check for NULL anyway though, because anything can happen once you let it decay to a pointer. Never assigning them is a good idea though, maybe it's best to think of them as constant pointers? But that's more confusing terminology for a C programmer, so maybe not. People get really wrapped around the axle when differentiating a constant pointer from a pointer to a constant.

Re: A convenient untruth: Array notation in C is a lie

#125
post #48
post #39

This gets weird when you compare it to how structs work in C. Both are complex data types, so I sometimes forget that array semantics are totally different to struct/union semantics. Unlike arrays, in ANSI C, structs are real value types. You can pass them by-value to functions, return them by-value from functions and assign them by-value to variables of the same type. Also, structs never work like pointers to themse…

Worth noting that you can always wrap an array in a structure and kick it around in your program happily, as if it's how the language should've been. struct { int a[5]; } arr5 = {{4, 3, 2}}; What's a bit weird though, is that while initialization like above is possible, assignment of a contsant is not as smooth in C, you will have to typedef your structure and use it in a typecast expression: typedef struct { int a[5…

You can actually drop the extra brackets (at least in C++, but IIRC also in C).

C++11 has array which has proper value semantics. It is implemented exactly like your structure above, so no overhead, and also has a proper operator== and assignment via initializer list.

Re: A convenient untruth: Array notation in C is a lie

#126

Earlier quoted context omitted.

The point of the article is that sometimes they're not equivalent, and that creates a lot of confusion. Please read it before commenting on it.

I thought the same things as GP. Anyone who has read K&R (as any C programmer ought to have) will know everything in this article.

I don't think that's true. While I don't have my copy of K&R handy, I don't recall it covering all the subtleties of how assignment and increment operators and sizeof will work differently for something declared as an array vs. something declared as a pointer. At least in the edition I've had, it just had the same "pointers and arrays are equivalent" which is misleading in exactly the way this article describes. Did that get added in your much-later edition?

Re: A convenient untruth: Array notation in C is a lie

#127

I once proposed a backwards-compatible way out of this: "Safe Arrays for C"[1]. The fundamental problem with arrays in C is that compiler has no idea how big they are. My proposal was to replace int read(int fd, char buf[n], size_t n); with a safe form int read(int n; int fd, char (&buf)[n], size_t n); This says that the size of "buf" is "n", which comes in as another parameter. There are no array descriptors; the ge…

> The fundamental problem with arrays in C is that compiler has no idea how big they are Isn't that the compiler's choice, to not know how big they are? You could write a standards compliant implementation of C that did track how big arrays were if you wanted to couldn't you?

That's been done, with "fat pointers". GCC used to have an option for that, but it wasn't used much. The overhead is all at run time and is substantial.[1]

[1] http://www.imperial.ac.uk/pls/portallive/docs/1/18619746.PDF

Re: A convenient untruth: Array notation in C is a lie

#128
post #55

Earlier quoted context omitted.

C programmers are not thinking that C is low level language. ;-) Assembler language is low-level language, because it is not portable between architectures. C is high-level language, because it's portable. It's means that one C language statement can be translated into many statements of assembler language, hence C has higher level of abstraction than assembler language.

C is low level for everyone using a programming language other than C (excluding assembly). That even applies to languages created in the early 60s, prior to C.

C is one of first high level languages. If someone is not educated well, it is his problem. It's possible to do low-level stuff in C, e.g. by inline assembler, but it does not makes C low level. Low level languages lacks abstractions, i.e. they tied to machine, while high level languages are not.

Re: A convenient untruth: Array notation in C is a lie

#129
> Why is this failing? Because the array’s name is a lie! Using a variable as an expression normally yields its value, but in the case of arrays the array name yields a pointer (to the first element; which is at least reasonable)

Ahh, not quite. sizeof(array) was using the variable as an expression - not an evaluated expression, but an expression nonetheless - and it's clearly not giving us the same result as sizeof(array+0). In C++, you can even construct references, which can be abused in conjunction with templates to create a 'safe' array size check, which relies on array maintaining it's array typing:

  int (&r)[5] = array;
Now, arrays are implicitly convert to pointers is you so much as sneeze in the same room as them, but there are instances (namely arrays of arrays and the like) where you can fuck up your pointer math if you assume that simply using the array name yields a pointer, or that the array 'is' a pointer - because that is the lie! If I'm feeling particularly explicit, I'll write something like (assuming a and b are arrays, in C++ again):

  std::copy(a+0, a+N, b+0);
Where the +0s ensure I'm actually dealing with pointers. This avoids any compiler errors from having mixed types for 'a' (array) and 'a+N' (pointer) which, while rare (the former typically converts to a pointer at some point), has happened to me at least once.

The real reason "this" (array init and assignment) is failing is that C decided arrays weren't copyable and assignable like this. That's all. Really! Now, one can think of plenty of rationale that made sense at the time (memcpy is more explicit, simplifies the implementation to only implement copy/assignment for simpler types, etc.) but it ultimately boils down to the choice of the implementors.

Re: A convenient untruth: Array notation in C is a lie

#130
post #69

In the last example, > char string[] = "Hello world"; I thought that gives out warning these days. Isn't the proper type of a string literal the following? > const char string[] = "Hello world"; Therefore, you can't really modify individual characters there.

C string literals are " char * ".

C++ string literals are " const char * " (one of the minor incompatibilities between the languages).

They ought to be "const char[]" but aren't. And, the behavior of "char[]" should be the same as "struct { char[] }", but it isn't.

Post reply on HN