c中const指针与typedef和函数声明的交互

Interaction between const pointer and typedef and function declaration in c

我这里有这段代码:

#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;
}

编译时出现错误:添加类型冲突
我知道我必须将 const 放入函数定义的参数中。
但是如果我像这样在代码中添加一个 typedef:

#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;
}

它编译并给了我输出:7
为什么会这样?

const int* x中,const int是说明符,*x是声明符。 (这种分离由 C 的形式语法指定,并且是将声明写成 int* x 错误表示语法的原因。)这个声明说 *x 是一个 const int,意思是 x 是指向 const int.

的指针

typedef int* int_ptr中,typedef int是说明符,*int_ptr是声明符。声明说 *int_ptr 是一个 int,而 typedef 是一个特殊的说明符,它修改它以便 int_ptr 被声明为一个类型,而不是一个对象(变量).

const int_ptr x中,const int_ptr是说明符,x是声明。所以这个声明说 xconst int_ptr.

这里const是修改int_ptrconst int_ptr x 表示 x 是指向 intconst 指针。在const int *x中,const修改了int,所以它说*x是指向const int的指针,意思是x是指向const int.

在大多数情况下,当使用参数类型列表声明函数时,参数必须在函数的每个声明中具有兼容的类型。但有一个例外: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.)

这表示,在确定 int add(const int_ptr x, const int_ptr y) 是否与 int add(int_ptr x, int_ptr y) 兼容时,忽略 const 限定符。那么参数类型相同,所以函数声明是兼容的。

int add(const int *x, const int *y)中,xy不符合const。他们指向const int,但他们本身并不是const。也就是说,x的指针是可以改变的(不是const)。它指向的东西是 const 的事实并不能使它成为 const。所以关于忽略函数参数中的限定符的规则在这里不适用; xy 没有限定符。所以 int add(const int *x, const int *y)int add(int *x, int *y) 没有兼容的参数类型。

关于忽略参数类型中的限定符这一规则的原因是限定符只影响对象,而不影响值。如果我们有一个对象 xconst,它不应该被改变(通过那个类型)。但是,如果我们从 x 中得到 int 值 3 并在表达式中使用它,那么说 3 是 const 就没有意义了。它只是一个在表达式中使用的值;没有分配给它的内存,我们可以在其中存储一个将 3 更改为 4 的新值。从 const int 检索到对象的值后,它只是一个 int.

类似地,如果我们有一个volatile int xvolatile意味着编译器每次在表达式中使用时都必须得到x的值,因为volatile 意味着某些东西可能会以编译器不知道的方式改变 x 的内存。但是,一旦我们从内存中获得 x 的值,它就只是一个值。我们已经完成了“你必须从内存中获取它”的部分,因此 volatile 不再起作用。

由于函数参数总是按值传递,因此限定符与调用者无关。当使用 void foo(const int x) 声明函数时,const 在函数内部具有含义:如果函数内部的任何内容试图使用其 [=30= 修改 x,编译器必须发出诊断]-限定类型。但调用者并不关心:调用者只传递一个值。函数启动时,它为自己创建了一个局部的x,而那个x就是const,但是对调用者没有影响。所以 void foo(int x)void foo(const int x) 是兼容的函数声明。