29

I'm asking specifically about C, not about other contemporary languages, but if the reason is "that's how B did it" or something please assume I'm talking about "in the lineage of C". I'm also aware that many if not all languages also adhere to the rule that only a single thing can be returned from a function, but again I'm interested mainly in why C does it.

A C function returns only a single thing, whether it be a fundamental data type or a pointer. I don't think there is any theoretical reason why it couldn't return more than one thing, and perhaps put all return values on the stack. So I'm wondering if there was a technological reason that the single return value rule was instated? If not, what was it?

**EDIT: by "single thing" I mean either a single variable of a fundamental data type or a pointer to something else. This is as opposed to returning a tuple, say:

(int x,char* y) = func(int a,double b);
21
  • 14
    I suppose it's important to understand what you mean by "single thing" here. A pointer to an array or struct may point to something that contains many things but is still a "single thing".
    – jwh20
    Commented Feb 1, 2023 at 19:05
  • 7
    Maybe "complex type" would be a more accurate term, as K&R C did not allow you to return e.g. structs or unions either. Commented Feb 1, 2023 at 19:12
  • 6
    @JonathanPotter K&R C did allow to return structs.
    – Leo B.
    Commented Feb 1, 2023 at 20:42
  • 8
    Plus the developers of C weren't programming language mavens, even given the understanding of programming languages and models at the time. They were trying to develop a useful computer system so that they could do their other interesting research. They needed an OS (which became Unix) and they needed a language higher than assembly to code it and their real applications in (which became C). But those weren't the end goal, and there's only so much time and so many people on your research team to work on stuff ...
    – davidbak
    Commented Feb 1, 2023 at 20:45
  • 12
    "This is as opposed to returning a tuple." It's interesting that you used a singular noun to describe what is being returned here. Even when discussing multiple values, it feels natural to group them into a singular item (effectively "the collection of returned values") for the purposes of communication.
    – Flater
    Commented Feb 2, 2023 at 4:53

10 Answers 10

55

The premise of the question is incorrect.

In the first edition of the C Programming Language book (thanks to @JonathanPotter), the authors mention the intent to support passing structs to functions and returning them from functions, along with assigning them to one another:

6.2 Structures and Functions

There are a number of restrictions on C structures. The essential rules are that the only operations that you can perform on a structure are take its address with &, and access one of its members. This implies that structures may not be assigned to or copied as a unit, and that they can not be passed to or returned from functions. (These restrictions will be removed in forthcoming versions.)

(note the parenthetical). This means that C was designed to return values of composite types from functions, but it was not considered essential to support them in the early releases of the compiler.

Indeed, while the C compiler in UNIX V5 does not compile the program below due to a bug (that is, it fails with an internal error message rather than a syntax error or a semantic error), and the V6 one says Unimplemented structure operation, the one available in BSD 2.9 happily compiles it correctly.

struct s { int a, b; };
struct s foo() {
    struct s r; 
    r.a = 5;
    r.b = 25;
    return r;
}
main() {
    struct s a;
    a = foo();
    printf("%d %d\n", a.a, a.b);
}

prints 5 25 as intended. Moreover, it does not happen by chance, as the assembly code contains an explicit copy of the function result into the memory location of the variable a, then its fields are pushed to the stack to be printed:

jsr     pc,_foo
mov     (r0),-12(r5)
mov     +2(r0),-10(r5)
mov     -10(r5),(sp)
mov     -12(r5),-(sp)

That is, the underlying C language mechanisms are sufficient to return arbitrarily complex structures. Writing functions returning ad-hoc tuples like

(int x,char* y) = func(int a,double b);

rather than values of predeclared types, would be an example of syntactic sugar.

