Hacker News new | past | comments | ask | show | jobs | submit login

Well, one of the interesting takeaways from the article is that C as specified often does not let you take advantage of your system architecture. The specification has things like GPUs and segmented memory architectures in mind when it forbids you from doing seemingly reasonable things like taking the difference between the addresses of two separately-allocated objects, even though chances are very good what you're trying to do works just fine on all architectures you care about.



Yes and no. The standard specification defines a C that is very portable. It is quite reasonable that you cannot portably take advantage of your system architecture, which means you cannot do it using C as defined by the standard.

But you still can write stuff that looks just like C, that a C compiler will accept, and that lets you take advantage of your system architecture. You just need to understand that a new version of your compiler can break all of your nifty tricks.


Finally someone that understands.


Not really because there are two kinds of c programer out there.

1. Those that communicate with the outside world exclusively via library calls to an operating system.

2. Those that do not.

People that live in world #1, usually don't get people living in world #2. Example.

  // wait for write ready
  while((spi.S & SPI_S_SPTEF_MASK) == 0)
    ;

  spi.D;
  spi.D = data;
Guess what,

No spi.D; is actually important. No just because the program never writes to spi.S does not mean you can delete the while loop. No you cannot reorder any of this and have it work.


Are you saying something other than "you, the programmer, must declare spi to be volatile, or else the compiler might lay a bear trap for you?".

Based on your comment it seems clear that you understand that... but given that you understand that I don't get the point you're trying to make (except perhaps that writing low-level hardware code in C often means actively stopping the compiler from screwing you over).

(for non-C programmers: "volatile" can be approximated by "hey compiler, this variable can change unexpectedly even if you didn't do anything to change it, so don't use any optimizations that assume you're the only one changing it).


Sorry, I have no idea what you are trying to say. Maybe stick to the relevant issue and be less abstract?


His example is anything but abstract. Embedded programming is a world unto itself. You sometimes need to read an address before you can write, etc.


I think the parent meant "implicit" rather than "abstract". The code is not abstract; but the pragmatic intent of the comment is not made very clear. I believe I follow but I'm not confident enough to risk confusing things by guessing.

(I believe I follow the intended conversational import; I certainly follow the code.)


Sort of the point is, in pure languages only the symbolic operations are important. And the side effects (memory operations) are unimportant and beyond the control of the programmer. So you can do all sorts of transformation on the code without effecting the end result.

C is definitely impure. There is a large set usage cases where the memory layouts are important. Where the orders of operation and memory accesses are important.

What I worry about is when the optimizer is allowed to make assumptions about undefined behavior, that may be actually important on certain targets. Consider referencing a null pointer.

Winows7, Linux, in user land if you do that and you'll get a seg fault. And if the default handler runs your program dies.

The ARM cortex processor I've been slopping code for, reading a null pointer returns the initial stack pointer. Writing generates a bus fault. Since I actually trap that, and error message gets written into non-initialize memory and then the processor gets forcibly reset.

On an AVR reading a null pointer gives you the programs entry point. Writing to that location typically does nothing. Though you can write to it if running out of a special 'boot sector' and you've done some magic incantations.

So that's the problem I have with the idea that 'undefined means the optimizer can make hash of your program' instead of trusting the back end of the compiler will do something target appropriate.


Yes, embedded is very side-effect-ful. When you have to make that kind of thing work, you usually have to know both your hardware architecture and your compiler.

  *(unsigned long*)0xEF010014 = 0x1C;
presumes memory mapped I/O, a 32-bit hardware register at 0xEF010014, and what 0x1C will mean to that register. It also presumes that the compiler will generate a 32-bit access for an unsigned long.

Going back to Gibbon1's code sample, you have to know what your compiler is going to do at what optimization level. You either have to declare spi.S to be volatile, or you have to compile at -O0, or some such. And you may well need to review the generated assembly code a few times in order to really understand what your compiler is doing with your code.

If you're writing the Linux kernel and you want to be portable across hardware architectures, it gets harder. You can't just be processor- or hardware-specific. You probably need the volatile keyword. I don't know what the kernel compiles in terms of optimizations, but I bet it's not aggressively optimized.


His comment is abstract. A fragment of correct code doesn't magically change that.


Why? That's the issue that people should solve.

Disclaimer: I have no C experience.


Ive worked on embedded systems where stuff like that would be a problem, howver I also knew the memory map for my target.

I wanted to make some crypto run faster so i used memcpy on a pointer to a function to copy executable code from slow flash to fast ram.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: