1

In C99, why is it that declaring a variable p as pointer to array needs to be cast before it is passed as a parameter to a function with array type argument, but declaring a variable p as void pointer then casting it to a pointer to array can be passed as pointer to array to the same function?

#include <stdio.h>

int arreglo(int locArr[])
{
    locArr[0]=1;
    printf("el arreglo es : %i\n",locArr[0]);
    return 0;
}

int main()
{
    /* Declare a pointer p to array */
    int (*p)[];

    int manArr[10];

    p=&manArr;   /* assign the adress of manArr as case below */

    /* Here passing pointer p is not allowed as expected,
       since our function has int* as argument */         

    /* so I need to do a casting */
    arreglo((int*)p);   
}

/* **But in this other main function**: */

int main()
{
    /* Declare a void pointer  */
    void *p=NULL;

    /* Do a casting from p to void to p to array */
    p=(int (*)[])p;

    int manArr[10];

    p=&manArr;  /* assing the adress of the array manArr as in above case */

    /* Now given the pointer to array as parameter to function WORKS¡¡,
       why?. As before the function expects int* as argument not
       a pointer to an array */  

    arreglo(p);

}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Aca
  • 25
  • 5
  • 1
    The question is: Why in the second main function it works. According to the C reference, when you have and array as argument it is converted to pointer to int (int*) so int* arr is the same as int arr[] (on the argument list only). But if you pass a pointer to Array in theory you need to cast it to int*. – Aca Jul 03 '13 at 01:47
  • 1
    This is going to sound pedantic, but I really am trying to help. You should try to put your question in a single sentence, and in question form `" ... ?"` Once you clarify what precisely the problem is, it will be much easier to answer. You may likely discover the answer yourself during this exercise. ... To start with, make sure you know exactly what these terms mean: pointer to int, array of int, pointer to array. ... Con-fusion: fusing together, the solution is to rigorously clarify and qualify the distinct concepts involved. – luser droog Jul 03 '13 at 02:22
  • 1
    thanks for the advice, this is my second question in this site. – Aca Jul 03 '13 at 23:18
  • +1 for learning and improving! Welcome to the site! – luser droog Jul 04 '13 at 23:50

3 Answers3

3

While an array-of-int and a pointer-to-int are equivalent, a pointer-to-array(-of-int) is an extra level of indirection.

Here's your program with some comments and corrections.

#include<stdio.h>

int arreglo(int locArr[])
{
    locArr[0]=1;
    printf("el arreglo es : %i\n",locArr[0]);
    return 0;
}

int main()
{
    /* Declare a pointer p to array */
    //int (*p)[]; //NO!
    /* Declare a pointer p to int */
    int *p;

    int manArr[10];


    //p=&manArr;   /* assign the adress of manArr as case below */  //NO!
    p=manArr;

    /* Here passing pointer p is not allowed as expected,
       since our function has int* as argument */ // Because `int*` and `int (*)[]` are different

    /* so I need to do a casting */ //NO! 
    //arreglo((int*)p);   
    arreglo(p);
}

/* **But in this other main function**: */

int main()

{
    /* Declare a void pointer  */
    void *p=NULL;

    /* Do a casting from p to void to p to array */
    //p=(int (*)[])p;  //NO! This does absolutely nothing at all.

    int manArr[10];

    //p=&manArr;  /* passing the adress of the array manArr as in above case */
    p=manArr;

    /* Now given the pointer to array as parameter to function WORKS¡¡,
       why?. As before the function expects int* as argument not
       a pointer to an array */  
    // A void* bypasses all type-checking, since it can be implicitly converted to any type

    arreglo(p); //This would still compile if p="potato", because a void* converts to any type.

}

So, let's start over from the beginning.

int i = 0;              // a simple int variable
int a[3] = { 1, 2, 3 }; // an array of ints
int *p = a;             // a pointer to int can be used the same as an array of int
p[0] = 4;               // so now a[0] = 4, too

int i   int a[3]
|----|  |----|----|----|
|  0 |  |  4 |  2 |  3 |
|----|  |----|----|----|
           ^
int *p     |
|----|     |
|  --|-----
|----|

A pointer-to-array, is totally different because it points to the whole array, not just a single int which may or may not be part of an array.

 int b[3] = { 5, 6, 7 };
 int (*bp)[3] = &b;   // bp points to the whole array b

     -------------
     V            |
 int b[3]         |  int (*bp)[3]
 |----|----|----| |  |----|
 |  5 |  6 |  7 |  --|--  |
 |----|----|----|    |----|

 bp[0][0] = 8;  // it now takes 2 dereferences to get to the int

     -------------
     V            |
 int b[3]         |  int (*bp)[3]
 |----|----|----| |  |----|
 |  8 |  6 |  7 |  --|--  |
 |----|----|----|    |----|