17
  • 2
    Is the ability to return a struct just syntactic sugar for having a function accept a pointer to a struct as an argument, and maybe return that pointer, or are there cases where it would offer some performance advantage?
    – supercat
    Commented Feb 1, 2023 at 20:51
  • 1
    ... and, separately, disjointly, digressively, noting that C++ has semi-recently introduced structured binding that ends up looking quite a bit like I imagine a call to the ad hoc tuple function hypothesised would look.
    – Tommy
    Commented Feb 1, 2023 at 20:57
  • 2
    @LeoB. From the 1974 C Reference Manual: "Not all the possibilities allowed by the syntax above are actually permitted. The restrictions are as follows: functions may not return arrays, structures or functions, although they may return pointers to such things;" I'm not sure what "stack reuse" would be facilitated by struct-returning functions as compared with having "user code" declare an automatic-duration object and pass its address if a temporary object is really necessary, or pass the address of whatever structure should be populated by a function.
    – supercat
    Commented Feb 1, 2023 at 21:32
  • 1
    I know that OP didn't want to be told that it was because predecessor languages also had that restriction, but I think it's worth suggesting that C was somewhat more heap-oriented than older languages and as such the developers might have been far more relaxed about returning pointers to newly-allocated storage. If only they'd initiated a robust culture of /always/ documenting whose responsibility it was to free memory once it was no longer needed... Commented Feb 2, 2023 at 21:05
  • 3
    @Invizio The premise is A C function returns only a single thing, whether it be a fundamental data type or a pointer. That is plainly wrong.
    – Leo B.
    Commented Feb 3, 2023 at 3:18
16

There is obviously no technical reason why C could not have returned a "complex" type. Using your example, (int x,char* y) could have been a returned.

But at what cost? Please remember the state-of-the-art in 1972 where the computer that first ran the first C compiler was the PDP-11. The language was designed to be simple to implement and performant. Every feature you add to the language adds complexity and impacts performance. I'm sure they believed that having complex return types was not worth the cost.

As a C developer is it really so much more difficult to return a pointer to a struct or other complex data vs. the struct or data itself? Not really in my opinion.

Since there are other languages today that do return complex data that proves that C could have done it as well but the developers of the language either decided not to support that or (more likely in my opinion) didn't even consider it because it didn't fit with their vision for the language.

I think we sometimes assume that C was carefully designed from a detailed specification but that is certainly not the case. It was a quick-and-dirty project that was done to meet an immediate need for the research these guys were involved with.

11
  • 2
    C compilers for PDP-11 are perfectly able to handle functions returning structs.
    – Leo B.
    Commented Feb 1, 2023 at 20:37
  • 2
    @LeoB.: They gained that ability some time after the 1974 C Reference Manual was written.
    – supercat
    Commented Feb 1, 2023 at 21:35
  • 1
    @supercat Who cares about 1974? From Wikipedia: In 1978, Brian Kernighan and Dennis Ritchie published the first edition of The C Programming Language. This book, known to C programmers as K&R, served for many years as an informal specification of the language.
    – Leo B.
    Commented Feb 1, 2023 at 21:55
  • 2
    @LeoB.: The fact that later C compilers added the ability to process struct return types as syntactic sugar doesn't imply that C wasn't designed initially to return a single non-aggregate object, nor that the PDP-11 didn't handle such returns much more efficiently than it could handle returns of aggregates.
    – supercat
    Commented Feb 1, 2023 at 23:21
  • 1
    @supercat Trying to deduce after the fact how C was "designed", is fruitless. Was C specifically "designed" to be ambiguous in its syntax of a=-b? Was it "designed" not to allow unsigned types? Declaring functions returning structs was not diagnosed as a prima facie user error even in 1974; thus I conclude that the intention to allow it was there from the beginning, but the implementation came a couple of years late.
    – Leo B.
    Commented Feb 1, 2023 at 23:46
13

On most platforms, expression evaluation would require having someplace reserved to put the result; simple compilers would always use the same CPU register or combination of registers for computations whose result is used for any purpose other than direct assignment to an register-qualified object.

