Why doesn't C++ support something like int[] funcarray(){} ?
You can return an array, but it's a real hassle to make such a function. And also, I heard somewhere that strings are just arrays of char. So if you can return a string in C++, why not an array?

Why not create the array using a pointer then return the pointer?
–
RageDMar 1 '11 at 16:57

@RageD Sure, you could do that. But couldn't the creators of C++ make array returning functions that do that automatically without bothering the programmer?
–
LockheadMar 1 '11 at 17:03

You don't return a string from a function, you return a pointer to that string. If you return a std::string() object, there is some copy overhead you should become aware of.
–
Mark0978Mar 1 '11 at 17:08

1

@MisterSir: I would say it's more of a feature - it allows consistency. If you create an array using a pointer, you've dynamically allocated memory on the heap - that said, you can return by reference and remove any copy overhead (so size of an array does not effect efficiency). However, you do need to remember to free the memory you've allocated.
–
RageDMar 1 '11 at 18:25

5

@MisterSir - also, it's not bothering the programmer. C and C++ are not application programming languages. They are systems programming languages. As such, there are design decisions in these languages that reflect the intended type of work. Don't think high-level. Think low level. Go low, down to the metal. Review back the stuff we learned in assembly, computer org and operating systems. Then things will start to make much more sense when it comes to C and C++.
–
luis.espinalMar 1 '11 at 21:06

9 Answers
9

I'd wager a guess that to be concise, it was simply a design decision. More specifically, if you really want to know why, you need to work from the ground up.

Let's think about C first. In the C language, there is a clear distinction between "pass by reference" and "pass by value". To treat it lightly, the name of an array in C is really just a pointer. For all intents and purposes, the difference (generally) comes down to allocation. The code

int array[n];

would create 4*n bytes of memory (on a 32 bit system) on the stack correlating to the scope of whichever code block makes the declaration. In turn,

int* array = (int*) malloc(sizeof(int)*n);

would create the same amount memory, but on the heap. In this case, what is in that memory isn't tied to the scope, only the reference TO the memory is limited by the scope. Here's where pass by value and pass by reference come in. Passing by value, as you probably know, means that when something is passed in to or returned from a function, the "thing" that gets passed is the result of evaluating the variable. In other words,

int n = 4;
printf("%d", n);

will print the number 4 because the construct n evaluates to 4 (sorry if this is elementary, I just want to cover all the bases). This 4 has absolutely no bearing or relationship to the memory space of your program, it's just a literal, and so once you leave the scope in which that 4 has context, you lose it. What about pass by reference? Passing by reference is no different in the context of a function; you simply evaluate the construct that gets passed. The only difference is that after evaluating the passed "thing", you use the result of the evaluation as a memory address. I once had a particular cynical CS instructor who loved to state that there is no such thing as passing by reference, just a way to pass clever values. Really, he's right. So now we think about scope in terms of a function. Pretend that you can have an array return type:

int[] foo(args){
result[n];
// Some code
return result;
}