luser droog
  • 18,988
  • 3
  • 53
  • 105
  • Why does the casting `p=(int (*)[])p;` in my program does not work? – Aca Jul 03 '13 at 23:26
  • I've edited my answer to try to help more. This cast doesn't work because it *doesn't do anything at all.* After storing the (casted) value to p, it has the type of p again, a void*. Now you could put the cast in the argument list of the function call, but you're still using the wrong type: it's the wrong number of indirections. Just use an `int *`. You will almost never, ever need a pointer-to-array. – luser droog Jul 04 '13 at 00:04
  • Thanks luser droog, I got the point. I really appreciate the help of all of you. – Aca Jul 04 '13 at 18:25
1

This is an attempt to answer the question, after a long thought..:) Please correct me if I am wrong.

The following line p=(int (*)[])p; has no effect on type of p. p is still of type void *(so your casting is redundant) and since void * is compatible with any data pointer type so the function call is fine.

As for the first main() function you have figured it write.

Look here(good read to avoid confusion).

EDIT:

In short: You are trying to chage the type of the lhs of expression. This is never the aim of typecasting.

In detail:

Converting an expression of a given type into another type is known as type-casting.

So, let us analyse the line p=(int (*)[])p; Consider the rhs of the expression: (int (*)[])p. It is a pointer to arrays of integer pointers(as expected). But you want it to be assigned to void * (operator =). Now the compiler does not complain because void * admits pointer of any type. So pointer to arrays of integer pointers is again type-cast to void *(implicitly).

Try: p=(*whatever type you like*)p; and the compiler will not complain.(Do not expect it to run..:))

Community
  • 1
  • 1
Aman Deep Gautam
  • 8,091
  • 21
  • 74
  • 130
  • You are right, if you take out the line `p=(int (*)[ ])p;` the program still works, so it means the type is void* as you said. But now the question is: Why the casting has not effect?, on what cases this happens? – Aca Jul 03 '13 at 23:12
0

Here is a straight-forward adaptation of your code, combining the two variants of main() into one by using variables p and q:

#include <stdio.h>

static int counter = 0;
static int arreglo(int locArr[])
{
    locArr[0] = ++counter;
    printf("el arreglo es: %i\n", locArr[0]);
    return 0;
}

int main(void)
{
    int manArr[10];
    int (*p)[] = &manArr;

    /* Here passing pointer p is not allowed as expected, since
    ** the function has int* as argument so I need to cast it...
    */
    arreglo((int*)p);
    printf("manArr[0] = %d\n", manArr[0]);

    /* Or, since p is a pointer to an array, *p is the array,
    ** which can be passed directly without a cast.
    */
    arreglo(*p);
    printf("manArr[0] = %d\n", manArr[0]);

    void *q = NULL;

    /* This is a no-op */
    q = (int (*)[])q;   /* Cast from q to void to q to array */

    q = &manArr;  /* assign the address of the array manArr as above */

    /* Now given the pointer to array as parameter to function WORKS¡¡
    ** Why?. As before the function expects int* as argument not
    ** a pointer to an array
    */
    arreglo(q);
    printf("manArr[0] = %d\n", manArr[0]);

    /* Convert the void * back to a pointer to array, then dereference it */
    arreglo(*(int (*)[])q);
    printf("manArr[0] = %d\n", manArr[0]);

    return 0;
}

The reason you could not pass p to arreglo() without a cast was that the type 'pointer to array of int' is not the same as 'pointer to int', and the parameter to the function is equivalent to a 'pointer to int'. Your cast bludgeoned the compiler into agreeing with you, and you get away with it because even though the type of p is incorrect, its value (byte address) is the same as the address of the start element of the array which the function expects. As noted in the comments, since you have a pointer to an array and the function expects an array, you can pass *p without any cast being necessary.

The void * version (ab)uses the fact that a void * can be converted to any other pointer to object type without a cast in C (C++ would require a cast). The compiler is not able to warn you because you're using void *. Again, you get away with it because the byte address of &manArr is the same as the byte address of &manArr[0], even though the type is different. Note that the explicit cast (back to pointer to array) and then dereferencing works.

Be cautious with void *; it loses type information. It can be a boon; it can also hide major problems.

Normally, you don't use pointers to arrays at all — they exist in the 'esoterica' section of the C pantheon of types. You'd normally write code such as:

#include <stdio.h>

static int counter = 0;

static int arreglo(int locArr[])
{
    locArr[0] = ++counter;
    printf("el arreglo es: %i\n", locArr[0]);
    return 0;
}

int main(void)
{
    int manArr[10];
    int *p = manArr;

    arreglo(p);
    printf("manArr[0] = %d\n", manArr[0]);
    return 0;
}

Which is much simpler to understand.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks Jonathan, your explanations are very very good, they not just answear my question, they clarify many concepts on the use of arrays and cast. – Aca Jul 04 '13 at 18:07