If evaluation of an int expression always leaves the result in register 0, and the platform's process of handling function returns wouldn't disturb that register, then all the code generation for return X; would have to do, within an int function, would be to output code to compute the value of the associated expression in usual fashion (which would leave the value in R0) and follow that with a return instruction. An expression which calls a function that returns int would be generated by outputting a function call instruction and then assuming that the value of the function call expression had just been generated.

An essential thing to note about this approach for returning values is that the register used to hold the return value generally wouldn't be able to usefully hold anything else if it weren't used for that purpose, so returning a value in this fashion is "free".

While compilers fairly quickly added support for returning structures, this was often syntactic sugar for having callers create a structure on the stack and pass its address as an extra "hidden" argument, and then having a return statement in the called function copy data from whatever structure was used in the return statement to buffer supplied by the caller. Unlike simple-value returns, this approach would require allocating stack storage that otherwise wouldn't be allocated, and thus isn't "free".

18
  • how many registers did the pdp-6 have? the -11 already had several registers that could have been used for return values, if needed. (at the time I was using pdp-8s and similar machines that had 1 or 2 registers (A and B) at the most.
    – davidbak
    Commented Feb 1, 2023 at 21:15
  • @davidbak: Registers that were specified for returning values would only be usable for register-qualified objects if their values were stacked across function calls.
    – supercat
    Commented Feb 1, 2023 at 21:25
  • Didn't early versions store arguments in specific memory locations, and forbid recursion? I don't see why they couldn't also store return values in specific locations. Commented Feb 1, 2023 at 23:52
  • 1
    Good answer, finally one that distinguishes returning a single struct object vs. multiple primitive types where the caller might only want to use one. e.g. memcmp would be more useful if it returned the mismatch position in another register, as well as the comparison. A struct would usually get returned by hidden pointer, adding latency, unless a C ABI allows returning structs of 2 objects. (I guess if standard library functions did that, ABI designers would be motivated to support it more efficiently.) Commented Feb 2, 2023 at 16:13
  • 1
    So to some degree this is an ABI design issue, but it still seems clunky to need to declare a struct type for every function that wants to return 2 things. (Supporting arbitrary amounts of return objects would be extra ABI complexity, and a limit of 2 would be arbitrary.) Historically the evolution of C with active use in its primitive forms led to bad designs of some library functions sticking. (Especially string functions like strcpy.) memcmp not returning a position is more understandable, but unfortunate these days when SIMD searching can go 32x faster than scalar. Commented Feb 2, 2023 at 16:18
13

Yes, prior to ANSI C, there was a technological reason. Valid code could corrupt the stack.

Prior to ANSI C, function declarations were optional. According to Bell Labs, if you called a function that had not yet been declared, the caller should assume that the return type was int. In their C Programmer's Handbook, p. 27, they warned that this could result in nonsensical results when the actual function definition had a different return type:

C Programmer's Handbook page 27

Examples --

  • Correct
extern double linfunc();
float y;
y = linfunc(3.05, 4.0, 1e-3)

The value of the function call is properly converted from double to float by the assignment to y.

  • Incorrect
float x;
float y;
x = 3.05;
y = linfunc(x, 4, 1e-3)

This is wrong because types of arguments do not match declarations in definition, that is x is float not double, 4 is int not double. The result is that arguments passed on the stack are the wrong size and format, so that arguments taken off the stack by the quadfunc [sic, should read linfunc] are nonsense. There is no predictable return value. Also, unless the type of linfunc is declared, it is presumed to be type int. Thus even if the linfunc() did return a meaningful double, the value of the function call expression would be a nonsensical int value (e.g., upper half of a double).


The "single things" (technical name: scalars) that a function can return have the nice property that they can fit entirely within the register space of most processors. You don't need the stack; you just put the return value in one or more registers.

A compound object such as a struct or array might not fit within a processor's register space, requiring them to be placed on the stack. For this to properly work, the following must happen:

  1. The caller must reserve space on the stack for the compound return value (in addition to the arguments also placed on the stack).
  2. Control then transfers to the called function, which then reads the arguments and writes the compound return result to the stack.
  3. Upon return, the caller then reads the return result from the stack, then discards the memory allocated to arguments and return value.

Getting back to pre-ANSI C, what happens if you call an undeclared function that intends to return a compound object?

  1. The pre-ANSI behavior is for the caller to assume that int is being returned, thus no space (or the improper amount of space) is allocated on the stack for the return value.
  2. Control is transferred to the called function. It tries to read arguments from the stack, which as noted above, might produce nonsense values. However, the called function will also try to store the compound result on the stack, which was not allocated by the caller. This will result in a corrupted stack.
  3. With a corrupted stack, control may not return to the caller or its predecessors, crashing the program.

The easiest way to avoid this problem was to not allow compound objects as return values. Later compilers required function declarations, which is a better way of solving the problem.

6
  • 3
    All this is true yet I think the first sentence, last paragraph is off the mark: "The easiest way to avoid this problem was to not allow compound objects as return values." Go back in time to that period - I think the easiest way to avoid the problem was to not think of it as an issue to be solved at all! Functions in all programming languages at that time - common ones anyway, not LISPy ones - returned one value via a function return (in a register) and used out parameters for all other results. That's just the way it was, everybody thought that way.
    – davidbak
    Commented Feb 3, 2023 at 5:08
  • 1
    The obvious solution was to define those functions using ‘extern’.
    – RonJohn
    Commented Feb 3, 2023 at 5:29
  • Still, struct returns were a) promised and b) implemented way before ANSI C.
    – Leo B.
    Commented Feb 3, 2023 at 9:06
  • @RonJohn: Especially because the implicit int doesn't work very well. It didn't work on DOS (most of the time) and it doesn't work now on x64.
    – Joshua
    Commented Feb 3, 2023 at 19:48
  • Calling an undeclared function that returns a type other than int has always been erroneous. That erroneous programs can produce all manner of ill effects continues to this day to be an explicitly acknowledged characteristic of C. That's not a technological reason for a function to return only "a single thing", especially when we accept functions returning a double as their single thing. A double doesn't necessarily fit in a CPU register, and usually does not fit in space the size of an int. Commented Feb 4, 2023 at 17:39
