31 ms·
I think the author misses the point of the phrase "C is just portable assembly". It does't literally mean that C is just like assembly, it's usually a shorthand
by edanm 16y ago
I think the author misses the point of the phrase "C is just portable assembly". It does't literally mean that C is just like assembly, it's usually a shorthand for saying the following things:
1. C is used in most contexts where you previously had to use assembly, and would use assembly if C didn't exist.
2. C is the language with the "lowest level" code imaginable.
3. What this means is, you can map almost any command in C into a specific command or a few specific commands in assembly.
That last line is the important part. C is optimized in the sense that every command in C will give you a deterministic amount of commands in assembly. When reading C code, someone who knows assembly can usually tell you what will happen in the compiled code. You won't see an operator which doesn't have a deterministic runtime or memory footprint. In fact, most of the questions on "Why C has this"/"Why C doesn't have that" can be answered exactly like that: you can't implement it with a deterministic set of commands.
In those senses, C is just like Assembly, at least more so than any other language around.
- wlievens 16y ago> 3. What this means is, you can map almost any command in C into a specific command or a few specific commands in assembly. Is that really true? It depends very much on your compiler and on the optimizations that it uses. I'd say that the level of optimization performed is proportional to the similarity between input and output. Straightforward translation makes for crappy optimization.
- edanm 16y agoThe important point is that a C command will result in a deterministic amount of actual commands. For example, C won't implement the "raise to nth power" operator (in Python, 210 means 2 to the power of 10, for example), because it can't be done with one assembly instruction, only with a loop. Even with optimizations, you can reason about your code as if every C command is one Assembly command, and you won't be far off (you can say that every C command is O(1) assembly commands). This is very different from other languages.
- 1amzave 16y agoNot sure I buy that. C has a division operator, despite the fact that plenty of CPUs (ARM, for a common example) don't have a divide instruction. Typically, the compiler generates a call to a runtime support library (e.g. libgcc) with division functions for various datatypes, and yes, those functions typically involve some looping.
- mansr 16y agoDividing one 32-bit number by another is a constant-time operation even if on some machines (e.g. ARM) it is implemented as a loop. This is what matters. Raising a number to the nth power is an O(log n) operation.
- jedbrown 16y agoDivision takes a data-dependent number of cycles on all x86-64 processors.
- baguasquirrel 16y agoThat's true, and it's also a bit of a nitpick. How many of us actually work on multiple architectures or with different C compilers? For most systems-level stuff, you are simply using gcc on x86 linux, with plain-vanilla glibc, so honestly, I know what that div op is going to map to. If you've been at this for a while, you know what to expect when you write a simple for loop vs some pointer arithmetic. And if you happen to be an ARM guy, you probably are familiar with all the quirks of your platform as well.
- jasonwatkinspdx 16y ago"every command in C will give you a deterministic amount of commands in assembly" This is most definitely not true on any modern compiler. The generated assembly depends on the program as a whole and may not correspond fragment to fragment in any readily comprehensible way. One example is a variable in C may correspond to a number of different storage locations at different points in the program graph, owing to SSA form (or individual optimizations that transform the program in a similar manner). Another example is how pointer aliasing affects generated code: copying arguments into temporaries before arithmetic can actually result in fewer assembly instructions as the compiler can determine no aliasing is possible. Add to this the more mundane and well understood optimizations like inlining, constant propagation, later passes of optimizations over the results of the prior and the result is that it's very hard to know exactly what assembly will correspond to any arbitrary part of a c program. Mapping memory locations to variables or assembly instructions to c program lines is not trivial. I know this seems like nitpicking, but programmers using the mistaken mental model of c being textual macros for assembly leads to poorly performing code at best, and a variety of security vulnerabilities at worst. I think if you actually need to know what's going on at the machine level it's very important to know that the c abstract machine is most definitely NOT what the real machine on your desk is doing.
- jrockway 16y agoAlso, like the article mentions, it has abstractions that you can't get rid of, like the stack.
- hackermom 16y ago"3. What this means is, you can map almost any command in C into a specific command or a few specific commands in assembly." Do you have any idea how wildly inaccurate that statement is? If you have no experience at all with assembly on any processor, or perhaps only on the x86 family, then I can understand your statement. It's still wildly inaccurate, though.