Should I pass an int array to a function by reference? - arrays

A basic idea of what I'm talking about is below.
#include <stdio.h>
void someFunction(int arr[], int n)
{
for (int i = 0; i < n; ++i)
arr[i] *= 2;
}
void someFunctionByReference(int *arr, int n)
{
for (int i = 0; i < n; ++i)
arr[i] *= 2;
}
int main()
{
int arr[] = {5, 3, 2, 4, 5, 7, 0};
int n = sizeof(arr) / sizeof(arr[0]);
someFunction(arr, n);
int arr2[] = {5, 3, 2, 4, 5, 7, 0};
int n2 = sizeof(arr2) / sizeof(arr2[0]);
someFunctionByReference(arr2, n2);
return 0;
}
Both of these functions (as I see it) do the same thing. They even contain the same code. However, I would like to understand what the differences between the two are, and if there is a proper and improper way of doing this in certain scenarios.

Both function invocations and definitions are identical. Under most circumstances, an expression of array type will “decay” to an expression of pointer type and the value of the expression will be the address of the first element. In the context of a function parameter declaration, T a[N] and T a[] are interpreted as T *a - the parameter is a pointer, not an array object. This is because when you pass an array expression as a function argument, the function will actually receive a pointer value, not an array.
This behavior is unique to array expressions - other aggregate types like struct and union types do not “decay” in this manner. If you pass a struct type with an array member, the contents of the array member will be copied over.
And this is not an example of “pass by reference”, either. C passes all function parameters by value, no exceptions. Sometimes those values are pointers. This particular quirk is simply fallout from how C treats arrays.

Related

Why can't int (*p)[] be used as an argument for C function?

#include <stdio.h>
void print(int (*p)[3]);
int main(void)
{
int a[3] = {1, 2, 3};
print(&a);
return 0;
}
void print(int (*p)[3])
{
for (int i = 0; i < sizeof(*p) / sizeof(**p); i++)
printf("%d\n", (*p)[i]);
}
I have written a C function. See above.
It can print all the elements in an array.
There is one thing which is not so perfect : The number of array elements seems to be known in advance.
So I made some modification in hopes of making the function universal :
#include <stdio.h>
void print(int (*p)[]);
int main(void)
{
int a[3] = {1, 2, 3};
print(&a);
return 0;
}
void print(int (*p)[])
{
for (int i = 0; i < sizeof(*p) / sizeof(**p); i++)
printf("%d\n", (*p)[i]);
}
In the function, p is pointer pointing to the entire array.
However, it fails to be compiled.
Why can't int (*p)[] be used as an argument for C function?
int (*p)[] can be used as an argument for the function. The part of your code that gives the error is sizeof *p which is obviously not possible, because the type of *p is int[] which is an incomplete type and hence has no known size.
For the function to know the length of the array, you must design a way for the function to receive this information. Options include:
what you did in the original code.
passing the length as another argument.
including the length as an array element.
having a sentinel value on the end of the array.
The most common idiom would be to pass int *p, size_t n, you do not really gain anything by using pointer-to-array without the dimension being given.
The problem is that int [] is an incomplete type as the array has no defined size and therefore its sizeof cannot be taken.
In "modern C" (i.e. for almost 2 decades) you could have used variable-length arrays for this - you can pass the size as an argument and then the array:
#include <stdio.h>
#include <stdlib.h>
void print(size_t n, int (*p)[*]);
int main(void) {
int a[3] = {1, 2, 3};
print(3, &a);
}
void print(size_t n, int (*p)[n]) {
for (size_t i = 0; i < sizeof(*p) / sizeof(**p); i++)
printf("%d\n", (*p)[i]);
}
Of course this gains you nothing, since sizeof *p / sizeof **pp will be ... n. Therefore we might as well have used
void print(size_t n, int p[n]) {
for (size_t i = 0; i < p; i++)
printf("%d\n", p[i]);
}
which is less typing.
Short answer: int (*p)[] can't be used as an argument and have the function magically know the array size, because the standard says so.
Longer answer:
int (*p)[] is a pointer to an array, but the array has no defined size. So by looking at the array, it is impossible to do pointer arithmetic, calculate size of the thing p is pointing at, etc..
You don't have array of arrays so you don't need int (*p)[]. You have an array of int, so int *p or int p[] should be enough. This does not solve the problem of knowing the size of your array in print. To do this you basically have 3 options
Hardcode the value in the function
Put a sentinel value in your array to mark the end
Pass the size as a separate parameter like this:
void print(int n, int p[n])
Just remember that whatever method you use, parameter passing of arrays will always use pointers behind the scenes, so you CAN NOT use sizeof(p) to calculate the size of the array. sizeof will always return the size of a pointer in those situations

