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
是声明。所以这个声明说 x
是 const int_ptr
.
这里const
是修改int_ptr
; const int_ptr x
表示 x
是指向 int
的 const
指针。在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)
中,x
和y
不符合const
。他们指向const int
,但他们本身并不是const
。也就是说,x
的指针是可以改变的(不是const
)。它指向的东西是 const
的事实并不能使它成为 const
。所以关于忽略函数参数中的限定符的规则在这里不适用; x
和 y
没有限定符。所以 int add(const int *x, const int *y)
和 int add(int *x, int *y)
没有兼容的参数类型。
关于忽略参数类型中的限定符这一规则的原因是限定符只影响对象,而不影响值。如果我们有一个对象 x
是 const
,它不应该被改变(通过那个类型)。但是,如果我们从 x
中得到 int
值 3 并在表达式中使用它,那么说 3 是 const
就没有意义了。它只是一个在表达式中使用的值;没有分配给它的内存,我们可以在其中存储一个将 3 更改为 4 的新值。从 const int
检索到对象的值后,它只是一个 int
.
类似地,如果我们有一个volatile int x
,volatile
意味着编译器每次在表达式中使用时都必须得到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)
是兼容的函数声明。
我这里有这段代码:
#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
是声明。所以这个声明说 x
是 const int_ptr
.
这里const
是修改int_ptr
; const int_ptr x
表示 x
是指向 int
的 const
指针。在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)
中,x
和y
不符合const
。他们指向const int
,但他们本身并不是const
。也就是说,x
的指针是可以改变的(不是const
)。它指向的东西是 const
的事实并不能使它成为 const
。所以关于忽略函数参数中的限定符的规则在这里不适用; x
和 y
没有限定符。所以 int add(const int *x, const int *y)
和 int add(int *x, int *y)
没有兼容的参数类型。
关于忽略参数类型中的限定符这一规则的原因是限定符只影响对象,而不影响值。如果我们有一个对象 x
是 const
,它不应该被改变(通过那个类型)。但是,如果我们从 x
中得到 int
值 3 并在表达式中使用它,那么说 3 是 const
就没有意义了。它只是一个在表达式中使用的值;没有分配给它的内存,我们可以在其中存储一个将 3 更改为 4 的新值。从 const int
检索到对象的值后,它只是一个 int
.
类似地,如果我们有一个volatile int x
,volatile
意味着编译器每次在表达式中使用时都必须得到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)
是兼容的函数声明。