Live data from Hacker News

A convenient untruth: Array notation in C is a lie

blog.feabhas.com

151–160 of 176 posts

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

#151
post #95

Earlier quoted context omitted.

I don't know if "declarations mirror use" gets you all the way there. You can use: *some_index[arr] (though obviously not saying you should ), but you can't declare: char *5[arr]; Obviously there's good reasons for that, but then you have "declarations mirror use, except when there's good reason not to", which basically brings you back to the original question.

> char *5[arr] Isn't that exploiting a quirk of the operator's definition, rather than how the operator is intended to be used?

I assume you're right, though I would love to see some of the original discussion around it. That said, while I don't think it's a catastrophic point against "declarations mirror use", I do think it's a strike against it.

Edit: I guess I'll also note here just for fun that while

  char [5]arr;
seems reasonable enough,

  char [4][5]arr1;
  char[5][4] arr2;
seems less so.

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

#152
post #14

These facts is not a lies. They are inconviniences which one can see looking on C from perspective of higher level language. But if you learn how to program with assembler before studing C, than all these facts would look like obvious and convinient syntactic sugar. Maybe in C++ these facts become inconvinient, because of C++ pretending to be higher level language than crossplatform assembler. But if it is a problem,…

None of those 'lies' have anything to do with assembler. C deviated from regularity for the sake of convenience in some cases, and now we are stuck with those bad decisions forever.

> None of those 'lies' have anything to do with assembler.

They do. Lets take a look at the very first one. "Array name is just a pointer".

Assebler "array" is a name of a label. So its just named address in memory. Or we can alternatively say, that assembler array is constant pointer.

`sizeof' returns size of array in bytes? Hmm... maybe that because main C abstraction for memory is the assembler one: memory is a continuous sequence of bytes? `sizeof' meant to be used for functions like malloc or memcpy, not for operator `new'. When we use some dynamic memory allocation in assembler we get pointer to untyped memory chunk, compare with C:

void* malloc(size_t size);

If you wish I can show you the connections of other 'C lies' with assembler abstractions. I'm too lazy to write about all of them, but I could write about one more if you ask. Just pick one you like more.

> now we are stuck with those bad decisions forever

Yes, you are right. We stuck with that. And it is bad. But it doesn't make my point wrong. C is crossplatform assembler, and these decisions looks pretty good from perspective of assembler. They give to programmer low level control on generated machine code while keeping code portable, and its very useful in some cases. For example when you developing an OS kernel.

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

#153
post #68

Earlier quoted context omitted.

although it's messed up anyway as But that's because they wanted it to be like that. To me, this code is very unclear: int a, *b, c, d; because 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. I'…

> [...] 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();

I've occasionally written code like:

    char buffer[16], *ptr = &buffer[0]; // declare an array and a pointer into it

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

#154

Earlier quoted context omitted.

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.

They actually are proper arrays in C++ [1], although, as usual, they decay to pointers faster than an unstable particle. [1]: https://godbolt.org/g/ZWISCu

I stand corrected!

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

#156
post #152

Earlier quoted context omitted.

None of those 'lies' have anything to do with assembler. C deviated from regularity for the sake of convenience in some cases, and now we are stuck with those bad decisions forever.