Failed to initialize a pointer array in C

int array[2] = {1, 1};
int (*pointer_array)[2] = {NULL, NULL};
The first line can be correctly compiled but not the second one? Why?
GCC compiler will pop up a warning, excess elements in scalar initializer.
How to initialize a pointer array in C?
EDITED
I declared a pointer array in a wrong way.
It should be like this:
int *pointer_array[2] = {NULL, NULL};
It should be
int (*pointer_array)[2]=(int[]){1,2};
This is pointer to array of int .I don't know you want pointer to array of int or array of pointers.
To declare as array of pointer you need to do this -
int *pointer_array[2];
Suppose you have an array of int of length 5 e.g.
int x[5];
Then you can do a = &x;
int x[5] = {1};
int (*a)[5] = &x;
To access elements of array you: (*a)[i] (== (*(&x))[i]== (*&x)[i] == x[i]) parenthesis needed because precedence of [] operator is higher then *. (one common mistake can be doing *a[i] to access elements of array).
Understand what you asked in question is a compilation time error:
int (*a)[3] = {11, 2, 3, 5, 6};
It is not correct and a type mismatch too, because {11,2,3,5,6} can be assigned to int a[5]; and you are assigning to int (*a)[3].
Additionally,
You can do something like for one dimensional:
int *why = (int[2]) {1,2};
Similarly, for two dimensional try this(thanks #caf):
int (*a)[5] = (int [][5]){ { 1, 2, 3, 4, 5 } , { 6, 7, 8, 9, 10 } };

Passing array as parameter

I want to pass an array as a parameter to another function:
int i;
int main()
{
int a[5] = {1, 2, 3, 4, 5};
printf("Main:\n");
for(i=0; i<sizeof(a)/sizeof(int); i++)
{
printf("%i\n", a[i]);
}
func(a);
return;
}
void func(int a[])
{
printf("Func:\n");
for(i=0; i<sizeof(a)/sizeof(int); i++)
{
printf("%i\n", a[i]);
}
}
The loop in the main function prints all 5 values:
Main:
1
2
3
4
5
But the loop in the function func only prints 2 values:
Func:
1
2
Why this strange behaviour?
I want to pass an array as a parameter to another function:
This is a common pitfall. Arrays do not bring along their length, as they do in other languages. A C "array" is just a bunch of contiguous values, so sizeof will not (necessarily) return the length of the array.
What actually happens is that the function gets passed a pointer to the area of memory where the array is stored (and therefore, to the first element of the array), but no information about that area's size. To "pass an array with size", you must do something to provide the extra information:
explicitly pass also its length as an extra parameter. Safer: you can pass uninitialized arrays.
use a special "terminating" value on the array. More compact: you pass only one parameter, the pointer to the array.
(suggested implicitly by #CisNOTthatGOODbutISOisTHATBAD's comment): pass a pointer to a struct holding a pointer to the memory and a size_t length in elements (or in bytes). This has the advantage of allowing to store yet more metadata about the array.
For arrays of integral type, you could even store the length in the first (zeroth) element of the array. This can sometimes be useful when porting from languages that have 'measured' arrays and indexes starting from 1. In all other cases, go for method #1. Faster, safer, and in my opinion clearer.
Strings are arrays of characters that employ a variation of method #2: they are terminated by a special value (zero).
Using method #1, your function would become:
void func(size_t n, int a[])
{
printf("Func:\n");
for (i=0; i < n; i++)
{
printf("%i\n", a[i]);
}
}
(it is equivalent to void func(size_t n, int *a) ).
Declaring a function with a parameter of array type is equivalent to declaring it with a parameter of pointer type, i.e. your function is equivalent to:
void func(int *a)
As such, the computation of sizeof(a) inside func computes the size of an int *, not the size of the original array (5 * sizeof(int)).
Since the size of an int * on your platform is apparently twice the size of an int, you see two values printed inside the function in contrast to the five printed outside it (where sizeof(a) correctly computes the size of the array).
This is all related to the fact that when you pass an array to a function, what you're actually doing is passing a pointer to its first element.
Note in passing that this is a FAQ of the comp.lang.c newsgroup:
http://c-faq.com/~scs/cgi-bin/faqcat.cgi?sec=aryptr#aryparmsize
I would change the code to this:
int i;
const int N = 5;
int main()
{
int a[N] = {1, 2, 3, 4, 5};
printf("Main:\n");
for(i=0; i<N; i++)
{
printf("%i\n", a[i]);
}
func(a);
return;
}
void func(int a[])
{
printf("Func:\n");
for(i=0; i<N; i++)
{
printf("%i\n", a[i]);
}
}
Parameter N is constant and define size of array.
I think it is better way, then 2 parameters in function.

Initializing "a pointer to an array of integers"

int (*a)[5];
How can we Initialize a pointer to an array of 5 integers shown above.
Is the below expression correct ?
int (*a)[3]={11,2,3,5,6};
Suppose you have an array of int of length 5 e.g.
int x[5];
Then you can do a = &x;
int x[5] = {1};
int (*a)[5] = &x;
To access elements of array you: (*a)[i] (== (*(&x))[i]== (*&x)[i] == x[i]) parenthesis needed because precedence of [] operator is higher then *. (one common mistake can be doing *a[i] to access elements of array).
Understand what you asked in question is an compilation time error:
int (*a)[3] = {11, 2, 3, 5, 6};
It is not correct and a type mismatch too, because {11,2,3,5,6} can be assigned to int a[5]; and you are assigning to int (*a)[3].
Additionally,
You can do something like for one dimensional:
int *why = (int p[2]) {1,2};
Similarly, for two dimensional try this(thanks #caf):
int (*a)[5] = (int p[][5]){ { 1, 2, 3, 4, 5 } , { 6, 7, 8, 9, 10 } };
{11,2,3,5,6} is an initializer list, it is not an array, so you can't point at it. An array pointer needs to point at an array, that has a valid memory location. If the array is a named variable or just a chunk of allocated memory doesn't matter.
It all boils down to the type of array you need. There are various ways to declare arrays in C, depending on purpose:
// plain array, fixed size, can be allocated in any scope
int array[5] = {11,2,3,5,6};
int (*a)[5] = &array;
// compound literal, fixed size, can be allocated in any scope
int (*b)[5] = &(int[5]){11,2,3,5,6};
// dynamically allocated array, variable size possible
int (*c)[n] = malloc( sizeof(int[n]) );
// variable-length array, variable size
int n = 5;
int vla[n];
memcpy( vla, something, sizeof(int[n]) ); // always initialized in run-time
int (*d)[n] = &vla;
int a1[5] = {1, 2, 3, 4, 5};
int (*a)[5] = &a1;
int vals[] = {1, 2};
int (*arr)[sizeof(vals)/sizeof(vals[0])] = &vals;
and then you access the content of the array as in:
(*arr)[0] = ...

function that returns an array (Win32 console application written in C, Learning about operating systems)

In C, is it not possible to make the return type an array? I'm starting to learn about pointers in my operating systems course and I need to make a function that takes 2 arrays as parameters and returns an array containing only the elements that are in both parameter arrays.
This is what I have so far for my C function that returns an array:
#include <stdio.h>
main()
{
printf("Hello world");
int array1[4] = {1, 2, 3, 4};
int array2[4] = {3, 4, 5, 6};
int* inter = intersection(array1, array2);
printf(inter); // <-- also, I don't know how I could get this to work for testing
//freezes program so it doesn't terminate immediately upon running:
getchar();
}
int* intersection(int array1[], int array2[])
{
int arrayReturn[sizeof(array1) + sizeof(array2)];
int count = 0;
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < 4; j++)
{
if(array1[i]==array2[j])
{
arrayReturn[count] = array1[i];
count = count + 1;
}
}
}
return arrayReturn;
}
Another question I have is how could I test this function in the main() method using a printf() statement?
The reason why I need to do this is because we're learning about processes and memory allocation and pointers play a big role in OS development. My professor told me that pointers are so difficult to understand that they leave pointers out of many programming languages.
here we go
#include <stdio.h>
/* declare function, you need to capture array size as well, and also allow
to get result target pointer, result space info and pointer to place where the
size of result will be captured */
int* intersection(int * array1, int a1len , int * array2, int a2len, int* result, int *resultsize, int resultspace);
main()
{
printf("Hello world\n");
int array1[4] = {1, 2, 3, 4};
int array2[4] = {3, 4, 5, 6};
int arrayr[32]; /*result will be in this table */
int resultsize;
int resultspace = 32;
int i;
/* here comes confusion - int resultsize means it will be read as integer,
so we need to write &resultsize to get the pointer,
array declaration like int arrayr[number] is actually understood by system
as a pointer to an integer, pointing to first element of array,
allowing you to use indexes
so arrayr[3] actually means *(arrayr + 3 * sizeof(int))
*/
int* inter = intersection(array1, 4, array2, 4, arrayr, &resultsize, resultspace);
/* inter is now a pointer to arrayr */
for (i = 0; i<resultsize; i=i+1) {
printf("%d\n", inter[i]);
}
//freezes program so it doesn't terminate immediately upon running:
getchar();
}
int* intersection(int * array1, int a1len , int * array2, int a2len, int* result, int *resultsize, int resultspace)
{
/* as we received a pointer to resultsize (*resultsize)
we need to de-reference it using "*" to get or set the actual value */
*resultsize = 0;
int i, j;
for(i = 0; i < a1len; i++)
{
for(j = 0; j < a2len; j++)
{
if(array1[i]==array2[j])
{
result[*resultsize] = array1[i];
*resultsize = *resultsize + 1;
if (resultspace == *resultsize)
return result;
}
}
}
return result;
}
In C, is it not possible to make the return type an array?
No. Some of the characteristics that differentiate arrays from pointers are:
sizeof array evaluates to n * sizeof *array, where n is the number of elements, instead of the size of a pointer.
&array evaluates to a pointer to an array, instead of a pointer to a pointer.
You can use an array to initialise a pointer, eg. int *ptr = (int[]){ 1, 2, 3, 4 };, but you can't use a pointer to initialise an array, eg. int array[4] = ptr;.
You can't assign to an array eg. int array[4]; array = (int[]){ 1, 2, 3, 4 };, but you can assign to a pointer: int *ptr; ptr = (int[]){ 1, 2, 3, 4 };, unless the pointer is declared as a const pointer to int, eg. int * const ptr = NULL; ptr = (int[]){ 1, 2, 3, 4 };
I'm starting to learn about pointers in my operating systems course
and I need to make a function that takes 2 arrays as parameters and
returns an array containing only the elements that are in both
parameter arrays.
This isn't possible. Firstly, your array arguments are actually pointer arguments. Look at sizeof array1 and sizeof array2. Try to initialise an array with them. Try to assign to them. How many of the tests above seem to indicate that they're pointers? When you pass an array to a function, the array expression evaluates to a pointer to the first element of the array. Perhaps you'd want to declare your function to accept pointer to arrays, eg:
int *intersection(size_t sz1, int (*array1)[sz1], // array1 is a pointer to int[sz1]
size_t sz2, int (*array2)[sz2]) // array2 is a pointer to int[sz2]
{ ... }
Secondly, your function clearly returns a pointer value, not an array. Regarding that return value, arrayReturn is declared inside intersection as an array that has automatic storage duration, and so it will be destroyed when intersection returns. When main attempts to use that value, it'll be attempting to use a destroyed array. Returning an array using automatic storage duration isn't possible. Returning a fixed-size struct using automatic storage duration is possible, but this isn't helpful for your problem because your return value would need to be dynamic in size.
Another question I have is how could I test this function in the
main() method using a printf() statement?
You can't do anything with the return value of that function, because using an object that has been destroyed is undefined behaviour.
The reason why I need to do this is because we're learning about
processes and memory allocation and pointers play a big role in OS
development.
The C programming language is independant of OS implementations; It doesn't matter if an OS delays the destruction of objects with automatic storage duration. If you use a destroyed object, you're invoking undefined behaviour.
My professor told me that pointers are so difficult to understand that
they leave pointers out of many programming languages.
Has he/she written out his/her lesson plan? If not, then he/she is missing a crucial point where improvement can be identified. How successful has his/her written lesson plan been in the past? If it's been 100% successful, it doesn't make sense to be using words like "difficult"; Why would you want to unnecessarily trigger overwhelming feelings in students? If parts are too complex, then it makes more sense to identify and clarify those parts, rather than identifying those parts and specifying them as "difficult". It also makes no sense to mention other programming languages in a course about C.
When a professor's lesson plan becomes fairly successful, it becomes feasible to publish it as a book. One example is K&R's "The C Programming Language, Second Edition". Do you have a book?

Resources