6

Preface: There is no single, hard reason, but rather a series of good decisions.

Was there any technological reason that C was designed to return only a single thing from a function?

Sure. C is intended as simple language. Simple does not mean easy, but simple to implement with the least amount of language constructs possible.

For result passing the most simple thing to do is restricting it to a single value. This is what nearly all computers can do, what nearly all languages provide as minimum consensus and - maybe most important - what was already standard on the machines C was developed on: Returning a function's result in R0.


I don't think there is any theoretical reason why it couldn't return more than one thing,

You mean beside complexity? You mentioned the lineage of C. The eventually most important aspect of that evolution was to drop everything that is not absolutely required. The important distinction between a function and a (sub) procedure is that a function returns at least one value. The ability to return more than one value is not simple, thus unnecessary.

Likewise any syntax for multiple return values (like shown in your pseudo-code) would have added complexity to the compiler. This complexity would not really bring any benefit that couldn't be gained by other means - like passing pointers to variables, or, even better, using a structure and exchanging pointers.

and perhaps put all return values on the stack.

Three reasons:

  1. Compatibility

    At that point it's important to remember that basic calling on the PDP-11 (*1) used to return any result value in R0. Sure, C could use parameter return on the stack. Except that would mean C programs could only call C functions/procedures. Any external function would need an assembly wrapper - or a compiler/linker extension to change parameter passing depending on the type of function called.

    C was not developed to write Unix, but to rewrite it. So linking to existing code was mandatory.

  2. Runtime Memory

    Here lies another important simplification: Procedures are just functions that don't return anything - or better whose return value is ignored. If return value(s) would be handled using the stack, each call would have to allocate memory for that return value.

  3. Execution Speed

    Each and every memory operation slows execution. Returning via stack means that any result value needs to be moved into that location first. And after returning from that location, storing a value at the end of a function in memory, that will be most likely be loaded right after returning again means two superfluous instructions to be executed without any gain.

  4. Code Bloat

    Of course additional instructions also need additional code space. So returning a value via the stack means 2 instructions or (at least) 4 additional bytes.