> None of those 'lies' have anything to do with assembler. They do. Lets take a look at the very first one. "Array name is just a pointer". Assebler "array" is a name of a label. So its just named address in memory. Or we can alternatively say, that assembler array is constant pointer. `sizeof' returns size of array in bytes? Hmm... maybe that because main C abstraction for memory is the assembler one: memory is a co…

From an assembler point of view a structure of N elements of type T and an array of T[N] have exactly the same layout and are accessed in exactly the same way [1], but in C have wildly different semantics.

Sizeof behaves exactly the same way for structs and arrays, so it is one of the few things in C that treat arrays "correctly".

[1] although usually the offset is constant for a struct field access.

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

#158
post #21

The equivalent in C# always made more sense to me (not comparing memory or allocation model between the languages, but simply syntax in relation to a person reading it): int[] arr = new int[5]; // C# int arr[5]; // C The fact that the brackets go on the datatype always made more sense to me, after all, I want to refer to memory of a certain cell size (as indicated by int). I realize that there is a lot of stuff going…

Unfortunately this is differently wrong in C# (and many other modern languages). arr here is not an array at all, but is actually a reference to an array and has different semantics from the the corresponding C declaration. This is not just being pedantic: using the same syntax for values and references does lead to confusion. At least C# has actual value types and 'ref' (but not value type arrays AFAIK), in Java thi…

I wouldn't agree to call it "wrong", since we're talking about languages that are trying to discourage you from doing your own memory management *(C#, that is, but I'd say this applies to any language running in a VM with gc, really). Thus, in such languages, there shouldn't be a conceptual or syntactical difference between values and references. The fact that C# allows to differentiate them from each other is a leaky abstraction imho, built in to satifsy people form a C/C++ background.

I believe that whether this is 'wrong' depends on the problem you're trying to solve. In my day to day work the difference usually doesn't matter, and I am totally happy passing around objects that others would call heavy. I am really happy that I don't have to care about references or values when doing scientific python stuff (pandas, numpy, etc), whereas I am also really happy that this stuff is important to people implementing these libraries.

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

#159
post #21

The equivalent in C# always made more sense to me (not comparing memory or allocation model between the languages, but simply syntax in relation to a person reading it): int[] arr = new int[5]; // C# int arr[5]; // C The fact that the brackets go on the datatype always made more sense to me, after all, I want to refer to memory of a certain cell size (as indicated by int). I realize that there is a lot of stuff going…

> I will now duck and get far away from the internet, in fear of all the hateful comments explaining to me how I am wrong, and apparently just don't understand the superior beauty of complicated C syntax. I am going to be that person :-) One phrase – "declarations mirror use". In a declaration, you use the same set of operators around the declared object that you would use in a normal expression. All of these operato…

Thank you for clarifying. I have never heard of "declaration mirrors use" (DMU) before. Also thank you for explaining it in a non-hateful way (which isn't always the tone when touching religious matters). Next up: Tabs vs. Spaces ;)

This sounds really odd to me. I can see, and others have pointed this out in the comments, too, that DMU might facilitate building a compiler for such a language and reuses several operators. However, I am not sure whether I like this goal from a person-centric perspective.

Experts might be used to matching declaration and use structures in their code, maybe to make it more 'symmetric', but I am sceptical whether this match is actually quite 'expensive' when developing code with many people of different skills.

Use and declaration are differnt concepts, and as such should have different representations (i.e., syntactically), otherwise you might introduce synonym defects (i.e., ambiguities, where it becomes hard to understand what somebody means). I found that some of my colleagues and students had a hard time to learn what pointers are about (and I believe this is a fairly common phenomenon, that people find pointers in C hard to grasp), because the asterisk is used both in declaration and in dereferencing. I found that some of my students got the concept more easily after I introduced a couple of macros:

    #include "stdio.h"

    #define IntPointer int*
    #define value_of(ptr) *ptr 
    #define address_of(v) &v

    int main() {
        int a = 42;
        IntPointer p = address_of(a);
        printf("%i\n", value_of(p));
        return 42;
    }
The above code has no other purpose than to separate the concepts verbally, so that they can be reasoned about explicitly. This can be achieved in other ways, for example, in C++ I tend to use templates or classes to abstract the pointer syntax away. Of course, I am aware that using classes introduces overhead and this technique is not necessarily feasible when you need as much performance as you can get.

This also underlines my original point to place the asterisk on the datatype rather than on the identifier, because the #define wouldn't make sense otherwise.

In summary, I was unaware that DMU was a design goal, but I suspect it to make the language more difficult to learn, code harder to read, altough this effect might not be an issue for experts.

I am not trying to convince anybody that DMU is "bad", but I am interested in the actual properties. Everybody making claims for readability owes the community an empirical evaluation. Disclaimer: I am currently working on a research project about program comprehension, including some eyetracking and FMRI work, providing exactly such evaluations. :)

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

#160
post #21

The equivalent in C# always made more sense to me (not comparing memory or allocation model between the languages, but simply syntax in relation to a person reading it): int[] arr = new int[5]; // C# int arr[5]; // C The fact that the brackets go on the datatype always made more sense to me, after all, I want to refer to memory of a certain cell size (as indicated by int). I realize that there is a lot of stuff going…

It was always puzzling to me why people stick to char *foo instead of char* foo foo is obviously a pointer to the char, not a char. It has different size and behavior.

I agree.

Simply put, it would sense to provide

    #define CharPointer char*
and then declare

    char c = 'a';
    CharCointer p = &c;
To me this became clear when I did some C# interop, where IntPtr is a common class, such as:

    [DllImport("user32.dll")]
    static extern IntPtr CallWindowProc(WndProcDelegate lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
It's just an object oriented pointer interface. It looks different. I agree:

> It has different size and behavior.

Post reply on HN