12
void foo(const char *s);

is equivalent to:

void foo(const char s[]);

Are there similar equivalents to the following two?

void foo(char * const s);
void foo(const char * const s);
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 10
    The caller doesn't care if you promise not to change your (you = `foo`) local variable (`s`), so I fail to see the difference of `const char*` and `const char* const` *for this example*. – bitmask Oct 27 '11 at 07:22
  • 1
    You may find this useful: http://stackoverflow.com/questions/7880384/const-correctness-const-pointer-as-function-argument – Blagovest Buyukliev Oct 27 '11 at 07:26
  • 4
    `void foo(char * const s);` is just a declaration. There is no difference of `void foo(char * const s);` and `void foo(char * s);` `const` is important for function definition only: `void foo(char * const s) { .... } ` – Alexey Malistov Oct 27 '11 at 07:27
  • 1
    Are you asking about C, C++, or both? They are very different languages. – Mike Seymour Oct 27 '11 at 07:49
  • @AlexeyMalistov: I am actually asking about definitions, but to keep the example code short, I only posted the declarations. Silly me... – fredoverflow Oct 27 '11 at 07:55
  • @MikeSeymour: Are they different in this particular aspect I am asking about? Then I would like two different answers, one per language. – fredoverflow Oct 27 '11 at 07:56
  • @FredOverflow: You can't, but even if you could, you probably shouldn't. *Typing* an array in your code when it is going to be processed as a *pointer* will just make it harder to read (you/others will have to perform the same conversion that the compiler does: from array to pointer). Some code review tools actually flag this as a potential for coding errors, where while reading the code you might be inclined to think that the argument is an array when it is not. – David Rodríguez - dribeas Oct 27 '11 at 08:07

4 Answers4

14

In C++, the compiler will automatically convert function parameters of type array of N elements of type T (where N can be unknown) into pointer to T. In the same transformation top level const qualifiers for the arguments are dropped:

void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )

That means that in the pointer case, the top level const qualifier is removed, and in the case of the array it is converted to pointer to. Now, on the other hand you cannot mix both, only because you cannot declare a constant array of N elements of type T, since arrays are always const. That means that you cannot write:

void f( const int a[] const );  // Invalid type definition

As the type of the parameter is invalid. If it was a valid type, then the conversions would apply, but because it is not, the compiler will reject the code before trying to perform the conversion.

This is treated in §8.3.5/3 of the C++03 standard (and probably somewhere close in C++11)

A single name can be used for several different functions in a single scope; this is function overloading (clause 13). All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the function type. The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, several transformations take place upon these types to determine the function type. Any cv-qualifier modifying a parameter type is deleted. [Example: the type void(*)(const int) becomes void(*)(int) —end example] Such cv-qualifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. If a storage-class-specifier modifies a parameter type, the specifier is deleted. [Example: register char* becomes char* —end example] Such storage-class-specifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. The resulting list of transformed parameter types is the function’s parameter type list.

Note that since the compiler will perform that conversion, it is better to write the actual type that is going to be used by the compiler, following the principle of least surprise:

void f( int a[10] ) { a[5] = 7; }

The compiler is not going to check that the passed array has 10 elements, it reads the declaration as void f( int * ), and will gladly accept a call with an array of less elements or even no array at all (a pointer to a single int). Using a pointer in the actual code:

void f( int *a ) { a[5] = 7; }

Will likely trigger some alarms in a code review: are we guaranteed that in all calls to f the argument will be at least 6 elements big? Should we not pass also the size just in case?

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 2
    +1 for *since the compiler will perform that conversion, it is better to write the actual type that is going to be used by the compiler, following the principle of least surprise*. Great point. And the following text after this is great as well. – Nawaz Oct 27 '11 at 09:00
13

You cannot in C89, but in C99 you can declare the equivalents as:

void foo(char s[const]);
void foo(const char s[const]);
caf
  • 233,326
  • 40
  • 323
  • 462
0

this will be useful in some cases:

class AA {
  void foo(char a[]);
  void foo(const char a[]);
};

void AA::foo(char* const a) { … }
void AA::foo(const char* const a) { … }

and in C:

extern void foo(char a[]);
extern void fooc(const char a[]);


void foo(char* const a) { … }
void fooc(const char* const a) { … }
justin
  • 104,054
  • 14
  • 179
  • 226
  • @Chris Lutz does it not qualify as "passing a constant pointer disguised as an array"? – justin Oct 27 '11 at 07:54
  • @Justin: The question is not just the title. Your answer is unrelated to the body of the question. – Gorpik Oct 27 '11 at 08:03
  • @Gorpik i see it as related to the body as well. i also noted that it will be useful in *some* cases. there certainly are valid uses for this form, and it satisfies the question and the body in those cases. – justin Oct 27 '11 at 08:12
  • The body asks how you can change the declaration from getting `char * const a` to something that uses `[]` instead of `*`. The first answer above says you should pass it like `char a[const]` which answers the question. In your answer, I don't see any `[]`s – Shahbaz Oct 27 '11 at 08:27
  • 1
    @Shahbaz every one of the declarations in my answer uses `[]`. the question is tagged c and c++ -- gcc and clang (the only compilers i tested) both rejected `char a[const]` when compiled as c++. my answer is a practical solution which satisfies both c and c++. some people do use this form. – justin Oct 27 '11 at 08:39
  • Sorry, my bad, what I meant was you don't really tell how to declare a function getting const pointers using []. The answer, as you can see was `char a[const]`. – Shahbaz Oct 27 '11 at 09:01
  • @Shahbaz a) the second `const` does not matter in the declaration. it matters only in the definition. b) the answer is not `char a[const]` if you are trying to write a c++ program using the compilers i use most often (gcc and clang). when i said both compilers rejected the program, that means they produced errors. – justin Oct 27 '11 at 09:13
0

I thought that a pointer can be null, while an array argument cannot be null (and that the compiler is permitted to optimize knowing that; however on a simple example gcc-4.6 don't do such an optimization, even with -O3).

I am expecting that the compiler would optimize differently the two functions below. It does not. I don't have my C standard at hand to check if it could remove the test in ss below.

int s (int *t)
{
  if (!t)
    return 0;
  return t[0] + t[1];
}


int ss (int t[])
{
  if (!t) // never false, since t is an array!!
    return 0;
  return t[0] + t[1];
}
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 1
    No, in your `ss` function, `t` is not an array; its actual type is `int*`. Despite what the code actually says, the compiler transforms all array-type parameters(`T x[]`) into pointer-type parameters(`T* x`). – James McNellis Oct 28 '11 at 21:40
  • Is it also true in C++? I do remember having read that pointers and arrays are different in that respect (and even, IIRC, that Linus Torvalds had an argument with GCC guys around that)... – Basile Starynkevitch Oct 28 '11 at 21:50
  • It is true in both C and C++. – James McNellis Oct 28 '11 at 21:56
  • @Basile Starynkevitch: In C99, if you declare the function as `int ss(int t[static 2])` it specifies that the pointer points to at least two `int` elements (and the compiler is therefore allowed to assume `t != NULL`). – caf Nov 06 '11 at 09:12