开发者

Why doesn't C++ support functions returning arrays?

Some languages enable you to just declare a function returning an array like a normal function, like Java:

public String[] funcarray() {
   String[] test = new String[]{"hi", "hello"};
   return test;
}

Why doesn't C++ support something like int[] funcarray(){} ? You can return an array, but it's a real ha开发者_运维百科ssle 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?


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:

int* foo(args){
    int* result = (int*) malloc(sizeof(int)*n));
    // Some code
    return result;
}

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.


C++ does support it - well sort of:

vector< string> func()
{
   vector<string> res;
   res.push_back( "hello" );
   res.push_back( "world" );
   return res;
}

Even C sort-of supports it:

struct somearray
{
  struct somestruct d[50];
};

struct somearray func()
{
   struct somearray res;
   for( int i = 0; i < 50; ++i )
   {
      res.d[i] = whatever;
   }
   // fill them all in
   return res;
}

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).


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.


"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."

From a discussion here:

http://forum.codecall.net/c-c/32457-function-return-array-c.html


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.


You can return a pointer to the array. Just be careful about releasing the memory later.

public std::string* funcarray() {
    std::string* test = new std::string[2];
    test[0] = "hi";
    test[1] = "hello";
    return test;
}

// somewhere else:
std::string* arr = funcarray();
std::cout << arr[0] << " MisterSir" << std::endl;
delete[] arr;

Or you can just use one of the containers in the std namespace, like std::vector.


"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.


Check out here. Really helpful.

  • How do I return an array from a function?
  • C++ Returning multidimension array from function
  • Return 2d array from function in C++


These answers are all missing the point. C++ just doesn't support it. It didn't even support a way to return a statically-sized array before std::array<T, N>. C++ could support returning even dynamically-sized arrays, but they don't. I'm sure there are defensible reasons why, but they could.

All you need to do is allocate the dynamic array on the stack, return the address and size of it, and make sure the caller bumps the stack pointer up to the end of the returned array. Possibly some stack frame fixing to do, but by no means impossible.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