consider the following code:
#define MAX_COUNT 4
static int foo(int w[MAX_COUNT])
{
int *p = w;
if (w) {
w[0] = 10; w[1] = 20; w[2] = 30; w[3] = 40;
}
return 0;
}
is it portable and legal pass NULL to above defined foo() (for example, for a situation when I don't need w[] be altered)? Given that the name of an array is a pointer to its first element, it seems to me that it should be OK, but perhpas I'm missing something?
Thanks !
In C, an array type function parameter is the same as a pointer type, so the following are the same:
static int foo(int w[]);
static int foo(int* w);
So, yes, it is legal to pass NULL to this function.
Given that the name of an array is a pointer to its first element
Not quite. An array decays to a pointer to its initial element under most circumstances, the exceptions being when it is the operand of sizeof or the unary & operator (the address-of operator).
It should be fine. The number of elements in the array that you passed are merely a formalism, and not really needed. w is just a pointer to int. There is no other runtime or compilation check.
C99 has the construct with the awfull syntax (for your example)
static int foo(int w[static MAX_COUNT]);
which basically means that the function expects at least MAX_COUNT
elements. Calling such a function with NULL would then be considered
as an error.
Unfortunately this feature is not yet widely implemented. e.g gcc
and clang just accept the syntax but don't do anything useful with the
information.
Related
Someone made an argument saying that in modern C, we should always pass arrays to functions through an array pointer, since array pointers have strong typing. Example:
void func (size_t n, int (*arr)[n]);
...
int array [3];
func(3, &array);
This sounded like it could potentially be a good idea to prevent all kinds of type-related and array-out-of-bounds bugs. But then it occurred to me I don't know how to apply const correctness to this.
If I do void func (size_t n, const int (*arr)[n]) then it is const correct. But then I can no longer pass the array, because of incompatible pointer types. int (*)[3] versus const int (*)[3]. The qualifier belongs to the pointed-at data and not to the pointer itself.
An explicit cast in the caller would ruin the whole idea of increased type safety.
How do I apply const correctness to array pointers passed as parameters? Is it at all possible?
EDIT
Just as info, someone said that the idea of passing arrays by pointer like this probably originates from MISRA C++:2008 5-2-12. See for example PRQA's high integrity C++ standard.
There is no way to do it except for the cast. This is significant drawback of the idea to pass arrays in this way.
Here is a similar thread where the C rules are compared to the C++ rules. We could conclude from this comparison that the C rules are not so well designed, because your use case is valid but C doesn't allow the implicit conversion. Another such example is conversion of T ** to T const * const *; this is safe but is not allowed by C.
Note that since n is not a constant expression, then int n, int (*arr)[n] does not have any added type safety compared to int n, int *arr. You still know the length (n), and it is still silent undefined behaviour to access out of bounds, and silent undefined behaviour to pass an array that is not actually length n.
This technique has more value in the case of passing non-VLA arrays , when the compiler must report if you pass a pointer to an array of the wrong length.
C standard says that (section: §6.7.3/9):
If the specification of an array type includes any type qualifiers, the element type is so- qualified, not the array type.[...]
Therefore, in case of const int (*arr)[n], const is applied to the elements of the array instead of array arr itself. arr is of type pointer to array[n] of const int while you are passing a parameter of type pointer to array[n] of int. Both types are incompatible.
How do I apply const correctness to array pointers passed as parameters? Is it at all possible?
It's not possible. There is no way to do this in standard C without using explicit cast.
But, GCC allow this as an extension:
In GNU C, pointers to arrays with qualifiers work similar to pointers to other qualified types. For example, a value of type int (*)[5] can be used to initialize a variable of type const int (*)[5]. These types are incompatible in ISO C because the const qualifier is formally attached to the element type of the array and not the array itself.
extern void
transpose (int N, int M, double out[M][N], const double in[N][M]);
double x[3][2];
double y[2][3];
...
transpose(3, 2, y, x);
Further reading: Pointer to array with const qualifier in C & C++
OP describes a function func() that has the following signature.
void func(size_t n, const int (*arr)[n])
OP wants to call it passing various arrays
#define SZ(a) (sizeof(a)/sizeof(a[0]))
int array1[3];
func(SZ(array1), &array1); // problem
const int array2[3] = {1, 2, 3};
func(SZ(array2), &array2);
How do I apply const correctness to array pointers passed as parameters?
With C11, use _Generic to do the casting as needed. The cast only occurs when the input is of the acceptable non-const type, thus maintaining type safety. This is "how" to do it. OP may consider it "bloated" as it is akin to this. This approach simplifies the macro/function call to only 1 parameter.
void func(size_t n, const int (*arr)[n]) {
printf("sz:%zu (*arr)[0]:%d\n", n, (*arr)[0]);
}
#define funcCC(x) func(sizeof(*x)/sizeof((*x)[0]), \
_Generic(x, \
const int(*)[sizeof(*x)/sizeof((*x)[0])] : x, \
int(*)[sizeof(*x)/sizeof((*x)[0])] : (const int(*)[sizeof(*x)/sizeof((*x)[0])])x \
))
int main(void) {
#define SZ(a) (sizeof(a)/sizeof(a[0]))
int array1[3];
array1[0] = 42;
// func(SZ(array1), &array1);
const int array2[4] = {1, 2, 3, 4};
func(SZ(array2), &array2);
// Notice only 1 parameter to the macro/function call
funcCC(&array1);
funcCC(&array2);
return 0;
}
Output
sz:4 (*arr)[0]:1
sz:3 (*arr)[0]:42
sz:4 (*arr)[0]:1
Alternatively code could use
#define funcCC2(x) func(sizeof(x)/sizeof((x)[0]), \
_Generic(&x, \
const int(*)[sizeof(x)/sizeof((x)[0])] : &x, \
int(*)[sizeof(x)/sizeof((x)[0])] : (const int(*)[sizeof(x)/sizeof((x)[0])])&x \
))
funcCC2(array1);
funcCC2(array2);
I posted this question on programmers.stackexchange earlier today. I have always assumed that int (*)[] does not decay into int ** in function parameters but I got multiple responses to my question that suggested that it does.
I have used int (*)[] heavily in my function parameters but now I have become really confused.
When I compile this function using gcc -std=c99 -pedantic -Wall
void function(int (*a)[])
{
sizeof(*a);
}
I get this error message:
c99 -Wall -pedantic -c main.c -o main.o
main.c: In function ‘function’:
main.c:3:11: error: invalid application of ‘sizeof’ to incomplete type ‘int[]’
make: *** [main.o] Error 1
Which suggests that *a has the type int [] and not int *.
Can someone explain if things like int (*)[] decays into int ** in function parameters and give me some reference (from the standard documents perhaps) that proves why it is so.
Only array types converted to pointer to its first element when passed to a function. a is of type pointer to an array of int, i.e, it is of pointer type and therefore no conversion.
For the prototype
void foo(int a[][10]);
compiler interpret it as
void foo(int (*a)[10]);
that's because a[] is of array type. int a[][10] will never be converted to int **a. That said, the second para in that answer is wrong and misleading.
As a function parameter, int *a[] is equivalent to int ** this is because a is of array type .
int (*)[] is a pointer to an array of int.
In your example, *a can decay to int*. But sizeof(*a) doesn't do decaying; it is essentially sizeof(int[]) which is not valid.
a can not decay at all (it's a pointer).
N1256 §6.7.5.3/p7-8
7 A declaration of a parameter as ‘‘array of type’’ shall be
adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword
static also appears within the [ and ] of the array type
derivation, then for each call to the function, the value of the
corresponding actual argument shall provide access to the first
element of an array with at least as many elements as specified by the
size expression.
8 A declaration of a parameter as ‘‘function returning type’’ shall
be adjusted to ‘‘pointer to function returning type’’, as in
6.3.2.1.
int (*)[] is "pointer to array of int". Is it "array of type"? No, it's a pointer. Is it "function returning type"? No, it's a pointer. Therefore it does not get adjusted.
In the case of int (*a)[], sizeof *a does not work for one reason: there is no element count for the array. Without an element count, the size cannot be calculated.
As a consequence of this, any pointer arithmetic on a will not work because it is defined in terms of the size of an object. Since the size is indeterminate for the array, you cannot use pointer arithmetic on the pointer itself. Array notation is defined in terms of pointer arithmetic, so sizeof a[0][0] (or any expression involving a[n] won't work whereas sizeof (*a)[0] will.
This effectively means you can do very little with the pointer. The only things allowed are:
dereferencing the pointer using the unary * operator
passing the pointer to another function (the type of the function parameter must be an array of arrays or a pointer to an array)
getting the size of the pointer (and alignment or type, if your compiler supports either or both of them)
assigning the pointer to a compatible type
If your compiler supports variable-length arrays (VLAs), and you know the size, you could work around the issue by simply adding a line at the start of the function body as in
void
foo (int (*a0)[], size_t m, size_t n)
{
int (*a)[n] = a0;
...
}
Without VLAs, you must resort to some other measure.
It is worth noting that dynamic allocation isn't a factor with int (*)[]. An array of arrays decays to a pointer to an array (like we have here), so they are interchangeable when passing them to a function (sizeof and any _Alignof or typeof keywords are operators, not functions). This means that the array pointed to must be statically allocated: once an array decays to a pointer, no more decay occurs, so you can't say a pointer to an array (int (*)[]) is the same as a pointer to a pointer (int **). Otherwise your compiler would happily let you pass int [3][3] to a function that accepts int ** instead of wanting a parameter of the form int (*)[], int (*)[n], int [][n], or int [m][n].
Consequently, even if your compiler doesn't support VLAs, you can use the fact that a statically allocated array has all of its elements grouped together:
void foo (int (*a0)[], size_t m, size_t n)
{
int *a = *a0;
size_t i, j;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
// Do something with `a[i * n + j]`, which is `a0[i][j]`.
}
}
...
}
A dynamically allocated one-dimensional array that is used as a two-dimensional array has the same properties, so this still works. It is only when the second dimension is dynamically allocated, meaning a loop like for (i = 0; i < m; i++) a[i] = malloc (n * sizeof *a[i]); to allocate each sub-array individually, that this principle not work. This is because you have an array of pointers (int *[], or int ** after array decay), which point to the first element of an array at another location in memory, rather than an array of arrays, which keeps all of the items together.
So:
no, int (*p)[] and int **q cannot be used in the same way. p is a pointer to an array, which means all items are grouped together starting the address stored in p. q is a pointer to a pointer, which means the items may be scattered at different addresses that are stored in q[0], q[1], ..., q[m - 1].
sizeof *p doesn't work because p points to an array with an unknown number of elements. The compiler cannot calculate the size of each element, so operations on p itself are very limited.
I've attempted to write a program that will take an array of numbers, and produce a new array
containing the squares of the entries of the first array. Here is the function which is supposed to do this;
void square_entires(numbers, squares){
for (int i=0; i<5; ++i) {
squares[i]=numbers[i]*numbers[i];
}
}
Now I get 3 errors on the squares[i]... line saying
"Subscripted value is neither array nor pointer".
Why on earth would I want i to be an array or a pointer!? Shouldn't it simply be an index for the loop to make sense? I've seen other examples of functions which loop over array elements this way and they work fine.. just my function doesn't work properly! Can somebody please explain why this is? thanks it advance.
Your functions declaration is wrong. You must have to specify the types of the arguments in a function. It should be
void square_entires(int numbers[], int squares[])
{
Without specifying type of parameters, it will be considered int by default which is allowed in C89.
n1570: 6.5.2.2 Function calls
Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.
Now I get 3 errors on the squares[i]... line saying
"Subscripted value is neither array nor pointer".
Why on earth would I want i to be an array or a pointer!? Shouldn't it simply be an index for the loop to make sense?
Clearly this warning is about the variables squares and numbers which should be declared either an array or pointer. Only then subscripted value is used.
Given A[B], the "subscripted value" is A. B is the subscript.
And, what others said about the missing type specifiers and declarator bits.
When you write:
int foo(a, b)
/* <- nothing here */
{
}
you're writing an old-style function. It's how C was written before some improvements took place in the 1980's which became standardized as ANSI C. The types for a and b are declared between the function declarator and body. If they are not declared, evidently they default to int. There are two ways out. The much preferred one is to use the modern style:
int square_entries(int *numbers, int *squares) // or some other type: double?
{
}
The obsolete style, not recommended, would have looked like:
int square_entries(numbers, squares)
int *numbers;
int *squares;
{
}
[] is the Subscript operator. The expression within the brackets is referred to as a subscript. A postfix expression followed by an expression in [ ] (brackets) specifies an element of an array.
You have not specified the types for numbers, squares in
void square_entires(numbers, squares) // Defaults to type int (where actually you need type int array
This (is valid in C89) main() implicitly meant (previously) int main(void). However the default return type rule has been abandoned in C99.
I think you need this:
void square_entires(int numbers[], int squares[])
or
void square_entires(int * numbers, int * squares)
Cause array decay into pointers in functions, hence you cannot calculate the size of the array in the function - so pass the sizes as well (if required), like this:
void square_entires(int numbers[], int squares[], int sizenumbers, int sizesquares)
By definition, the expression a[b] is equivalent to the expression *((a) + (b)), and, because addition is associative, it is also equivalent to b[a].
When you pass an argument to a function, the argument should represent the type of the parameter you are going to pass. Here, when you see your passed arguments i.e. numbers and squares, it doesn't make it clear that what is the type of the data you are going to pass to your function.
Well, you are going to pass an array which contains the int data type, that is the reason why you need to declare your argument as an int array i.e. int numbers[] (where in the subscript [] represents that your argument is going to be an array and "int" represents that the array contains the data of int type".)
so, your code should be like :
void square_entires(int numbers[], int squares[])
{
I read that;
Although an array name can be used as a pointer (after decaying into pointer), it's not possible to assign it a new value. Attempting to make it point elsewhere is an error:
while (*a != 0) // a is of (int *) type
a++; //wrong
On the other hand, when passed to a function, an array name is always treated as a pointer. The function call
largest = largest_num(b, n) // b is an array of int
for the function
int find_largest(int a[], int n)
{
....
....
}
causes a pointer to the first element of b to be assigned to a.
Above two statements ( in bold ) seems to me contradictory. I am confused.
In a function declaration, an array is treated as if you'd declared a pointer, so
int find_largest(int a[], int n) {
is processed as if it were
int find_largest(int *a, int n) {
So a is a pointer, not an array, and there's no contradiction.
Since it's a pointer, you can reassign a, e.g.
a++;
is allowed.
No contradiction there - you're still working with a pointer (to int), and int a[] notation is allowed only for convenience. Quoting the comp.lang.c FAQ:
Since arrays decay immediately into pointers, an array is never
actually passed to a function. You can pretend that a function
receives an array as a parameter, and illustrate it by declaring the
corresponding parameter as an array:
void f(char a[])
Interpreted literally, this declaration would have no use, so the
compiler turns around and pretends that you'd written a pointer
declaration, since that's what the function will in fact receive:
void f(char *a)
This conversion of array-like declarators into pointers holds
only within function formal parameter declarations, nowhere else. If
the conversion bothers you, you're under no compulsion to make use of
it; many programmers have concluded that the confusion it causes
outweighs the small advantage of having the declaration ``look like''
the call or the uses within the function.
I think that it is because the former is an array of pointers to char and the latter is a pointer to an array of chars, and we need to properly specify the size of the object being pointed to for our function definition. In the former;
function(char * p_array[])
the size of the object being pointed to is already included (its a pointer to char), but the latter
function(char (*p_array)[])
needs the size of the array p_array points to as part of p_array's definition?
I'm at the stage where I've been thinking about this for too long and have just confused myself, someone please let me know if my reasoning is correct.
Both are valid in C but not C++. You would ordinarily be correct:
char *x[]; // array of pointers to char
char (*y)[]; // pointer to array of char
However, the arrays decay to pointers if they appear as function parameters. So they become:
char **x; // Changes to pointer to array of pointer to char
char (*y)[]; // No decay, since it's NOT an array, it's a pointer to an array
In an array type in C, one of the sizes is permitted to be unspecified. This must be the leftmost one (whoops, I said rightmost at first). So,
int valid_array[][5]; // Ok
int invalid_array[5][]; // Wrong
(You can chain them... but we seldom have reason to do so...)
int (*convoluted_array[][5])[][10];
There is a catch, and the catch is that an array type with [] in it is an incomplete type. You can pass around a pointer to an incomplete type but certain operations will not work, as they need a complete type. For example, this will not work:
void func(int (*x)[])
{
x[2][5] = 900; // Error
}
This is an error because in order to find the address of x[2], the compiler needs to know how big x[0] and x[1] are. But x[0] and x[1] have type int [] -- an incomplete type with no information about how big it is. This becomes clearer if you imagine what the "un-decayed" version of the type would be, which is int x[][] -- obviously invalid C. If you want to pass a two-dimensional array around in C, you have a few options:
Pass a one-dimensional array with a size parameter.
void func(int n, int x[])
{
x[2*n + 5] = 900;
}
Use an array of pointers to rows. This is somewhat clunky if you have genuine 2D data.
void func(int *x[])
{
x[2][5] = 900;
}
Use a fixed size.
void func(int x[][5])
{
x[2][5] = 900;
}
Use a variable length array (C99 only, so it probably doesn't work with Microsoft compilers).
// There's some funny syntax if you want 'x' before 'width'
void func(int n, int x[][n])
{
x[2][5] = 900;
}
This is a frequent problem area even for C veterans. Many languages lack intrinsic "out-of-the-box" support for real, variable size, multidimensional arrays (C++, Java, Python) although a few languages do have it (Common Lisp, Haskell, Fortran). You'll see a lot of code that uses arrays of arrays or that calculates array offsets manually.
NOTE:
The below answer was added when the Q was tagged C++, and it answers from a C++ perspective. With tagged changed to only C, both the mentioned samples are valid in C.
Yes, Your reasoning is correct.
If you try compiling the error given by compiler is:
parameter ‘p_array’ includes pointer to array of unknown bound ‘char []’
In C++ array sizes need to be fixed at compile time. C++ standard forbids Variable Lenght Array's(VLA) as well. Some compilers support that as an extension but that is non standard conforming.
Those two declarations are very different. In a function parameter declaration, a declarator of [] directly applied to the parameter name is completely equivalent to a *, so your first declaration is exactly the same in all respects as this:
function(char **p_array);
However, this does not apply recursively to parameter types. Your second parameter has type char (*)[], which is a pointer to an array of unknown size - it is a pointer to an incomplete type. You can happily declare variables with this type - the following is a valid variable declaration:
char (*p_array)[];
Just like a pointer to any other incomplete type, you cannot perform any pointer arithmetic on this variable (or your function parameter) - that's where you error arises. Note that the [] operator is specified as a[i] being identical to *(a+i), so that operator cannot be applied to your pointer. You can, of course, happily use it as a pointer, so this is valid:
void function(char (*p_array)[])
{
printf("p_array = %p\n", (void *)p_array);
}
This type is also compatible with a pointer to any other fixed-size array of char, so you can also do this:
void function(char (*p_array)[])
{
char (*p_a_10)[10] = p_array;
puts(*p_a_10);
}
...and even this:
void function(char (*p_array)[])
{
puts(*p_array);
}
(though there is precious little point in doing so: you might as well just declare the parameter with type char *).
Note that although *p_array is allowed, p_array[0] is not.
Because,
(1) function(char * p_array[])
is equivalent to char **p_array; i.e. a double pointer which is valid.
(2) function(char (*p_array)[])
You are right, that p_array is pointer to char array. But that needs to be of fixed size in the case when it appears as function argument. You need to provide the size and that will also become valid.