提问



我总是搞砸如何正确使用const int*const int * constint const *。是否有一套规则定义了您能做什么和不能做什么?


我想知道在任务,传递到职能等方面做的所有事情和所有事情。

最佳参考


向后读(由顺时针/螺旋规则驱动):[131]



  • int* - 指向int
  • 的指针
  • int const * - 指向const int的指针

  • int * const - const指向int
  • 的指针
  • int const * const - const指向const int的指针



现在第一个const可以在类型的任何一侧,所以:



  • const int * == int const *

  • const int * const == int const * const



如果你想真的疯了,你可以做这样的事情:



  • int ** - 指向int
  • 的指针
  • int ** const - 指向int
  • 指针的const指针
  • int * const * - 指向int
  • 的const指针的指针
  • int const ** - 指向const int
  • 的指针
  • int * const * const - 指向int
  • 的const指针的const指针
  • ...



并确保我们清楚const的含义


const int* foo;
int *const bar; //note, you actually need to set the pointer 
                //here because you can't change it later ;)


foo是指向常量整数的变量指针。这使您可以更改指向的内容,但不能更改指向的值。大多数情况下会看到C风格的字符串,其中有一个指向const char的指针。您可以更改指向的字符串,但不能更改这些字符串的内容。当字符串本身位于程序的数据段中且不应更改时,这很重要。


bar是指向可以更改的值的常量或固定指针。这就像没有额外语法糖的参考。由于这个事实,通常你会使用一个你会使用T* const指针的引用,除非你需要允许NULL指针。

其它参考1


对于那些不了解顺时针/螺旋规则的人:
从变量名称开始,顺时针移动(在这种情况下,向后移动)到下一个指针类型。重复直到表达结束。


这是一个演示:
















其它参考2


我觉得这里已经回答了所有问题,但我只想补充一点,你应该提防typedef s!他们不只是文本替换。


例如:


typedef char *ASTRING;
const ASTRING astring;


astring的类型是char * const,而不是const char *。这是我总是倾向于将const放在类型右侧的一个原因,而且从不在开始时。

其它参考3


几乎所有人都指出:


const X* pX* const pconst X* const p之间有什么区别?[132]



  你必须阅读指针声明
  右到左。

  
  

      
  • const X* p表示p指向一个X为const:X对象不能通过p改变。

  •   
  • X* const p表示p是指向非const的X的const指针:你不能改变指针p本身,但你可以通过p改变X对象。

  •   
  • const X* const p表示p是一个指向X的常量指针:你不能改变指针p本身,也不能通过p改变X对象。

  •   


其它参考4



  1. 常量参考:


    对变量(此处为int)的引用,它是常量。我们主要将变量作为引用传递,因为引用的大小小于实际值,但是存在副作用,这是因为它就像实际变量的别名。我们可能会通过完全访问别名来意外更改主变量,因此我们将其设置为常量以防止此副作用。


    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
    

  2. 常量指针


    一旦常量指针指向变量,它就不能指向任何其他变量。


    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
    

  3. 指向常量的指针


    一个指针,通过它可以不改变它指向的变量的值,称为指向常量的指针。


    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    

  4. 指向常量的常量指针


    指向常量的常量指针是一个指针,既不能改变它所指向的地址,也不能改变保存在该地址的值。


    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    


其它参考5


这个问题显示正是为什么我喜欢按照我在问题中提到的方式做事,因为类型ID可以接受?


简而言之,我发现记住规则的最简单方法是const在之后适用于它。所以在你的问题中,int const *意味着int是常量,而int * const意味着指针是常量。


如果有人决定把它放在最前面(例如:const int *),作为一种特殊的例外情况,它适用于它后面的东西。


许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好。我不喜欢它,因为它是一个例外,因此混淆了事情。

其它参考6


一般规则是const关键字立即适用于它之前的内容。例外,起点const适用于以下内容。



  • const int*int const*相同,表示指向常数int的指针

  • const int* constint const* const相同,表示指向常数int的常量指针




对于Dos和Don,如果这个答案还不够,你能更准确地了解你的想法吗?

其它参考7


简单使用'const'



最简单的用法是声明一个命名常量。为此,我们声明一个常量,好像它是一个变量,但在它之前加上'const'。必须立即在构造函数中初始化它,因为当然,之后无法设置值,因为这会改变它。例如,


const int Constant1=96; 


将创建一个整数常量,无法想象地称为Constant1,值为96。


这些常量对于程序中使用的参数很有用,但在编译程序后不需要更改。对于程序员而言,它优于C预处理器'#define'命令,因为它被理解为&由编译器本身使用,而不是在到达主编译器之前由预处理器替换为程序文本,因此错误消息更有帮助。


它也适用于指针但是必须要小心'const'来确定指针或它指向的是否是常量或两者。例如,


const int * Constant2 


声明Constant2是指向常量整数的变量指针


int const * Constant2


是一种替代语法,它做同样的事情,而


int * const Constant3


声明Constant3是一个指向变量integer的常量指针


int const * const Constant4


声明Constant4是指向常量整数的常量指针。基本上'const'适用于其左边的任何东西(除非没有任何东西,在这种情况下它适用于它的直接权利)。


参考:http://duramecho.com/ComputerInformation/WhyHowCppConst.html [135]