All of this has to be seen in context of a PDP-11/20 with a main memory of only 64 KiB (256 KiB with MX11 - not sure if the one used by Ritchie had that).


*1 - The way PDP-11 subroutine calling is implemented is further complicated by how C implemented it.

4
  • 1
    On a CPU with more than 2 registers, two return values could easily be returned in registers. It's just a matter of designing a calling convention to support it. The "primary" return value can still go in R0, and a secondary return value (like a mismatch position from memcmp or strcmp) can go in another call-clobbered register; functions like memcmp will naturally end with that value in a register anyway. Obviously handling this would take more complexity for the compiler, so might have been problematic at the time. Commented Feb 2, 2023 at 16:24
  • 1
    @PeterCordes yes, a lot can be done (PDP-11 hat 8, but 3 are already been taken by default). Just look at some really nice languages. Except, C is not about being nice, but providing the necessary minimum.
    – Raffzahn
    Commented Feb 2, 2023 at 16:43
  • 1
    Oh, I see all 4 of your three (cough) reasons are in a section replying to the OP asking about returning on the stack, rather than as primary reasons why it's a problem to return 2 registers. I had been going to say those only apply with a crappy ABI / compiler incapable of returning two values in registers that are call-clobbered anyway in the asm calling convention. But that's the simplicity argument you make in the first half of the answer, which makes sense for early compilers on cramped systems. Commented Feb 2, 2023 at 16:56
  • @PeterCordes yes, in the end all of that can be derivative once one accepts the main reason of C being as minimalist as possible while still covering all basic cases. Spelling them out does make understanding more easy to anyone not investing mas much time to think about.
    – Raffzahn
    Commented Feb 2, 2023 at 16:59
4

Is simplicity a technological reason?

Usually, C functions parameters are pushed into the stack before call and then flushed after by the calling procedure (PASCAL is different, the callee pops its own parameters). The stack is one-way.

If possible, the return value is passed in a register, "accumulator", R0, EAX or their floating-point equivalents.

5
  • It may be sufficient, yes. I am wondering if there is anything specific happening on the computers that C was developed for that necessitated it, or it really was just a choice for simplicity. As one of the comments pointed out, a function that returns a single thing can easily be used in an if statement. However, I am looking for a firm source rather than a well-reasoned idea :) Commented Feb 1, 2023 at 19:27
  • but they wouldn't have chosen to pass parameters and return values in this way if there could be more of them. The stack is not one-way. Commented Feb 1, 2023 at 23:53
  • Modern calling conventions return structs as large as 2 registers in two registers, not memory. godbolt.org/z/14efTbn53 shows x86-64 returning in RDX:RAX and AArch64 returning in x1:x0, for a pointer + integer return value. (Like memcmp or strcmp could have used, to not throw away the mismatch-position information.) If you only need one of the return values, if (foo().first < 1). Or if C had a notion of a "primary" default return value if you just do foo() < 0, and an optional secondary return value you could also grab. Commented Feb 2, 2023 at 16:33
  • 2
    If C had been designed with multiple return values, C calling conventions / ABIs would have evolved to allow returning in multiple registers, similar to how calling conventions pass args in multiple registers. Commented Feb 2, 2023 at 16:33
  • @user253751 C was developed on a pretty low-spec system. That’s reason enough for a “high level assembly language” to keep things simple. Mathematical functions return one valve, that’s what they knew functions to be, so that’s what they did.
    – RonJohn
    Commented Feb 3, 2023 at 5:34
4

C does support returning multiple values - using out parameters.

int arg_a;
int out_r;
f(arg_a, &out_r);

