Passing arrays vs structures to functions - c

In C, arrays are passed to functions as pointers. Structures can be passed to functions either by value or by address (pointer). Is there any specific reason why we can not pass array by value but we can pass structre by value ?

In C, everything is passed by value. There is another rule that says that in most contexts, the name of an array is equivalent to a pointer to its first element. Passing an array to a function is such a context.
So, the special case is not that arrays are passed by reference, the special case is the rule about arrays decaying to pointers. This gives one the impression that an array is passed by reference (which it effectively is, but now you know why!)
The post in my link above explains in more detail about the type of an array in different contexts.

Related

Why you cannot return fixed size / const array in C? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
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?
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.
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.
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.
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.

Why can C functions not return arrays?

In C, a struct (record data structure) can be the return type of a function, but an array cannot be. What design characteristics of the C Language cause arrays to be an exception?
A naked array type in C language is not copyable for primarily historical reasons. For this reason it is not possible to initialize arrays with arrays, assign arrays to arrays, pass arrays by value as parameters or return arrays from functions. (Initialization context has a notable exception of char s[6] = "Hello";.)
It is still possible to do all the above if the array is wrapped in struct type, which demonstrates that the limitation is purely declarative in nature. There's no compelling technical reason for it.
C language inherited its approach to array implementation from its historical predecessors - B and BCPL languages. In B/BCPL arrays were openly implemented as pointers, meaning that an attempt to assign one array to another actually represented assignment of pointers. C language followed a different approach. In C arrays are not pointers, but the interface specification of C arrays is kept superficially compatible with that of B/BCPL. Arrays in C still "pretend" to be pointers in most contexts. This is one reason they are not immediately copyable.
Most obviously, the "lack" is that C doesn't permit a function to return a result of an array type. This is stated explicitly in the language standard.
Array types are, in a sense, second-class citizens in C. In most contexts, an expression of array type is implicitly converted to a pointer to its first element. The exceptions are when the array expression is the operand of sizeof (which yields the size of the array), when it's the operand of unary & (which yields the address of the array), and when it's a string literal in an initializer used to initialize an array object.
This absolutely does not mean that arrays are "really" pointers; they're not. You'll see people claiming that they are. They're wrong.
Functions return values. You can have a value of a structure type; that value consists of the values of its members. C permits assignment, parameter passing, and function results of structure type. All these manipulate array values (they deal with them by value, not by reference).
The same is not true for arrays. The rules I mentioned above imply that you can't construct an expression whose value is of an array type. There are array values (consisting of the values of all the array's elements), but such values are difficult or impossible to manipulate directly.
The way C code usually manipulates arrays is by using pointers to individual elements.
It probably wouldn't have been too difficult to have designed C so that fixed-size arrays can be treated as values, with assignment, parameter passing, and so forth. But then you'd run into problems where int[10] and int[11] are two distinct and incompatible types. Most C code that deals with arrays needs to handle arrays whose size is determined at run time. For example, the string functions in <string.h> deal with arrays of characters of any arbitrary length. They do so by using pointers to the elements of the arrays. You couldn't very well have distinct functions for 1-element, 2-element, 3-element, and so forth, arrays.
You can do the equivalent of returning an array value from a function, but it's unfortunately awkward. You can return a structure containing the array -- but then the size of the array has to be fixed at compile time. You can return a pointer to (the first element of) the array -- but then you have to deal with allocating and deallocating memory to hold the array. You can have the caller pass in a pointer to an array -- but that places the burden of memory management on the caller. And so forth.
Yes, it's all a bit of a mess. But dealing with arrays that can vary in size is genuinely difficult. C gives you all the tools you need to do it, but leaves a lot of the detailed management to you, the programmer. (Other languages provide arrays as first-class types. Many of those languages have compilers or interpreters written in C.)
Suggested reading: Section 6 of the comp.lang.c FAQ.
The characteristic is that in the small and speedy C language you don't want the equivalent of large memcpy operations when returning. If you badly need arrays returned, make them a member of a struct, and voila, array return in C. Sort of, starting with C89 :-)
Or use a memcpy yourself when and where you need it.
While the array can't be returned from a C function, a pointer to the array may. For code example of how to do what you're looking for, visit the site:
http://www.tutorialspoint.com/cprogramming/c_return_arrays_from_function.htm

C - pass predefined function as pointer

I am working with a Hashtable struct that maps keys to values, where the values are (void *) so that Hashtables can hold any kind of value.
To be able to free those values, the deconstructor of a Hashtable takes in a pointer to a freeing function as an argument. In my case, I know I am going to be freeing basic types, like char* and int*. Is it possible to pass in a pointer to the free() function, since this can deal with basic types?
Something like this:
FreeHashTable(hashtable_name, free);
You can (and should) pass to free every pointer that has returned by malloc, no matter which type (or struct) it points to. Be careful to not pass to free pointers that you didn't get from malloc. (Middle of arrays, local variables, etc)
BTW, unless some of your data types need some work before freeing, you can do it without pass pointer to function - just call free.
You don't need to pass the pointer to the function.
Just loop through the values and call free.

What happens when I pass an array to a function/subroutine?

I had never thought about this before, but lately I've been worried about something. In Fortran90(95), say I create a really big array
Integer :: X(1000000)
and then I write a function that takes this array as an argument. When I pass the array to the function (as in myfunc(X)) what exactly happens during run time?
Does the entire array get passed by value and a new copy constructed inside the function?(costly)
Or does the compiler simply pass some sort of reference or pointer to the array?(cheap)
Do the dimension of the array or the declaration of the function make a difference?
In Fortran 90 , as in most other programming languages, arrays are passed by reference (technically, this is often a reference to the first item of the array). In Fortran 90, non-array values are also usually passed by reference. So, you needn't worry about the size of the parameters you pass, since they won't be copied but will, instead, be passed simply by reference.
The one thing you don't want to do is something like:
INTEGER :: X(1:1000,1:1000,1:1000)
CALL myRoutine(X(2:999,2:999,2:999))
where myRoutine cannot operate on the bounds of the array for some reason. It cannot pass the reference to the slice of the array since it not contiguous in memory. So it creates a temporary array and copies the values from X. Needless to say this is very slow. But you shouldn't have that issue with 1D array, even when specifying slices, as they are still contiguous in memory.

What is a "value" array?

In C, the idea of an array is very straightforward—simply a pointer to the first element in a row of elements in memory, which can be accessed via pointer arithmetic/ the standard array[i] syntax.
However, in languages like Google Go, "arrays are values", not pointers. What does that mean? How is it implemented?
In most cases they're the same as C arrays, but the compiler/interpreter hides the pointer from you. This is mainly because then the array can be relocated in memory in a totally transparent way, and so such arrays appear to have an ability to be resized.
On the other hand it is safer, because without a possibility to move the pointers you cannot make a leak.
Since then (2010), the article Slices: usage and internals is a bit more precise:
The in-memory representation of [4]int is just four integer values laid out sequentially:
Go's arrays are values.
An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C).
This means that when you assign or pass around an array value you will make a copy of its contents. (To avoid the copy you could pass a pointer to the array, but then that's a pointer to an array, not an array.)
One way to think about arrays is as a sort of struct but with indexed rather than named fields: a fixed-size composite value.
Arrays in Go are also values in that they are passed as values to functions(in the same way ints,strings,floats etc.)
Which requires copying the whole array for each function call.
This can be very slow for a large array, which is why in most cases it's usually better to use slices

Resources