const指针有什么意义?

我不是在讨论const指针的指针,而是指向const指针本身。

我正在学习C和C ++,超越了基本的东西,直到今天,我才意识到指针是按值传递给函数的,这是有道理的。 这意味着在一个函数内,我可以使复制的指针指向其他一些值,而不影响来自调用者的原始指针。

那么有一个函数头说什么点:

void foo(int* const ptr); 

在这样的函数内部,你不能使ptr指向其他的东西,因为它是const的,你不希望它被修改,但是像这样的函数:

 void foo(int* ptr); 

工作也一样! 因为无论如何都会复制指针,即使修改副本,调用方中的指针也不受影响。 那么const的优点是什么?

const是一个你应该用来追求非常重要的C ++概念的工具:

在编译时查找错误,而不是运行时,让编译器强制执行你的意思。

即使它不改变function,当你做你不想做的事情时,添加const产生编译器错误。 想象下面的错字:

 void foo(int* ptr) { ptr = 0;// oops, I meant *ptr = 0 } 

如果你使用int* const ,这会产生一个编译错误,因为你正在改变这个值到ptr 。 通过语法添加限制通常是件好事。 只是不要过分 – 你给的例子是大多数人不打扰使用const

const于使用const参数,因为这会启用更多的编译器检查:如果我不小心在函数内部重新分配参数值,编译器会咬我。

我很less重用variables,创build新variables来保存新值是更清晰的,所以基本上我所有的variables声明都是const (除了一些情况,比如constvariables会阻止代码工作)。

请注意,这只在函数的定义中才有意义。 它不属于声明 ,这是用户看到的。 而用户不关心我是否使用const函数内部的参数。

例:

 // foo.h int frob(int x); 
 // foo.cpp int frob(int const x) { MyConfigType const config = get_the_config(); return x * config.scaling; } 

注意参数和局部variables都是const 。 这些都不是必要的,但function甚至更大一些,这一再使我免于犯错误。

你的问题涉及更一般的东西:函数参数应该是const?

值参数的常量(比如你的指针)是一个实现细节并不构成函数声明的一部分。 这意味着你的function永远是这样的:

 void foo(T); 

完全取决于函数的实现者是否要以可变或恒定的方式使用函数范围参数variables:

 // implementation 1 void foo(T const x) { // I won't touch x T y = x; // ... } // implementation 2 void foo(T x) { // l33t coding skillz while (*x-- = zap()) { /* ... */ } } 

所以,按照简单的规则,不要把const放在声明(header)中,如果你不想或者不需要修改variables,就把它放在定义(实现)中。

声明中放弃了顶级const限定符,因此问题中的声明声明了完全相同的函数。 另一方面,在定义 (实现)中,编译器将validation如果将指针标记为const,则不会在函数体内对其进行修改。

const关键字有很多,这是一个相当复杂的。 一般来说,在你的程序中增加大量的const被认为是很好的编程习惯,在网上search“const正确性”,你会发现很多关于这个的信息。

const关键字是一个所谓的“types限定符”,其他的是volatilerestrict 。 至lessvolatile会遵循与const一样的(令人困惑的)规则。


首先,const关键字有两个目的。 最明显的一点是通过使数据(和指针)成为只读来保护数据(和指针)免于故意或意外的滥用。 任何修改constvariables的尝试都将在编译时被编译器发现。

但是在任何带有只读存储器的系统中还有另一个目的,即确保在这样的存储器内部分配一定的variables – 例如可以是EEPROM或闪存。 这些被称为非易失性存储器,NVM。 在NVM中分配的variables当然仍然遵循constvariables的所有规则。

有几种不同的方法可以使用const关键字:

声明一个常量variables。

这可以做为或者

 const int X=1; or int const X=1; 

这两种forms完全相同 。 后者风格被认为是不好的风格,不应该使用。

第二行之所以被认为是不好的风格,可能是因为诸如static和extern之类的“存储类说明符”也可以在实际types, int static之后声明。但是对于存储类说明符,这样做是标记为C委员会陈旧的特征(ISO 9899 N1539草案,6.11.5)。 因此,为了一致性,不应该以这种方式编写types限定符。 它没有其他目的,但无论如何混淆读者。

声明一个指向常量variables的指针。

 const int* ptr = &X; 

这意味着“X”的内容不能被修改。 这是你声明这样的指针的正常方式,主要是作为“const正确性”的函数参数的一部分。 因为'X'实际上不必被声明为const,所以它可以是任何variables。 换句话说,你总是可以将一个variables“升级”为const。 从技术上讲,C也允许通过明确的types转换从const降级到一个简单的variables,但这样做被认为是不好的编程,编译器通常会给出警告。

声明一个常量指针

 int* const ptr = &X; 

这意味着指针本身是不变的。 您可以修改它指向的内容,但不能修改指针本身。 这没有太多的用处,有一些,如确保指针指向(指针指针)没有地址改变,而作为parameter passing给一个函数。 你必须写下这样不太可读的东西:

 void func (int*const* ptrptr) 

我怀疑许多C程序员可以在那里获得const和*权利。 我知道不能 – 我必须检查GCC。 我认为这就是为什么你很less看到指针指针的语法,尽pipe它被认为是很好的编程习惯。

也可以使用常量指针来确保指针variables本身在只读内存中声明,例如,您可能想要声明某种基于指针的查找表并将其分配给NVM。

当然,正如其他答案所指出的那样,常量指针也可以用来强制“const正确性”。

声明常量数据的常量指针

 const int* const ptr=&X; 

这是上面描述的两种指针types的组合,它们的所有属性都是。

声明一个只读的成员函数(C ++)

由于这是C ++的标签,我还要提到你可以声明一个类的成员函数为const。 这意味着这个函数在被调用的时候不允许修改其他类的成员,这样既可以防止类的程序员发生意外错误,也可以通知成员函数的调用者他们不会弄乱任何东西通过调用它。 语法是:

 void MyClass::func (void) const; 

你是对的,对于来电者来说,它绝对没有区别。 但是对于这个function的作者来说,它可以成为一个安全networking“好的,我需要确保我不会把这点指向错误的东西”。 不是很有用,但也不是无用的。

它基本上和你的程序中的int const the_answer = 42

…今天我意识到,指针是通过价值传递给函数,这是有道理的。

(IMO)它作为默认没有任何意义。 更明智的默认是作为不可重新分配的指针( int* const arg )传递。 也就是说,我会更喜欢作为parameter passing的指针被隐式声明为const。

那么const的优点是什么?

好处是,当你修改参数所指向的地址时,它很容易,有时也不清楚,这样当不是很容易的时候就可以引入一个bug。 改变地址是非典型的。 如果你的意图是修改地址,创build一个局部variables就更加清楚了。 原始指针操作也是引入错误的一种简单方法。

所以当你想改变参数指向的地址时,通过不可变地址传递并创build副本(在这些非典型情况下)是更清楚的:

 void func(int* const arg) { int* a(arg); ... *a++ = value; } 

并补充说本地服务实际上是免费的,它减less了出错的几率,同时提高了可读性。

在更高层次上:如果您将参数操作为数组,那么客户端将参数声明为容器/集合通常会更清晰且更不容易出错。

一般来说,将const添加到值,参数和地址是一个好主意,因为您并不总是意识到编译器乐于执行的副作用。 因此,它与其他几种情况下使用的const一样有用(例如,问题类似于'为什么我应该声明值为const?')。 幸运的是,我们也有参考资料,不能被重新分配。

如果在有内存映射设备的地方进行embedded式系统或设备驱动程序编程,那么通常会使用两种forms的“const”,一种用于防止指针被重新分配(因为它指向固定的硬件地址),而如果外设注册它指向是一个只读的硬件寄存器,然后另一个const会在编译时检测到很多错误,而不是运行时。

一个只读的16位外设芯片寄存器可能如下所示:

static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;

然后,您可以轻松读取硬件寄存器,而无需使用汇编语言:

input_word = *peripheral;

int iVal = 10; int * const ipPtr =&iVal;

就像普通的constvariables一样,一个const指针必须在声明时被初始化为一个值,并且它的值不能被改变。

这意味着一个const指针总是指向相同的值。 在上述情况下,ipPtr将始终指向iVal的地址。 但是,由于被指向的值仍然是非const的,因此可以通过取消引用指针来更改指向的值:

* ipPtr = 6; //允许,因为pnPtr指向一个非const int

同样的问题可以问任何其他types(不只是指针):

 /* Why is n const? */ const char *expand(const int n) { if (n == 1) return "one"; if (n == 2) return "two"; if (n == 3) return "three"; return "many"; } 

你的问题实际上更多的是为什么把一个variables定义为一个const,而不是一个函数的const指针参数。 当你将任何variables定义为常量,如果它是一个函数或成员variables或局部variables的参数,这里适用相同的规则。

在你的特定情况下,从function上来说,当你声明一个局部variables为const时,它并没有像其他许多情况下的区别,但是它确实限制了你不能修改这个variables。

将一个const指针传递给一个函数是没有意义的,因为它将被值传递。 这只是通用语言devise所允许的那些东西之一。 禁止它只是因为它没有意义,只会使语言规范。 大。

如果你在一个函数里,这当然是另一种情况。 有一个指针不能改变它指向的是一个使代码更清晰的断言。

我想一个好处是,编译器可以在知道这个指针不能改变的函数内部执行更积极的优化。

它也避免了例如。 将这个指针传递给一个接受非const指针引用的子函数(因此可以像void f(int *&p) )那样改变指针,但是我同意在这种情况下有用性是有限的。

因此,可以certificateconst指针的适用性很高的一个例子。 考虑你有一个内部有一个dynamic数组的类,并且你想要传递用户对数组的访问权限,但不授予他们更改指针的权利。 考虑:

 #include <new> #include <string.h> class TestA { private: char *Array; public: TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } } ~TestA(){if(Array != NULL){ delete [] Array;} } char * const GetArray(){ return Array; } }; int main() { TestA Temp; printf("%s\n",Temp.GetArray()); Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access Temp.GetArray()[1] = ' '; printf("%s\n",Temp.GetArray()); } 

其中产生:

input数据
把数据

但是,如果我们尝试这个:

 int main() { TestA Temp; printf("%s\n",Temp.GetArray()); Temp.GetArray()[0] = ' '; Temp.GetArray()[1] = ' '; printf("%s\n",Temp.GetArray()); Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null } 

我们得到:

错误:作为左侧操作数赋值//所需的左值再次挫败!

很显然,我们可以修改数组的内容,而不是数组的指针。 如果您希望确保指针在传递给用户时具有一致的状态,那么这很好。 但是有一个问题:

 int main() { TestA Temp; printf("%s\n",Temp.GetArray()); Temp.GetArray()[0] = ' '; Temp.GetArray()[1] = ' '; printf("%s\n",Temp.GetArray()); delete [] Temp.GetArray(); //Bwuahaha this actually works! } 

即使我们不能修改指针本身,我们仍然可以删除指针的内存引用。

所以,如果你想要内存引用总是指向某些东西(IE永远不会被修改,类似于当前引用的工作方式),那么它是非常适用的。 如果你想让用户有完整的访问权限并修改它,那么non-const就是为你准备的。

编辑:

注意okorz001注释由于GetArray()是一个右值操作数而不能分配,他的注释是完全正确的,但是如果你要返回一个对指针的引用(我想我假设GetArray是参考参考),例如:

 class TestA { private: char *Array; public: TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } } ~TestA(){if(Array != NULL){ delete [] Array;} } char * const &GetArray(){ return Array; } //Note & reference operator char * &GetNonConstArray(){ return Array; } //Note non-const }; int main() { TestA Temp; Temp.GetArray() = NULL; //Returns error Temp.GetNonConstArray() = NULL; //Returns no error } 

会在第一个返回导致一个错误:

错误:分配只读位置'Temp.TestA :: GetArray()'

但是,尽pipe潜在的后果,第二个将愉快地发生。

很显然,这个问题将会被提出,“你为什么要返回一个指针的引用”? 有less数情况下需要将内存(或数据)直接分配给相关的原始指针(例如,构build您自己的malloc / free或new / free前端),但在这些情况下,它是非const引用。 对const指针的引用我还没有遇到过这样的情况(除非可能是声明的const引用variables而不是返回types?)。

考虑如果我们有一个函数需要一个const指针(而不是一个):

 class TestA { private: char *Array; public: TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } } ~TestA(){if(Array != NULL){ delete [] Array;} } char * const &GetArray(){ return Array; } void ModifyArrayConst(char * const Data) { Data[1]; //This is okay, this refers to Data[1] Data--; //Produces an error. Don't want to Decrement that. printf("Const: %c\n",Data[1]); } void ModifyArrayNonConst(char * Data) { Data--; //Argh noo what are you doing?! Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position printf("NonConst: %c\n",Data[1]); } }; int main() { TestA Temp; Temp.ModifyArrayNonConst("ABCD"); Temp.ModifyArrayConst("ABCD"); } 

const中的错误产生了这样的消息:

错误:只读参数“数据”

这是好事,因为我们可能不想这样做,除非我们想要引起评论中提到的问题。 如果我们在const函数中编辑减量,会发生以下情况:

NonConst:A
常量:B

显然,即使A是'Data [1]',它也被当作'Data [0]',因为NonConst指针允许减量操作。 随着const的实现,正如另一个人写的,我们在潜在错误发生之前就抓住了。

另一个主要考虑因素是const指针可以用作伪引用,因为参考指向的内容不能被改变(人们不禁要问,如果可能的话,它是如何实现的)。 考虑:

 int main() { int A = 10; int * const B = &A; *B = 20; //This is permitted printf("%d\n",A); B = NULL; //This produces an error } 

当试图编译时,产生以下错误:

错误:分配只读variables'B'

如果需要不断地引用A,那么这可能是一件坏事。 如果B = NULL被注释掉,编译器会高兴地让我们修改*B ,因此答:这似乎对整数似乎不是有用的,但考虑如果你有一个单一的graphics应用程序,你想要一个不可修改的指针,它可以传递

它的用法是可变的(原谅双关语),但正确使用,它是在框中的另一个工具,以协助编程。

没有什么特别的指针,你永远不会希望他们是const。 正如你可以拥有类成员的常量int值一样,你也可以有类似的理由来指定常量指针:你要确保没有人改变指向的东西。 C ++引用有点解决这个问题,但是指针行为是从Cinheritance的。

我相信这会阻止代码增加或减less函数体内的指针。

声明任何variablestypes –
(1)声明一个常量variables。
DataType const varibleName;

  int const x;
     X = 4;  //你可以只分配一次它的值 

(2)声明一个指向常量variables的指针
const dataType* PointerVaribleName=&X;

  const int * ptr =&X;
      //这里的指针variables指的是'X'的内容const是不能被修改的 

dataType* const PointerVaribleName=&X;

  int * const ptr =&X;
      //这里指针variables本身是不变的这样可以修改'X'的值但是指针不能被修改