Live data from Hacker News

Pointers Are More Abstract Than You Might Expect in C

stefansf.de

71–80 of 267 posts

Re: Pointers Are More Abstract Than You Might Expect in C

#71

This scratches the surface of why I hope C slowly fades away as the default low-level language. C sounds simple when you look through K&R C. C lets you feel like you understand the stack, ALU and memory. A pointer is just an integer and I can manipulate it like an integer. But the reality is filled with a staggering number of weird special cases that exist because memory doesn't work like a simple flat address space;…

The following is purely my opinion. Rust might have gone too far the other way. Yes it strives to be a modern language, with a lot of functional programming features and "OOP" done right (aka no inheritance, simply method polymorphism). To me Rust is more a C++ replacement than a C replacement. A replacement for C should try to be as simple as possible while fixing C weak typing mess with strong typing for instance,…

While I am not a fan of it, D's BetterC mode kind of fits what you are looking for.

https://dlang.org/spec/betterc.html

It's kind of a subset of D language, without dependency on GC or runtime. It feels like cleaned up C. Of course without runtime, most of the standard library doesn't work, so you are on your own for the most part, but then, C doesn't really have that much of a standard library to begin with.

Re: Pointers Are More Abstract Than You Might Expect in C

#72
post #60

Earlier quoted context omitted.

That is said a lot, but what are actual desicions that are made to conform to the C model? What would be good ideas in CPU design that aren't made since they are not compatible with C? (I get it that CPUs have to support some common paradigms and use cases. For example, virtual memory / process isolation, maybe branch prediction things, or support for calling conventions. However, I don't think that is specific to C)…

Very speculative, don't take any of this as gospel: The very concept of "the stack" implies a C-style evaluation model. Having a single address space for both code and data mainly just gives an opportunity for optimisation to fail. Everything-is-mutable gives a lot of opportunity for optimisation to fail. The compiler wants the program in SSA form, it register-allocates to a bunch of reused names, only for the CPU mi…

The stack is one of those things that would fall under "support for common paradigms". I checked that PDP-11 (the machine on which C was created) already had a hardware stack - and probably even older machines. There are many other programming languages that have the concept of a call stack. Hardware stacks are just an optimization to support this paradigm, and you are not required to use it. Conversely, C does not require any hardware support for implementation of the call stack.

The main problem with hardware call stacks is that they can't be relocated if you need predictable performance and/or have pointers instead of offsets into the stack. That means they are bounded size. With 64-bit addresses that may be less of a problem, but still. Functional language do not need to use the hardware stack, so that shouldn't be an issue.

> Having a single address space for both code and data mainly just gives an opportunity for optimisation to fail.

I don't think C requires that in any way, but since void pointers are allowed to point to either code or data, representation of void pointers must be somewhat "compatible" with both address spaces.

> Everything-is-mutable gives a lot of opportunity for optimisation to fail.

Not a hardware issue. Also not everything is mutable, not even in C. But you need some mutability for performance even at more abstract levels.

Re: Pointers Are More Abstract Than You Might Expect in C

#73
post #53
post #38

Earlier quoted context omitted.

> memory doesn't work like a simple flat address space Can you expand on that? I thought that modern (non-segmented) memory does work like a simple flat address space, at least from the perspective of userspace programs. Isn't all the weirdness only a result of compiler optimisations?

Just to take one example: multisocket x86 is NUMA. There’s HW to paper over that fact, but the power/perf cost is quite high.

Also Epyc and (even more so) Threadripper have somewhat NUMA memory access in a single CPU.

Re: Pointers Are More Abstract Than You Might Expect in C

#75

This scratches the surface of why I hope C slowly fades away as the default low-level language. C sounds simple when you look through K&R C. C lets you feel like you understand the stack, ALU and memory. A pointer is just an integer and I can manipulate it like an integer. But the reality is filled with a staggering number of weird special cases that exist because memory doesn't work like a simple flat address space;…

> I think there's room for other languages to occupy a similar space but they're need to focus on no-std-lib no-runtime operation (not always the sexiest target).

You're describing the Zig language!

It aims to be as fast as C, and unlike most languages that say this is a goal, it means it. There's no "almost as fast as C if you ignore the garbage-collector and the array-bounds-checking", it's actually as fast as C.

Its author values the minimalism of C, but wants to create a far better language for doing that sort of work. [0]

They're doing surprisingly well. They even managed to make a faster-than-C (faster even than hand-tuned assembly) SHA-2 [1]

[0] https://andrewkelley.me/post/intro-to-zig.html , https://github.com/ziglang/zig/wiki/Why-Zig-When-There-is-Al...

