15

I wonder why it is not possible to return array in C? After all, array is just a pointer backed by size info (to make sizeof work). First I thought this was done to prevent me from returning array defined on my stack, but nothing prevents me from returning pointer to something on my stack (gcc warns me, but code compiles). And I also can return string literal which is statically storaged array of chars. By the way, in lunux it is stored in .rodata, and const array is stored there also (check it with objdump), so I can return array (casting it to pointer) and it works, but AFAIK this is just implementation-specific (another OS/Compiler may store const on stack).

I have 2 ideas how to implement array returning: Just copy it as value (as it is done for structure. I even can return array wrapping it into structure!!), and create pointer to it automatically or allow user to return const array and create contract that such array should have static storage duration (as it done for strings). Both ideas are trivial! So, my question is why K&R did not implement something like that?

user996142
  • 2,753
  • 3
  • 29
  • 48
  • Returning a pointer to a local variable is a coding error though, even if it may compile. – juanchopanza Mar 16 '15 at 23:03
  • I understand that. That is why I have 2 ideas: store it in statically (as done for string literals) or just copying it (as it done for structures, ints, chars) – user996142 Mar 16 '15 at 23:05
  • 1
    Sure, it is technically possible to make arrays copyable and assignable like other built-ins, and it is annoying that they aren't. I often ask myself this. – juanchopanza Mar 16 '15 at 23:07
  • An array is not a first class type in C. It's just a bunch of "other" things. I am not even sure what you are asking... – Prof. Falken Mar 16 '15 at 23:08
  • You can return a struct, even if it contains an array. It *can* cause a lot of parameter/return traffic, though. – wildplasser Mar 16 '15 at 23:08
  • 3
    @Prof.Falken If I understand correctly, OP is asking why this is so. It is not uncommon for design decisions to have a reason. – juanchopanza Mar 16 '15 at 23:09
  • possible duplicate of [Returning an array using C](http://stackoverflow.com/questions/11656532/returning-an-array-using-c) – ely Mar 16 '15 at 23:13
  • 1
    @Mr.F Yeah, that is definitely not a duplicate. – juanchopanza Mar 16 '15 at 23:16
  • I guess there would be a problem with scope: on return, not only the object, but also its type & size have to be returned. The caller can only accomodate return types whose size is known in advance. – wildplasser Mar 16 '15 at 23:18
  • now you know why c++ has std::array – pm100 Mar 16 '15 at 23:18
  • @wildplasser No problem at all. The size of a fixed size array is as known in advance as the size of an `int` or a `double`. – juanchopanza Mar 16 '15 at 23:23
  • 1
    Because it's how [stack variables](https://en.wikipedia.org/wiki/Stack-based_memory_allocation) work. When you declare variable on stack -- you just increment your [stack pointer](https://en.wikipedia.org/wiki/Stack_register). When your function (or [scope](https://en.wikipedia.org/wiki/Scope_%28computer_science%29#Function_scope), for that matter) is finished, your stack pointer is being decremented back, and you basically lost your local variable (array in your case). – Sam Protsenko Mar 16 '15 at 23:23
  • 4
    @SamProtsenko Jeez. Is it not possible to return an `int`? Or a `struct` that holds an array? It is a *design decision*. There is no technical reason it can't be done. – juanchopanza Mar 16 '15 at 23:24
  • 2
    @SamProtsenko In the C standard, there is no mention of a *stack* to pass and return function arguments and returns. (most implementations do use a stack, though) – wildplasser Mar 16 '15 at 23:27
  • @wildplasser: Yeah, it depends on particular architecture's ABI (or [calling conventions](https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI)). – Sam Protsenko Mar 16 '15 at 23:36
  • 1
    BTW: one practical restriction is than an array is not an assignable lvalue. So `int array[2]; array = func(args);` would not work anyway. So the real question is: why are arrays not assignable lvalues? – wildplasser Mar 16 '15 at 23:36
  • 1
    @wildplasser: one simple explanation which comes to mind is that if that were allowed, programmers would use this feature often, because it's convenient. But it involves a lot of memory copying on big arrays, so language/standard creators are pushing us to use pointers more extensively this way (remember, C is a quite low-level language, and doesn't have references). – Sam Protsenko Mar 16 '15 at 23:40
  • 1
    @SamProtsenko: C doesn't strike me as a language that would introduce such constraints because programmers might use it to shoot themselves in the foot. :) – Dolda2000 Mar 16 '15 at 23:51
  • 1
    @Dolda2000: ok, how about [this explanation](http://stackoverflow.com/a/3438004/3866447)? – Sam Protsenko Mar 16 '15 at 23:58

4 Answers4

18

Technically, you can return an array; you just can't do it "directly", but have to wrap it in a struct:

struct foo {
    int array[5];
};

struct foo returns_array(void) {
    return((struct foo) {
        .array = {2, 4, 6, 8, 10}
    });
}

Why C doesn't allow you to do it directly even though it has the ability is still a good question, though. It is probably related to the fact that it doesn't support whole-array assignments either:

void bar(int input[5]) {
    int temp[5];

    temp = input;   <-- Doesn't compile
}

What makes it even stranger though, of course, is that whole-array copy via argument-passing is supported. If someone knows how to find the ANSI committee's decisions on the matter, that would be interesting to read.

However,

After all, array is just a pointer backed by size info (to make sizeof work).

This is not correct. There is no explicit pointer, nor any stored size, of an array. The array is stored as the raw values, packed together; the size is only known inside the compiler and never made explicit as run-time data in the program. The array decays to a pointer when you try to use it as one.

Dolda2000
  • 25,216
  • 4
  • 51
  • 92
14

An array is not "just a pointer backed by size info".

An array is a block of contiguous elements of a certain type. There is no pointer.

Since an array is an object, a pointer can be formed which points to the array, or to one of the array's elements. But such a pointer is not part of the array and is not stored with the array. It would make as much sense to say "an int is just a pointer backed by a size of 1 int".

The size of an array is known by the compiler in the same way that the size of any object is known. If we have double d; then it is known that sizeof d is sizeof(double) because the compiler remembers that d is an object of type double.

nothing prevents me from returning pointer to something on my stack

The C standard prevents you from doing this (and using the returned pointer). If you write code that violates the standard then you are on your own.

And I also can return string literal

A string literal is an array of char. When you use an array in a return statement, it is converted to a pointer to the first element.

To enable arrays to be returned (and assigned) by value, the rule regarding conversion of array to pointer (sometimes called "decay") would have to be changed. This would be possible, but K&R decided to make the decay almost ubiquitous when designing C.

In fact it would be possible to have a language like C but without having the decay at all. Maybe in hindsight that would have saved a lot of confusion. However they just chose to implement C in the way that they did.


In K&R C, it was not possible to return structures by value either. Any copy operation that was not a primitive type, had to be done with memcpy or an equivalent iterative copy. This seems like a reasonable design decision given the way hardware resources were in the 1970s.

ANSI C added the possibility to return structures by value , however by then it would have been too late to change the decay rule even if they had wanted to; it would break a lot of existing code which is relying on the decay rule.

M.M
  • 138,810
  • 21
  • 208
  • 365
2

The "reason" is that arrays decay to pointers in most expressions and things would "as wrong" as if you would want to allow for assignment of arrays. If you'd return an array from a function, you wouldn't be able to distinguish it from a normal pointer. If f() would be returning double[5], say, the initialization

double* A=f();

would be valid. A would take the address of a temporary object, something that in C only lives up to the end of the full expression where the call to f appeared. So then A would be a dangling pointer, a pointer that points to an address that is not valid any more.

To summarize: the initial decision to have arrays behave similar to pointers in most contexts, imposes that arrays can't be assigned nor returned by functions.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
2

Because if suddently, a revision of the language allows a function to be able to return a complete array, that revision should deal with these situations too:

  • Allow assignment between arrays (because if a function returns an array, it's because it is going to be assigned to an array variable in the caller function)
  • Allow passing a complete array as value parameter (because the name of an array is no longer a pointer to its first element, as this would conflict with the first situation)

If these constructions are allowed, existing programs that pass the name of an array as an argument to a function, expecting the function to modify that array, will cease to work.

Also, existing programs that use the array's name as pointer to assign it to a pointer variable will cease to work.

So, while it's technically feasible, making arrays to work as complete entities that can be assigned, returned and so on would break a lot of existing programs.

Note that structs could be "upgraded" because there were no prior semantics in the K&R C that related the name of a variable structure to be a pointer to itself. Any function that had to use structures as arguments or return values had to use pointers to them.

mcleod_ideafix
  • 11,128
  • 2
  • 24
  • 32