其它参考8


在我遇到C ++大师Scott Meyers的这本书之前,我有同样的疑问。参考本书中的第三项,他详细讨论了使用const[136]


请遵循这个建议



  1. 如果const这个词出现在星号的左边,那指的是不变的

  2. 如果单词const出现在星号的右侧,则指针本身是常量

  3. 如果双方都出现const,则两者都是常数


其它参考9


在C ++中还有很多其他关于const正确性的细微之处。我想这里的问题只是关于C,但我会给出一些相关的例子,因为标签是C ++:



  • 您经常传递像TYPE const &这样的字符串之类的大型参数,以防止对象被修改或复制。示例:


    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }


    但是TYPE & const没有意义,因为引用总是const。

  • 您应该始终将不修改类的类方法标记为const,否则您无法从TYPE const &引用中调用该方法。示例:


    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • 常见的情况是返回值和方法都应该是const。示例:


    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }


    实际上,const方法不能将内部类数据作为引用返回非const。

  • 因此,必须经常使用const重载创建const和非const方法。例如,如果你定义T const& operator[] (unsigned i) const;,那么你可能也会想要由下式给出的非const版本:


    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }



Afaik,C中没有const函数,非成员函数本身不能在C ++中使用const,const方法可能有副作用,而编译器不能使用const函数来避免重复函数调用。实际上,即使是简单的int const &引用也可能见证它在其他地方引用的值。

其它参考10


这很简单但很棘手。请注意,我们可以将const限定符与任何数据类型(intcharfloat等交换。


我们来看下面的例子。





const int *p ==> *p是只读的[[p是指向常量整数的指针]]


int const *p ==> *p是只读的[[p是指向常数整数的指针]]





int *p const ==> 错误声明。编译器抛出语法错误。


int *const p ==> p是只读的[[p是一个指向整数的常量指针]]。
由于指针p在这里是只读的,因此声明和定义应该在同一个地方。





const int *p const ==> 错误声明。编译器抛出语法错误。


const int const *p ==> *p是只读的


const int *const p1 ==> *pp是只读的[[p是一个指向常数整数的常量指针]]。由于这里的指针p是只读的,因此声明和定义应该在同一个地方。





int const *p const ==> 错误声明。编译器抛出语法错误。


int const int *p ==> 错误声明。编译器抛出语法错误。


int const const *p ==> *p是只读的,相当于int const *p


int const *const p ==> *pp是只读的[[p是指向常数整数的常量指针]]。由于指针p在这里是只读的,因此声明和定义应该在同一个地方。

其它参考11


原始设计者一再将C和C ++声明语法描述为失败的实验。


相反,让s 命名类型“指向Type&rdquo ;;我将称之为Ptr_:


template< class Type >
using Ptr_ = Type*;


现在Ptr_<char>是指向char的指针。


Ptr_<const char>是指向const char的指针。


const Ptr_<const char>是指向const charconst指针。


那里。


[137]

其它参考12


这主要涉及第二行:最佳实践,作业,功能参数等。


一般做法。尝试尽你所能const。或者换句话说,让所有const开头,然后准确删除允许程序运行所需的最小const组。这将有助于实现const-correctness,并有助于确保当人们尝试分配他们不应该修改的东西时,不会引入细微的错误。


避免像瘟疫那样的const_cast<>。它有一两个合法的用例,但它们很少,而且很少。如果你试图改变一个const对象,你会更好地找到第一步宣布它const的人,并与他们讨论这个问题以达成共识应该发生。


这非常巧妙地导致了作业。只有在非const的情况下才可以分配。如果要分配到const的内容,请参阅上文。请记住,在声明中int const *foo;int * const bar;不同的东西是const - 这里的其他答案令人钦佩地涵盖了这个问题,所以我不会进入它。


功能参数:


通过价值:例如void func(int param)你不会在调用站点关注这种方式。可以认为有一些用例将函数声明为void func(int const param),但这对调用者没有影响,只是在函数本身,因为在调用期间函数无法更改传递的任何值。


通过引用传递:例如void func(int &param)现在确实有所作为。刚刚声明func被允许改变param,任何调用站点都应该准备好应对后果。将声明更改为void func(int const &param)会更改合同,并保证func现在不能更改param,这意味着传入的内容将会返回。正如其他人所指出的,这对于廉价传递一个你不想改变的大对象非常有用。传递引用要比按值传递大对象便宜得多。


通过指针:例如void func(int *param)void func(int const *param)这两个几乎与他们的参考对应物同义,但需要注意的是被叫函数现在需要检查nullptr,除非某些其他合同保证确保func它永远不会在param中收到nullptr


关于该主题的意见。在这样的情况下证明正确性是非常困难的,它太容易犯错误。所以不要冒险,并且总是检查nullptr的指针参数。从长远来看,你将拯救自己的痛苦和苦难。至于检查的成本,它是便宜的,并且在编译器内置的静态分析可以管理它的情况下,优化器无论如何都会忽略它。打开MSVC或WOPR的链接时间代码生成(I对于GCC来说,你会得到它的程序范围,即使是在跨越源代码模块边界的函数调用中。


在一天结束时,所有上述内容都非常可靠,总是更喜欢对指针的引用。他们全面安全。