2

Given the following function definition:

void f(int const ** ptr_ptr_a);

how would you understand what the function takes and what it guarantees.

  1. The function takes an int ** as an argument and guarantees no changes to happen explicitly and only to **ptr_ptr_a inside the function scope.
  2. The function takes an int const ** as the function argument, meaning it is imposing that the passed argument needs to be constant before it entered the function scope.

The motivation comes from trying to understand the warning given by the following example:

void f(int const **ptr_ptr_a){
}

int main(int argc, char *argv[])
{
    int * ptr_a;
    f(& ptr_a); // warning: passing argument 1 of ‘f’ from incompatible pointer type [-Wincompatible-pointer-types]
}

Assuming definition 1. is correct

The warning is useless and makes us think that the inside of the function makes worries about how the variable behaves outside the function scope.

Assuming definition 2. is correct

Means that the declarations arguments and implying what the qualifiers of the arguments passed to the function during calling should have, in which case I'm confused.

I would kindly ask for an explanation on why is this useful given that only pass by value is possible in C.

kdopen
  • 8,032
  • 7
  • 44
  • 52
TheMeaningfulEngineer
  • 15,679
  • 27
  • 85
  • 143
  • 3
    You should add this to your bookmarks: https://cdecl.org/ – r3mainer Jul 17 '17 at 12:46
  • 1) C does not support symbolic _constants_ (except enum-constants which are not relevant here). `const` is a guarantee the programmer gives to the compiler. 2) Wrt you the title: the argument (the standard uses the term _parameter_) passed is **not** `const`. 3) The recommended warnings are rarely "useless". Treat them as errors. – too honest for this site Jul 17 '17 at 12:51
  • 1
    I don't understand the downvotes. It's a good question as this warning would not occur with a single pointer argument. I'm also confused about that and agree with the OPs assumptions. – Andre Kampling Jul 17 '17 at 12:55
  • Please elaborate on the down votes. The suggested link that is gaining upvotes gives a syntax error for the function in the example, which is succesfully being built with both `clang` and `gcc`. And if the question is unclear please state what part or if it's not practic enough in which case I woull migrate it to https://softwareengineering.stackexchange.com/ – TheMeaningfulEngineer Jul 17 '17 at 12:56
  • @Olaf 1) Why couldn't it be a guarantee a library is giving to the programmer? 2) Please rewrite your suggestion on the title, I didn't understand it. 3) You can contribute by explaining how the warning helps in the given example. – TheMeaningfulEngineer Jul 17 '17 at 13:01
  • 1
    Possible duplicate of [Double pointer const-correctness warnings in C](https://stackoverflow.com/questions/5055655/double-pointer-const-correctness-warnings-in-c) – ad absurdum Jul 17 '17 at 13:12
  • @TheMeaningfulEngineer You're right; it only appears to work with type definitions. Try [`int const ** ptr_ptr_a`](https://cdecl.org/?q=int+const+**+ptr_ptr_a) – r3mainer Jul 17 '17 at 13:12
  • @DavidBowling It's close, but not a duplicate. Different warning message for one thing. – kdopen Jul 17 '17 at 13:15
  • 1) What does a libary have to do with the language itself? You are asking about the language. A libray can guarantee whatever it wants. There is no immanent **need** for using `const` at all. For the rest: think about it! A good C book might be a good idea - or use cdecl as suggested. You need a good tutorial. – too honest for this site Jul 17 '17 at 13:23
  • 1
    @Olaf Thank you for your kind suggestions :). If I am writing the library I would worry about giving the potential user as much information on what it can expect from the library functions. In those cases `const` and how to interpret the function declaration gives a lot of information. – TheMeaningfulEngineer Jul 17 '17 at 13:32
  • @Olaf "const is a guarantee the programmer gives to the compiler." Well, not exactly. The parameter list of a function declaration is a promise from the function's author to its callers. – kdopen Jul 17 '17 at 13:33
  • @kdopen: I would assume the "function's author" **is** the programmer. Typically this is the same writing the declarator, of course. I don't see what you want to add with your comment, it should be clar I did not mean Charles Dickens. – too honest for this site Jul 17 '17 at 13:38
  • @TheMeaningfulEngineer: Where did I say different?? Please read comments carefully. – too honest for this site Jul 17 '17 at 13:40
  • That slight difference (in terms of who is receiving the promise) is the entire thrust of the OP's question - "what the function takes and what it guarantees." If all you have is the header file (i.e. a function prototype), and a precompiled binary, understanding the contract becomes very important. – kdopen Jul 17 '17 at 13:40

