2

我试图理解一件事。

我知道我不能改变常量指针的值,但我可以改变它的地址,如果我用以下方式初始化一个指针:

int foo = 3;
const int *ptr = &foo;
*ptr = 6; // throws an error
int bar = 0;
ptr = &bar; // foo == 0

现在,假设我声明(/define,我不记得是哪一个)一个函数:

void change(const int arr[], int size);

int main() {
   int foo[2] = {};
   change(foo, 2);
   std::cout << foo[0];
}

void change(const int arr[], int size) {
   // arr[0] = 5 - throws an error
   int bar = 5;
   arr = &bar;
}

上面代码的最后一行不会引发任何错误。但是,当函数结束并且我显示第一个元素时,它显示 0 - 所以没有任何改变。

为什么呢?

在这两种情况下,我都有常量指针,我尝试更改它的地址。在第一个示例中它有效。在第二个中它没有。


我还有一个问题。有人告诉我,如果我想将两指针类型传递给函数, const 关键字将无法按预期工作。真的吗?如果是这样,那是什么原因呢?

4

4 回答 4

18

你把术语搞砸了,所以我要从那里开始,因为我认为这是你困惑的主要原因。考虑:

int x;
int* p = &x;

x是一个int并且p是一个“指向”的指针int修改值p意味着改变p自身指向其他地方。指针值是它保存的地址。该指针p保存int对象的地址。改变指针的值并不意味着改变int对象。例如,p = 0;将修改p的值。

除此之外,地址p不是它持有的地址。如果你这样做了,地址p将是你得到&p的,并且是“指向指针的指针int”类型。也就是说,地址p是您可以p在内存中找到指针的位置。由于对象不会在内存中移动,因此不存在“更改其地址”之类的事情。

所以现在已经不碍事了,让我们了解什么是常量指针。const int*不是一个常量指针。它是一个指向常量对象的指针。它指向的对象是常量,而不是指针本身。常量指针类型看起来更像int* const. 这里const适用于指针,所以它是“指向的const指针int”类型。

好的,现在我将快速给你一个简单的方法来记住声明和定义之间的区别。如果你买了一本字典,里面只有一个单词列表,你真的会称它为字典吗?不,字典应该充满单词的定义。它应该告诉你这些词是什么意思。没有定义的字典只是声明这样的词存在于给定的语言中。因此,声明表明某物存在,而定义则给出了它的含义。在你的情况下:

// Declaration
void change(const int arr[], int size);

// Definition
void change(const int arr[], int size) {
   // arr[0] = 5 - throws an error
   int bar = 5;
   arr = &bar;
}

现在在这里解释这个问题。没有数组参数类型这样的东西。任何数组类型参数都将转换为指针。所以声明change实际上等同于:

void change(const int arr*, int size);

当你这样做时,arr = &bar;你只是将地址分配给bar指针arr。这对arr指向的数组元素没有影响。为什么要呢?您只是在更改arr指向的位置,而不是它指向的对象。事实上你不能改变它指向的对象,因为它们是const ints。

于 2013-04-18T19:59:10.810 回答
5

我知道我不能改变常量指针的值,但我可以改变它的地址

不。你不能改变任何东西的地址。你的意思是你不能改变它指向的对象,但你可以改变指针本身?因为这就是事实——在指向 const 类型的情况下。但是,如果你有一个指向非 const 对象的 const 指针,那么你不能改变指针,你只能改变它指向的任何东西。

附录(编辑):一个方便的经验法则是const适用于左侧的东西,除非左侧没有任何东西,因为它适用于右侧的类型。例子:

const int *ptr;
int const *ptr; // these two are equivalent: non-const pointer to const int

int *const ptr; // const pointer to non-const int

int const *const ptr; // const pointer to const int
const int *const ptr; // same as above

但是,当函数结束并且我显示第一个元素时,它显示 0 - 所以没有任何改变。

范围。arr是一个函数参数 - 所以它是函数的本地参数。无论你用它做什么,它在函数之外都不会有效。要实现您想要的,请将其声明为参考:

void change(const int *&arr, int size)

有人告诉我,如果我想将两指针类型传递给函数, const 关键字将无法按预期工作。真的吗?

这取决于你的期望是什么。如果您仔细阅读标准并有适当的期望,那么它确实会按预期工作。例子:

const int **ptr; // pointer to pointer to const int
int const **ptr; // same as above
const int *const *ptr; // pointer to const pointer to const int

等等。您可以使用CDecl生成更多这些时髦的声明

于 2013-04-18T19:53:41.933 回答
2

首先是使用正确的术语,这实际上有助于理解:

const int *ptr = &foo;

那是指向常量整数的指针,而不是指向整数的常量指针。您无法更改指向的对象,但可以更改指针以引用不同的对象。

void change(const int arr[], int size);

该签名由编译器处理为void change( const int *arr, int size ),我建议您将其键入,因为它会减少混淆。在调用函数的地方change(foo,2),编译器将转换具有类型的参数foo(类型为int[2])(这两种转换通常称为数组衰减到指针)。&foo[0]const int*

现在就像在第一个代码块中一样,您无法更改指向的内存,但您可以更改指针以引用不同的对象。

此外,在 C++ 中,默认模式是按值传递。arr里面的指针change是value的副本&foo[0]。在函数内部,您正在更改该副本,但这不会影响函数上下文之外的任何内容。

于 2013-04-18T20:00:06.220 回答
0

const int *是在做它应该做的事情,让你困惑的是它的目的。

将其视为指向只读 int 的指针。你可以指向任何你想要的 int,但无论如何它都是只读的。

例如,您可以使用它来遍历类型数组const int

于 2013-04-18T19:56:30.200 回答