2

Which is better? I noticed the latter is used in a lot of C code. People will typically malloc an array, and then pass that as a parameter to a function, which will then populate it. Whereas in Java, the former seems more popular.

Is one better than the other? If not, do language features invoke a preference?

1
  • 1
    you may want to give a read to How is a Java reference different from a C pointer? "Usually Java references will be implemented as pointers, but that's not required by the specification..." (@Deduplicator see also your own answer in the referred question, "that comparison is a non-starter")
    – gnat
    Sep 7, 2017 at 14:44

3 Answers 3

5

In classical C, returning an array from a function isn't as easy as in Java. That's why C functions often choose to populate an array passed in from the caller, whereas Java methods typically follow the natural data flow, i.e. returning the results as array.

So in Java you can (and should) follow the natural data flow, i.e. returning a fresh array from the method. In C, although it's counter-intuitive, it's often better to have the caller provide the array - C programmers are used to that style.

Let me explain my reasoning:

C style

If you want to return an array from a function, you have to allocate it dynamically (it can't be on the stack as that wouldn't survive leaving the function, and it can't be static, because then multiple function calls would get mixed up). To the caller, that means that he gets responsible for eventually freeing the array.

The caller needs to know how long the array is, so he doesn't access invalid elements. As an array result (e.g. int[]) is in fact just a pointer to its element type (int *), there's no place to communicate the length.

So in C, there's no easy and natural way to return an array from a function.

(Of course, that's only classical C, and later versions added much of the features that classical C lacked, but many library functions were already defined early...).

Java style

Arrays are always created in dynamic memory (on the heap), but that's managed by the JVM. So the caller has no duty of freeing any memory, because that's done by the garbage collector.

Also there's no problem with the array length as Java arrays know their length.

5

From a semantic perspective, returning an array from the method suggests that it's a different array than the one you passed in. That's why the accepted C style, when modifying an array in place, is to modify the array that is passed in, in place. Returning a new collection is more common in Java than it is in C, which is why you see it being passed out of the function as a return value.

An array in C is really just a pointer to a typed value; C doesn't even know how long an array is if you allocated it dynamically, making it difficult for a caller to handle a returned array. Arrays in Java contain metadata, such as the length of the array and the type of its members, making it easier for methods to return new arrays and for callers to consume them.

Note that you can still modify an existing array in-place in Java if you wish, in which case you would do the same thing in Java that you would in C: modify the passed-in array parameter in-place.

C pointers and Java references are not the same thing, even though they are used to accomplish similar objectives. In C, a pointer is an actual memory address. Pointer arithmetic is possible in C, where it is actually used to allocate memory and dereference array members and struct members.

In Java, references are an implementation detail, not a memory address. You cannot perform pointer arithmetic on them, nor can you make any assumptions at all about how they work under the hood from a memory perspective, because the Java language specification does not stipulate how they are to be implemented.

This difference is further highlighted in the treatment of zero-length arrays: they don't exist in C, except as the last member of a struct.

0
0

do language features invoke a preference?

Absolutely, yes. Because of how C treats array expressions, passing an already-allocated array (whether static, automatic, or dynamic) as a parameter to a function is far more preferable to having the array allocate space and return a pointer to the caller.

First, the basics:

Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array1. IOW, array expressions lose their "array-ness" in most circumstances.

When you call a function with an array parameter, such as

int arr[N];
...
foo( arr );

the expression arr in the call to foo will "decay" from type int [N] to int *, and what foo receives will be a pointer value, not an array object:

void foo( int *arr ) // T a[N] and T a[] are treated as T *a
{                    // in a function parameter declaration
  ...
}

You can't return an array object from a function - C doesn't allow functions to return array types (a prototype like int foo()[10] is not allowed). Because of the conversion rule above, return arr; winds up returning a pointer to the first element:

int *foo( void )
{
  int arr[N];
  ...
  return arr; // equivalent to return &arr[0]
}

Except arr ceases to exist once foo exits2, so that returned pointer value is invalid, and attempting to dereference it will lead to undefined behavior.

At this point, you have two choices - make the function responsible for dynamically allocating the target buffer and passing the result back to the caller, or make the caller responsible for allocating the target buffer (dynamically or not) and passing it to the function.

Remember that standard C does not define any sort of automatic garbage collection for dynamic memory - all dynamic memory must be explicitly deallocated by calls to free.

The first option splits memory management duties between the function and the caller, which is not desirable. Whoever uses the function has to be aware that the function is dynamically allocating memory and that he is responsible for freeing that memory when he's done with it. If the user of the function doesn't pay attention to the documentation and doesn't free the memory returned from the function, he winds up with a memory leak.

The second option puts all the memory management responsibility on one party. The called function gets a pointer and the max buffer size - it doesn't have to worry about whether the pointed-to memory is statically, automatically, or dynamically allocated. This method is safer and more robust, which is why you see it more often in C code.

In Java, arrays are treated the same as pretty much any other data type (they don't "decay" to a pointer type), so it's more natural to have the function simply return an array to the caller. Even though arrays are allocated from the heap when they're created, you don't have to worry about deallocating them when you're done - they'll be automatically garbage-collected when their reference count goes to 0.


  1. "An array is just a pointer" is a mis-statement of this rule. Arrays are not pointers. Array expressions are converted to pointer expressions under most circumstances, but an array object is not a pointer.
  2. If we declare arr with the static keyword, it will persist after the function exits, but the code will no longer be re-entrant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.