2 Answers2

2

The declaration int const ** p (or const int ** p) states that p is a pointer to a pointer to an int which is const.

Thus the contract being specified is that f() will not perform an operation such as the following

**ptr_ptr_a = 1;

I.e. it will not write to the referenced int.

It is, however, perfectly free to change the value of ptr_a thus

*ptr_ptr_a = 0;

To remove the warning, ptr_a needs to be declared as int const * ptr_a; or const int * ptr_a; (which is more idiomatic).

Now, is this warning useless? Consider an embedded controller where the pointer size is different for pointers into RAM and ROM/FLASH (and yes, I've worked on those). Your current ptr_a could not address an int which resided in high read-ony memory.

kdopen
  • 8,032
  • 7
  • 44
  • 52
  • While legal, `*ptr_ptr_a = 0;` is strongly discouraged for reasons of clarity&readability. Use the `NULL` macro if you want a _null pointer constant_. – too honest for this site Jul 17 '17 at 13:33
  • Actually, the NULL macro is just syntactic sugar. Any experienced C programmer knows that `0` is the null-pointer-constant. Remember, the compiler never sees `NULL` - it's stripped out by the preprocessor. – kdopen Jul 17 '17 at 13:36
  • Where did I say different? Please read comments carefully and understand the implications (as a sidenote: `NULL` is typically defined as `(void *)0` - C is not C++. This will result in (intentional) warnings about a pointer conversion if use accidentally with non-pointer LHS. Don't work against your compiler, but use its features. As a sidenote: the qualifier discussed in this question also is syntactic sugar on systems executing from RAM (all desktops and servers). – too honest for this site Jul 17 '17 at 13:44
  • And pointers to different qualified versions of the same type cannot have different sizes according to the standard. You always can pass an `int * to a `const int *` parameter, etc. – too honest for this site Jul 17 '17 at 13:48
  • And we've had this discussion before - Embedded C compilers are not always strictly conforming and programmers don't always have the option to refuse to use them. As a counter-example, you can't pass a `const int *` to an `int *` parameter, without generating at least a warning - and that works perfectly well in the architecture I described. – kdopen Jul 17 '17 at 13:55
  • And that qualifier is not syntactic sugar. It makes a very real promise to the caller of the function, and the compiler enforces it when compiling the function's definition. – kdopen Jul 17 '17 at 13:58
  • I really wonder how we wrote code before `const`, or in languages not supporting it like Assembly. A promise is not a programming construct, but always an informal guarantee **in the first place**. `const` just gives the compiler a hint about the intentions. Pretty much like using "headline" tags in text instead of just using a bold, larger font. – too honest for this site Jul 17 '17 at 14:03
  • I don't see any hint the question is about bare-metal embedded systems, my comment was very clear. Not sure if you just don't want to understand or really can't. – too honest for this site Jul 17 '17 at 14:05
  • @Olaf Nope, I fully understand what you are saying - I simply don't agree with you. – kdopen Jul 17 '17 at 14:08
  • @kdopen `Useless` was an unskillful word on my side. What I meant is that the warning is saying: `I'm assuming that you're thinking about f( const int const * *ptr_ptr_a) even though you're reading f(const int ** ptr_ptr_a), so here is a warning` – TheMeaningfulEngineer Jul 17 '17 at 14:15
  • @TheMeaningfulEngineer Not to worry, you'd be amazed how many people with decades of C experience can still get confused by multi-level pointers: especially when you start throwing in 'const' :) That's why so many of the interview questions I ask focus on them – kdopen Jul 17 '17 at 14:17
  • @kdopen I've tried it now with `void f(int const * const * ptr_ptr_a)`. Gives a warning again. Now that is a stupid warning. It's a perfectly safe cast. – TheMeaningfulEngineer Jul 17 '17 at 16:38
0

Neither definition is correct. When you declare

void f(int const ** ptr_ptr_a);

you're telling the compiler that this function takes a pointer to a pointer to a const int. The warning is the compiler telling you that you are not abiding by your own contract.

Mark Benningfield
  • 2,800
  • 9
  • 31
  • 31