tags:

views:

1256

answers:

7

This question goes out to the C gurus out there:

In C, it is possible to declare a pointer as follows:

char (* p)[10];

.. which basically states that this pointer points to an array of 10 chars. The neat thing about declaring a pointer like this is that you will get a compile time error if you try to assign a pointer of an array of different size to p. It will also give you a compile time error if you try to assign the value of a simple char pointer to p. I tried this with gcc and it seems to work with ANSI, C89 and C99.

It looks to me like declaring a pointer like this would be very useful - particularly, when passing a pointer to a function. Usually, people would write the prototype of such a function like this:

void foo(char * p, int plen);

If you were expecting a buffer of an specific size, you would simply test the value of plen. However, you cannot be guaranteed that the person who passes p to you will really give you plen valid memory locations in that buffer. You have to trust that the person who called this function is doing the right thing. On the other hand:

void foo(char (*p)[10]);

..would force the caller to give you a buffer of the specified size.

This seems very useful but I have never seen a pointer declared like this in any code I have ever ran across.

My question is: Is there any reason why people do not declare pointers like this? Am I not seeing some obvious pitfall?

Thanks in advance

+3  A: 

The obvious reason is that this code doesn't compile:

extern void foo(char (*p)[10]);
void bar() {
  char p[10];
  foo(p);
}

The default promotion of an array is to an unqualified pointer.

Also see this question, using foo(&p) should work.

Keith Randall
Of course foo(p) will not work, foo is asking for a pointer to an array of 10 elements so you need to pass the address of your array...
Brian R. Bondy
AndreyT
Keith Randall
A: 

Maybe I'm missing something, but... since arrays are constant pointers, basically that means that there's no point in passing around pointers to them.

Couldn't you just use void foo(char p[10], int plen); ?

fortran
Arrays are NOT constant pointers. Read some FAQ on arrays, please.
AndreyT
For what matters here (unidimensional arrays as parameters), the fact is that they decay to constant pointers. Read a FAQ on how to be less pedantic, please.
fortran
+1  A: 

Well, simply put, C doesn't do things that way. An array of type T is passed around as a pointer to the first T in the array, and that's all you get.

This allows for some cool and elegant algorithms, such as looping through the array with expressions like

*dst++ = *src++

The downside is that management of the size is up to you. Unfortunately, failure to do this conscientiously has also led to millions of bugs in C coding, and/or opportunities for malevolent exploitation.

What comes close to what you ask in C is to pass around a struct (by value) or a pointer to one (by reference). As long as the same struct type is used on both sides of this operation, both the code that hand out the reference and the code that uses it are in agreement about the size of the data being handled.

Your struct can contain whatever data you want; it could contain your array of a well-defined size.

Still, nothing prevents you or an incompetent or malevolent coder from using casts to fool the compiler into treating your struct as one of a different size. The almost unshackled ability to do this kind of thing is a part of C's design.

Carl Smotricz
+1  A: 

You can declare an array of characters a number of ways:

char p[10];
char* p = (char*)malloc(10 * sizeof(char));

The prototype to a function that takes an array by value is:

void foo(char* p); //cannot modify p

or by reference:

void foo(char** p); //can modify p, derefernce by *p[0] = 'f';

or by array syntax:

void foo(char p[]); //same as char*
s1n
Don't forget that a fixed-size array can also be dynamically allocated as `char (*p)[10] = malloc(sizeof *p)`.
AndreyT
See here for a more detailed discussion between the differences of char array[] and char *ptr here. http://stackoverflow.com/questions/1807530/difference-between-using-character-pointers-and-character-arrays/1807766#1807766
tommieb75
+22  A: 

What you are saying in your post is absolutely correct. Any C developer comes to exactly the same discovery and to exactly the same conclusion when (if) they reach certain level of proficiency with C language.

When the specifics of your application area call for an array of specific fixed size (array size is a compile-time constant), the only proper way to pass such an array to a function is by using a pointer-to-array parameter

void foo(char (*p)[10]);

(in C++ language this is also done with references

void foo(char (&p)[10]);

).

This will enable language-level type checking, which will make sure that the array of exactly correct size is supplied as an argument. In fact, in many cases people use this technique implicitly, without even realizing it, hiding the array type behind a typedef name

typedef int Vector3d[3];

void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);

Note, that the above code is invariant with relation to Vector3d type being an array or a struct. You can switch the definition of Vector3d at any time from an array to a struct and back, and you won't have to change the rest of the code (there are exceptions to this, but within the context of this discussion this is true).

However, you won't see this method of array passing used explicitly too often, simply because too many people get confused by a rather convoluted syntax and are simply not comfortable enough with such features of C language to use them properly. For this reason, in average real life, passing an array as a pointer to its first element is a more popular approach. It just looks "simpler".

But in reality, using the pointer to the first element for array passing is a very niche technique, a trick, which serves a very specific purpose: its one and only purpose is to facilitate passing arrays of different size (i.e. run-time size). If you really need to be able to process arrays of run-time size, then the proper way to pass such an array is by a pointer to its first element with the concrete size supplied by an additional parameter

void foo(char p[], unsigned plen);

Actually, in many cases it is very useful to be able to process arrays of run-time size, which also contributes to the popularity of the method. Many C developers simply never encounter (or never recognize) the need to process a fixed-size array, thus remaining oblivious to the proper fixed-size technique.

Nevertheless, if the array size is fixed, passing it as a pointer to an element

void foo(char p[])

is a major technique-level error, which unfortunately is rather widespread these days. A pointer-to-array technique shall be used instead in such cases.

Another reason that might hinder the adoption of the fixed-size array passing technique is the dominance of naive approach to typing of dynamically allocated arrays. For example, if the program calls for fixed arrays of type char[10] (as in your example), an average developer will malloc such arrays as

char *p = malloc(10 * sizeof *p);

This array cannot be passed to a function declared as

void foo(char (*p)[10]);

which confuses the average developer and makes them abandon the fixed-size parameter declaration without giving it a further thought. In reality though, the root of the problem lies in the naive malloc approach. The malloc technique above is, again, reserved for arrays of run-time size. If the array type has compile-time size, the proper way to malloc it would look as follows

char (*p)[10] = malloc(sizeof *p);

This, of course, can be easily passed to the above declared foo

foo(p);

and the compiler will perform the proper type checking. But again, this is overly confusing to an unprepared C developer, which is why you won't see it in too often in the "typical" average everyday code.

AndreyT
Very nice and elaborated answer.
Johannes Schaub - litb
@Johannes: You are being sarcastic, no? There are to many veiled insults in that answer to call it "nice" :)))
AndreyT
@AndreyT: Very nice answer indeed. Thanks for the taking the time to elaborate on it.
figurassa
A: 

I would not recommend this solution

typedef int Vector3d[3];

since it obscures the fact that Vector3D has a type that you must know about. Programmers usually dont expect variables of the same type to have different sizes. Consider :

void foo(Vector3d a) {
   Vector3D b;
}

where sizeof a != sizeof b

Per Ekman
He was not suggesting this as a solution. He was simply using this as an example.
figurassa
A: 
figurassa