[1] https://ziglang.org/download/0.2.0/release-notes.html , it's also mentioned somewhere in this talk https://youtu.be/Z4oYSByyRak

Re: Pointers Are More Abstract Than You Might Expect in C

#76
post #37

There's nothing surprising in the first example. Comparing the addresses of stack variables is undefined behaviour. The second one is more interesting: extern int _start[]; extern int _end[]; void foo(void) { for (int *i = _start; i != _end; ++i) { /* ... */ } } GCC optimized "i != _end" into "true". The kernel guys fixed this by turning "_start" and "_end" into "extern int*". I always thought [] was just syntactic s…

The confusion in the example is the choice of names "start" and "end". If we rename them "some_array" and "some_totally_different_array" the undefined behavior is clear.

Re: Pointers Are More Abstract Than You Might Expect in C

#77
post #55
post #44

Earlier quoted context omitted.

Actually, the headline article tells us that it optimized it to true. That affected some of my code from years ago, too; and another fix is to keep _start and instead of having another array called _end have an external size_t giving the number of elements of start and loop while i!=_start+count . In fairness to the compiler people there shouldn't really be anything surprising in the second example, either. There are…

Yup, I've mistyped "true" as "false". If you set "_start" and "_end" to addresses of different objects, then you're invoking undefined behaviour and all bets are off. But this should be perfectly valid IMO: extern char* _start; extern char* _end; void foo() { for (char* c = start; c != end; c++) ... } int main(int, char**) { char buf[128]; _start = &buf[0]; _end = &buf[128]; foo(); } Right?

The thing there is indeed not the validity, but the introduction of extra memory references and variables.

The code with the arrays compiles to immediate value loads with load-time fixups to the code; and if this is linking to an assembly language module the necessary assembly language is just some labels against the data.

The code with the pointers compiles to loads from data memory, with (in the more usual paradigm where the start and end are bracketing some vector of initialized data in the program image) load-time fixups to the initial values of the variables; and the assembly language has to be different, some variables with these labels whose initial values are the addresses of some other labels against the actual data.

Notice that the Linux Kernel developer in 2016 did not immediately appreciate the need to modify the concomitant assembly language and linker script stuff to match the change in the C language code.

The approach that I mentioned keeps the immediate value for the start, and uses a load from initialized data memory for the count. Other approaches are possible, but they need more work in the build process. For example: By generating the C/C++ language declaration and definition from a script or suchlike, one could calculate and declare the number of elements in the array, making it possible to use sizeof.

Re: Pointers Are More Abstract Than You Might Expect in C

#78
post #43

Earlier quoted context omitted.

Agree on the alarm bells with the pointer arithmetic. Disagree that gcc is doing the right thing here. The clang behavior (different comment in this thread) is much more sane: if the pointers happen to be the same, they compare as equal. If the pointers happen to not be the same, they compare as not equal.

Except clang's behaviour changes depending on the optimisation level. If you use -O1, then you get a different result.

Yes, because the pointers are different at -O.

As there are no guarantees as to the relative placement of auto/stack variables relative to each other, that is perfectly fine.

Re: Pointers Are More Abstract Than You Might Expect in C

#79
post #65

Earlier quoted context omitted.

That was the argument of C++ advocates in the 90s.

Considering that C doesn't have any good facilities for polymorphism, it's going to continue to be the argument of any language that succeeds C. Unless, that is, that successor language also doesn't have any good facilities for polymorphism, in which case we have Go as a fascinating real-world study in how people will incessantly demand them and deride the language for lacking them (which is saying something, conside…

At least C11 has light generics support.

Re: Pointers Are More Abstract Than You Might Expect in C

#80

Earlier quoted context omitted.

The following is purely my opinion. Rust might have gone too far the other way. Yes it strives to be a modern language, with a lot of functional programming features and "OOP" done right (aka no inheritance, simply method polymorphism). To me Rust is more a C++ replacement than a C replacement. A replacement for C should try to be as simple as possible while fixing C weak typing mess with strong typing for instance,…

> To me Rust is more a C++ replacement than a C replacement. People make this comparison a lot, but it’s not fair. Rust is far simpler, in terms of number of features of the language, to that of C++. It’s fair to say that Rust is far more expressive than C, but this just represents an initial learning curve that is higher than C, but not as high as C++. The type system is fairly simple in Rust, but it can be used to…

Why would you think that Rust is "far simpler" than C++? Exactly the same concepts (with extras on Rust's side because of the ML-like constructs) must be learned for both, except they're distributed differently on the learning curve.

And I wouldn't say that Rust's safer than Java either. Memory access errors are basically non-existant in Java and it has quite robust concurrency primitives and libraries.

Post reply on HN