Home > Software design >  Interaction between const pointer and typedef and function declaration in c
Interaction between const pointer and typedef and function declaration in c

Time:12-16

I have this code here:

#include <stdio.h>

int add(const int* x, const int* y);

int main()
{
    int x = 4;
    int y = 3;

    printf("%d", add(&x, &y));

    return 0;
}

int add(int* x, int* y)
{
    return *x   *y;
}

When I compile it gives me an error: conflicting type for add
I know I have to put the const into the parameters of the function definition.
But if I add a typedef into the code like this:

#include <stdio.h>

typedef int* int_ptr;

int add(const int_ptr x, const int_ptr y);

int main()
{
    int x = 4;
    int y = 3;

    printf("%d", add(&x, &y));

    return 0;
}

int add(int_ptr x, int_ptr y)
{
    return *x   *y;
}

It compiled and gave me the output: 7
Why does this happen ?

CodePudding user response:

In const int* x, const int are the specifiers and *x is the declarator. (This separation is specified by the formal grammar of C and is a reason why writing declarations as int* x misrepresents the grammar.) This declaration says that *x is a const int, meaning x is a pointer to const int.

In typedef int* int_ptr, typedef int are the specifiers, and *int_ptr is the declarator. The declaration says that *int_ptr is an int, and typedef is a special specifier that modifies it so that int_ptr is declared to be a type, rather than an object (variable).

In const int_ptr x, const int_ptr are the specifiers, and x is the declaration. So this declaration says that x is a const int_ptr.

Here const is modifying int_ptr; const int_ptr x says that x is a const pointer to an int. In const int *x, const modifies int, so it says *x is a pointer to a const int, meaning x is a pointer to a const int.

For the most part, when a function is declared with parameter type lists, the parameters must have compatible types in each declaration of the function. But there is an exception: C 2018 6.7.6.3 15 says:

… (In the determination of type compatibility and of a composite type, … each parameter declared with qualified type is taken as having the unqualified version of its declared type.)

This says that, when determining whether int add(const int_ptr x, const int_ptr y) is compatible with int add(int_ptr x, int_ptr y), the const qualifiers are ignored. Then the parameter types are the same, so the function declarations are compatible.

In int add(const int *x, const int *y), x and y are not qualified with const. They point to const int, but they themselves are not const. That is, the pointer that is x can be changed (it is not const). The fact that it points to something that is const does not make it const. So the rule about ignoring qualifiers in function parameters does not apply here; there are no qualifiers on x and y. So int add(const int *x, const int *y) and int add(int *x, int *y) do not have compatible parameter types.

The reason for this rule about ignoring qualifiers in parameter types comes from the fact that qualifiers only affect objects, not values. If we have an object x that is const, it should not be changed (through that type). But, if we have gotten the int value 3 from x and are using it in an expression, there would be no meaning to saying 3 is const. It is just a value being used in an expression; there is no memory assigned to it where we could store a new value that would change 3 to 4. Once the value of an object is retrieved from a const int, it is just an int.

Similarly, if we have a volatile int x, the volatile means the compiler must get the value of x each time it is used in an expression, because volatile means something could be changing the memory of x in ways the compiler does not know about. But, once we have gotten the value of x from memory, it is just a value. We are done with the “you have to get it from memory” part, so the volatile has no more effect.

Since function arguments are always passed by value, the qualifiers are irrelevant to the caller. When a function is declared with void foo(const int x), the const has a meaning inside the function: The compiler must issue a diagnostic if anything inside the function attempts to modify x with its const-qualified type. But the caller does not care: The caller only passes a value. When the function starts, it creates a local x for itself, and that x is const, but it has no effect on the caller. So void foo(int x) and void foo(const int x) are compatible function declarations.

  • Related