It seems obvious nowadays that a function consists of an argument list and a result, which then poses the question why there is no "result list".

The latest iterations of mainstream languages like C# do indeed have a result list, with accompanying syntax to define the result list in the function header, and to assign the results to multiple variables at the call site.

int arg_a = 1;
int arg_b = 2;
(int out_r, int out_s) = f(arg_a, arg_b);

However, back when C was first designed, it might not have seemed obvious why a function should even have a result as well as an argument list, when the argument list alone is capable of meeting all relevant needs.

In fact, a single pointer argument, passing the address of a struct, is capable of shuttling as many inputs and outputs as the programmer may find necessary.

struct f_args {
    int arg_a;
    int arg_b;
    int out_r;
    int out_s;
};

...

f_params fa;

fa.arg_a = 1;

...

f(&fa);

The answer to the question is really found in considering why a single pointer argument is not generally considered enough.

Computational efficiency

The first issue is computational efficiency. Passing multiple values in registers is more efficient than just passing a pointer in a register (or on the stack) and letting the callee unpack the contents.

One of the benefits of C at the time of its conception, compared to assembly, was that the compiler was capable of selecting and using registers appropriately for the purpose of shuttling values into and out of functions - using the full complement of registers furnished by whatever hardware was in use - without the programmer being hassled by the task of register selection as they would be when writing assembly.

Therefore, passing values in as individual items (rather than as a pointer to a struct) has a real bearing on efficiency.

Typical balance of arguments and resultants/composition into expressions

It's also common in practice that functions take multiple arguments but return a single value. All basic arithmetical operators take two arguments and return one value - dyadic and monovalent. Standard mathematical expressions also rely on all evaluations being monovalent.

Modelling this traditional mathematical style (without attempting to innovate it), and the de facto reality that many functions have multiple inputs but only one output, is therefore what strongly justifies the specific configuration of a multi-value argument list, and a single return - polyadic and monovalent.

Again to compare with assembly, expressions were a big jump in functionality - and there must be at least one return value (as distinct from merely an out parameter) to allow composition of functions as part of expressions.

Syntactic ease

The practice of defining the argument list inline with the header (as opposed to defining a struct), and of composing values together inline as part of the call (as opposed to assigning values into a struct as a preparatory step before a call), has been found to be a good usability feature.

Although the inline composition has always been present in C, the ANSI C syntax for actually defining a function (and its arguments and argument types), does not correspond with the original style in K&R C.

A style of function definition that nowadays seems standard across many programming languages, was not a settled question at the time C was originally designed.

Mathematics uses functions, but it had no corresponding practice or syntax for defining type constraints (or even any explicit concept of "type" as programmers know it), so there was additional design work required on the syntax.

Conclusion

It's at least for the above reasons why C came to have a polyadic-monovalent style of function, as opposed to either the bare bones of what is necessary (which is one argument and no return value - monadic and nonvalent), or some other style.

The existence of the return value at all, is primarily driven by its compelling use as part of expressions. Multiple return values do not exist, because how to integrate them usefully into expressions is not even clear today, and certainly wasn't on the agenda in 1972.

Certainly, modern functional languages have operators which can select individual values from amongst multiple results of a previous stage, but there's often a massive loss of explicitness and obviousness.

Meanwhile, I suspect in C# that the predominant use of multiple return values, is not for use within expressions - in other words, multiple returns are used for completely different (and far less important) purposes than what single returns are typically used for.

And the long-time existence of a multi-value argument list (as opposed to just a single value), besides aligning with the existing mathematical practice, is primarily because it saves the hassle of always defining and assigning a separate structure, and because it is capable of catering to the need for multiple output parameters as well as multiple inputs.

3

There's also nomenclature: C calls these functions, like the mathematical concept of a function. Mathematical functions return a single type of value even if that's a tuple or something more complicated. So functions returning a single thing matches the metaphor of what a computer operator would have expected out of the f(x) notation used. If they were 'subroutines' maybe they would have had no return code at all and only in-parameters and out-parameters.