The problem here is that result evaluates to the address of the 0th element of the array. But when you attempt to access this memory from outside of this function (via the return value), you have a problem because you are attempting to access memory that is not in the scope with which you are working (the function call's stack). So the way we get around this is with the standard "pass by reference" jiggery-pokery:

We still get a memory address pointing to the 0th element of the Array, but now we have access to that memory.

What's my point? In Java, it is common to assert that "everything is pass by value". This is true. The same cynical instructor from above also had this to say about Java and OOP in general: Everything is just a pointer. And he's also right. While everything in Java is in fact pass by value, almost all of those values are actually memory addresses. So in Java, the language does let you return an array or a String, but it does so by turning it in to the version with pointers for you. It also manages your memory for you. And automatic memory management, while helpful, is not efficient.

This brings us to C++. The whole reason C++ was invented was because Bjarne Stroustrup had been experimenting with Simula (basically the original OOPL) during his PhD work, and thought it was fantastic conceptually, but he noticed that it performed rather terribly. And so he began working on what was called C with Classes, which got renamed to C++. In doing so, his goal was to make a programming language that took SOME of the best features from Simula but remained powerful and fast. He chose to extend C due to its already legendary performance, and one tradeoff was that he chose to not implement automatic memory management or garbage collecting on such a large scale like other OOPL's. Returning an array from one of the template classes works because, well, you're using a class. But if you want to return a C array, you have to do it the C way. In other words, C++ does support returning an array EXACTLY the same way that Java does; it just doesn't do all of the work for you. Because a Danish dude thought it'd be too slow.

Also, in response to the "Stings are arrays of characters" comment; This is mostly true. In C, there is no such thing as a String type; you handle it by yourself. They are stored in arrays of characters, terminated by a null, and while there exists a String library to do things like find the length and all of that, it is done by parsing the string. A String in C++ or Java can be thought of as a class that contains an array of characters but also contains other member fields that maintain information about the array like length so it is easier to manipulate. So back to pass by reference.
–
Doug StephenMar 1 '11 at 19:07

This is EXACTLY the answer I was looking for! Greatly improved my understanding of memory as well. Thank you!
–
LockheadMar 1 '11 at 20:04

4

Not again... arrays and pointers are different beasts this type of answers, even with the to treat it lightly qualifier only add to the confusion.
–
David Rodríguez - dribeasMar 1 '11 at 20:25

@David I'm not trying to add to the confusion, but I don't think that he was looking for a treatise on lvalues and pointer-array equivalency.
–
Doug StephenMar 1 '11 at 20:30

I also never said an array was a pointer. I said that the NAME of an array was a pointer. Which, while very semantically false, was just a short and non-technical way of saying that except for in very special circumstances, the NAME of an array of type T will decay in to a pointer of type T pointing at the first element, though it goes without saying that the name of an array is an unmodifiable lvalue. But sorry nonetheless. I understand your concern.
–
Doug StephenMar 1 '11 at 20:36

A std::string is a class but when you say a string you probably mean a literal. You can return a literal safely from a function but actually you could statically create any array and return it from a function. This would be thread-safe if it was a const (read-only) array which is the case with string literals.

The array you return would degrade to a pointer though, so you would not be able to work out its size just from its return.

Returning an array, if it were possible, would have to be fixed length in the first place, given that the compiler needs to create the call stack, and then has the issue that arrays are not l-values so receiving it in the calling function would have to use a new variable with initialisation, which is impractical. Returning one may be impractical too for the same reason, atlhough they might have used a special notation for return values.

Remember in the early days of C all the variables had to be declared at the top of the function and you couldn't just declare at first use. Thus it was infeasible at the time.

They gave the workaround of putting the array into a struct and that is just how it now has to remain in C++ because it uses the same calling convention.

Note: In languages like Java, an array is a class. You create one with new. You can reassign them (they are l-values).

If the size of the array is fixed at compile time, you can use the time std::array<X,N> (or std::tr1::array<X,N> or boost::array<X,N>).
–
ysdxMar 1 '11 at 18:29

1

A std::vector is not an array, and neither a struct containing one. Those are simply mechanisms to work-around the limitation on returning arrays (the actual native type, not a struc or object wrapper for it). I understand where you are going with it, and these are workable examples. However, these are neither examples of a feature (returning native type arrays) being supported by C++ (or C), nor explain why the limitation exists in C++.
–
luis.espinalMar 1 '11 at 20:39

@luis C++ uses the same calling convention as C. Arrays are not l-values in C or C++ which is the main issue.
–
CashCowMar 2 '11 at 11:23

Your example is still returning an invalid pointer to local memory -- without a copy constructor to do a deep copy, the 'd' member of the return value will be identical to the 'd' member of local variable 'res', which points to memory on the stack which no longer exists.
–
c-urchinOct 8 '12 at 19:10

Arrays in C (and in C++ for backwards compatibility) have special semantics that differ from the rest of the types. In particular, while for the rest of the types, C only has pass-by-value semantics, in the case of arrays the effect of the pass-by-value syntax simulates pass-by-reference in a strange way:

In a function signature, an argument of type array of N elements of type T gets converted to pointer to T. In a function call passing an array as argument to a function will decay the array to a pointer to the first element, and that pointer is copied into the function.

Because of this particular treatment for arrays --they cannot be passed by value--, they cannot be returned by value either. In C you can return a pointer, and in C++ you can also return a reference, but the array itself cannot be allocated in the stack.

If you think of it, this is not different from the language that you are using in the question, as the array is dynamically allocated and you are only returning a pointer/reference to it.

The C++ language, on the other hand, enables different solutions to that particular problem, like using std::vector in the current standard (contents are dynamically allocated) or std::array in the upcoming standard (contents can be allocated in the stack, but it might have a greater cost, as each element will have to be copied in those cases where the copy cannot be elided by the compiler). In fact, you can use the same type of approach with the current standard by using off-the-shelf libraries like boost::array.

Regarding "In a function signature, [arrays -> pointers]" "[therefore] they cannot be returned by value". 8.3.5.5 does require 'any parameter of type "array of T"' be adjusted to use a pointer, but there's no statement saying that treatment applies to return types as they're not allowed. Your explanation makes it sound like the treatment for parameters is applied to returned types and yields a then-invalid signature. That's not so - plain and simple, array return types just aren't allowed: 8.3.5.8 "Functions shall not have a return type of type array or function".
–
Tony DApr 12 '13 at 2:58

"You can't return array from the
function because that array would be
declared inside the function, and its
location would then be the stack
frame. However, stack frame is erased
when function exits. Functions must
copy return value from stack frame to
return location, and that's not
possible with arrays."

Downvote for copying verbatim from the link you're referencing. In addition, this answer is misleading. In particular "Functions must copy return value [sic]" is technically false, since functions can return references and pointers.
–
phoojiMar 1 '11 at 17:03

7

I don't see a problem with the quote, linked the reference.
–
OrbitMar 1 '11 at 17:05

@phooji: references and pointers are both pointers, which are themselves both values. There's nothing misleading if you understand what a pointer is.
–
InverseMar 1 '11 at 17:24

5

I cannot agree with this answer. For most other types you can return by value and there is no problem with the fact that the returned object is inside the function: a copy is made (or elided if the compiler manages to do so). That is a common behavior and the fact that the same cannot be done with arrays is more of a design decision in the C language --inherited in C++. As a matter of fact, if you enclose the array in a struct, that is exactly what would happen: the struct (including the internal array) will be copied in the return statement.
–
David Rodríguez - dribeasMar 1 '11 at 17:39

Other have said that in C++, one use vector<> instead of the arrays inherited from C.

So why C++ doesn't allows to returns C arrays? Because C doesn't.

Why C doesn't? Because C evolved from B, a untyped language in which returning an array doesn't make sense at all. When adding types to B, it would have been meaningful to make it possible to return an array but that wasn't done in order to keep some B idioms valid and ease the conversion of programs from B to C. And since then, the possibility of making C arrays more usable as always been refused (and even more, not even considered) as it would break too much existing code.

"making C arrays more usable... would break too much existing code" - not true. Existing progarms won't have compiled if they included functions returning arrays, so such features would only be relevant to new code choosing to use those functions and in no way invalidate existing code. Put another way, you're not postulating a change of existing behaviour, rather - it'd be new independent behaviour.
–
Tony DApr 12 '13 at 3:03

@TonyD, you'd need either to remove the automatic decay of an array to a pointer, and that will break lot of code, or make so many special cases that you haven't make C arrays more usable at all, or change so few things that it won't worth the pain.
–
AProgrammerApr 12 '13 at 6:42

interesting assertion. Please help me understand your specific concerns. For context, consider int[4] f() { int x[4]; ...populate x...; return x; } and to make that useful in an intuitive way, let's add a requirement for new support of assignment to arrays both in the return and ala int x[4] = f();. I don't see how any of this would require pointer decay, nor need to change other code to prevent pointer decay. What kind of code do you see conflicting with this?
–
Tony DApr 12 '13 at 7:55

@tonyd, if you don't change the current rules the result of f() would decay into a pointer (just like with int (*p)[4], *p decays into a pointer).
–
AProgrammerApr 12 '13 at 8:13

But when would it decay? - it only decays if the assignment isn't possible with the original type. Much like long x = get_char(); - the conversion to long is only attempted because the rhs operand to the assignment isn't already a long. So, what we're talking about is not some suppression of the pointer decay, but having something new work before it's ever considered. "(just like with int (*p)[4], *p decays into a pointer)" - not so, *p is still int[4] - confirmed by passing to template <int N> void f(int (&a)[N]) { std::cout << N << '\n'; }. The decay is last resort.
–
Tony DApr 12 '13 at 9:52

@MisterSir - No, there is no need. test is a variable residing on stack and goes out of scope on function return. However, the location test was pointing resides on heap/free store and is returned to arr. So, if you delete arr, that's sufficient.
–
MaheshMar 1 '11 at 17:42

"Why doesn't C++ support something like": Because it would not make any sense. In reference-based languages like JAVA or PHP, memory management is based on garbage collection. The portions of memory which have no references (no variable in your program points to it any more) is automatically freed. In this context you can allocate memory, and pass the reference around carefreely.

C++ code will be translated to machine code, and there is no GC defined in it. So in C and C++ there is a strong sense of ownership of memory blocks. You have to know if the pointer you go is yours to free at any time (in fact you shoud free it after use), or you have a pointer to a shared portion of memory, which is an absolute no-no to free.

In this environment you would win nothing with cretaing endless copies of an array every time it passes to and from a function. It is amuch more complex task to manage your arrays of data in c-like languages. There is no one-size-fits-all solution, and you need to know when to free memory.

Would an array returned by a function always be a copy (yours to free) or you have to make copies of them? Whet would you win by getting an array insted of a pointer to an array?

Return a std::vector<> instead of an array. In general, arrays do not work well with C++, and should generally be avoided.

Also, the string datatype is not just an array of characters, although a "quoted string" is. The string manages an array of characters, and you can get access to it with .c_str(), but there's more to a string than that.