3

DrSheldon's answer is right in that implicit function declarations require a canonicalized way to return a fixed amount of values, which was set to one. And this is a technological reason, albeit on the language level. He has my upvote for it.

As a matter of fact, it is just as easy to define an ABI that allows for arbitrary return values as it is to allow for arbitrary parameters. The available mechanisms are exactly the same: First you can define some registers to hold the first few values, and just as larger values are passed on the stack, the caller can reserve stack space to return larger values.

As such, I believe that the real reason for not allowing more than one return value is the same as for modern languages: Virtually all languages use the notion that a function call has a value just like a variable name or some expression does. C is no exception to this. If you have a function that returns multiple values, you are only able to use a single one of these values implicitly, or the tuple of return values as a whole.

Today, some languages have adopted tuple unpacking in an assignment to work around this problem. But the problem persists: Where you can use a single return value right in the middle of any odd expression, you must first assign multiple return values to something in a tuple assignment. Needless to say, C does not have tuples. It has struct, and you can indeed define a struct for a function return. However, you need to give that struct a name, both for the type and the variable to which you assign the function result. It is doable, but requires some boilerplate code.

So, the real technical reason to not implement multiple return values has nothing to do with the hardware. It is a problem of language design that persists today, and even the most modern languages can only ever work around it. These workarounds introduce complexities into the compiler that must have looked quite unnecessary, and most certainly not simple, to the people who developed C.

4
  • On multiple results, F# for example is capable of piping the results list of one call as the argument list of the next, without unpacking. It also has operators which select individual items from the results (and discards others). In my opinion, the problem with this approach is not a language design difficultly, the problem is that the presence of all the intermediary names are purged from the code, and everything becomes far too implicit. The design is a victim of its own success.
    – Steve
    Commented Feb 5, 2023 at 9:51
  • @Steve Well, piping the results into a function call as arguments is using the tuple of return values as a whole. Of course, this can be done. And I share your unease about dropping all intermediary names. However, as a former assembler programmer, I've frequently been annoyed by C's inability to return two values without fuss or pointer argument overhead. It's extremely straightforward to return a handful of results in assembler without touching the stack. Commented Feb 5, 2023 at 10:11
  • I'm not an assembly programmer myself, but I wonder whether the problem you're alluding to requires a careful fitting-together of how both the caller and callee use the registers? In other words, the use of registers has to be analysed and designed as a matching combination by the assembly programmer, an approach which would then make the C compiler's register-use analysis potentially a global problem for the entire program, rather than local to each function. Also, I gather your grievance is more about the computational inefficiency of passing a pointer, than about the syntactical weight?
    – Steve
    Commented Feb 5, 2023 at 12:39
  • @Steve Indeed, my grievance was about performance. As an assembler programmer, you have all the liberties that a compiler does. And if you say: This function accepts these three inputs in these three registers, and produces these two outputs in these two registers, that's how it is. All five in-/outputs behave exactly the same. Not so as a C programmer: The compiler can pass inputs in registers, but not several outputs. You are forced to accept at least two superfluous memory accesses plus an address taking instruction. And memory accesses are expensive even when the are cached. Commented Feb 5, 2023 at 18:25
-2

It is worth noting that C is a low level procedural language and does not really support Object Oriented programming.

This seems odd seeing that all the children languages of big-daddy-C brought OOP to the masses.

C was designed to deal specifically with hardware at a low level. It does this exceptionally well, but a lot of the niceties we have grown used to are absent in its implementation.

2
  • C has a syntax that is concise and consistent, and there are a lot of source-code related tools for that syntax. No need to reinvent the wheel when developing a new language, just add the new features you want, which is exactly what the children languages did.
    – DrSheldon
    Commented Feb 7, 2023 at 21:29
  • It's far from clear that this answers the question that was asked. Commented Feb 5 at 19:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .