const指针有什么用处(相对于指向常量对象的指针)?

32

我经常使用指向常量对象的指针,就像这样...

const int *p;

这意味着您无法通过 p 更改整数指针所指向的内容。但我也看到过像这样声明的常量指针...

int* const p;

我的理解是,这意味着指针变量本身是常量——你可以整天更改它所指向的整数,但你不能让它指向其他东西。

那会有什么可能的用处呢?

11个回答

31

当你在设计嵌入式系统的 C 程序或需要引用相同内存的特殊程序(共享内存的多处理器应用程序)时,你需要常量指针。

例如,我有一个 32 位 MIPs 处理器,它连接了一个 小型 LCD。我必须将我的 LCD 数据写入到内存中的特定端口,然后再发送到 LCD 控制器。

我可以使用 #define 宏定义该数字,但这样我也必须将其强制转换为指针,而当我这样做时,C 编译器可选项就不那么多了。

此外,我可能需要将其设置为 volatile,这也可以进行强制转换,但使用提供的语法——一个指向易失性内存位置的常量指针更容易和清晰。

对于 PC 程序,一个例子就是:如果你设计 DOS VGA 游戏(有一些在线教程很有趣,可以通过学习基本的低级图形知识),则需要写入 VGA 内存,该内存可能被引用为常量指针的偏移量。


3
小小的建议:对于内存映射设备,你肯定需要将变量标记为volatile,否则你无法确定编译器何时会实际发出读取或写入操作。 - Michael Burr
2
你可能需要使用volatile。根据设备,你可以使用barrier()和其他语义。确实需要小心处理值,例如缓存/非缓存、屏障等。这取决于设备类型,而volatile并不总是最佳选项。 - artless noise
@MichaelBurr:我同意你的观点,volatile肯定应该在里面;另一方面,许多编译器供应商的头文件似乎并不关心这一点——这可能有点令人恼火(因为即使编译器通常做正确的事情,它们也可能忽略尝试读取寄存器但不对结果进行任何操作的尝试)。 - supercat

29

它可以让你保护指针不被改变。这意味着你可以保护基于指针永远不会被更改的假设或者避免无意中修改指针,例如:

int* const p = &i;

...

p++;     /* Compiler error, oops you meant */
(*p)++;  /* Increment the number */

1
我以前从未见过这个,我同意这是一个非常有用的技巧。 - Rob
1
这不是诡计 :). 我尽可能在函数参数中使用const,以便清楚地表明函数不会修改传递进来的字符串或结构。 - Andrew Johnson
令人困惑的是,const 在声明中经常出现两次,并且它可能实际上处于几个不同的位置。通常它可能会出现为类似 const uint8 *const value 的东西。这声明了指针和它所指向的值是不可修改的(但如果强制转换,则可以修改)。 - James Wald

8
另一个例子: 如果您知道指针被初始化的位置,您可以避免将来的空值检查。 编译器保证指针永远不会改变(为NULL)...

1
在C中。在C++中,对于您的特定情况(即非NULL指针),您使用引用而不是指针。 - paercebal

6
在任何非const的C++成员函数中,指针 this 的类型为 C * const,其中 C 为类类型--您可以更改其指向(即其成员),但不能将其更改为指向 C 的不同实例。对于 const 成员函数,this 的类型为 const C * const。还有一些(很少遇到)volatileconst volatile 成员函数,对于这些函数,this 也具有 volatile 限定符。

5
一种用途是在低级别(设备驱动程序或嵌入式)代码中,您需要引用映射到输入/输出设备(如硬件引脚)的特定地址。一些语言允许您在特定地址链接变量(例如,Ada具有use at)。在C语言中,最惯用的方法是声明一个常量指针。请注意,这样的用法也应该具有volatile限定符。
其他时候,这只是为了防御性编程。如果您有一个指针不应该改变,那么将其声明为不能更改是明智的。这将使编译器(和lint工具)检测到修改它的错误尝试。

4

当我想要避免指针的意外修改(如指针算术或在函数内部)时,我总是使用它们。您还可以将它们用于单例模式。

'this' 是一个硬编码常量指针。


3
与“const int”相同……如果编译器知道它不会改变,它可以基于此进行优化假设。
struct MyClass
{
    char* const ptr;
    MyClass(char* str) :ptr(str) {}

    void SomeFunc(MyOtherClass moc)
    {
         for(int i=0; i < 100; ++i)
         { 
                 printf("%c", ptr[i]);
                 moc.SomeOtherFunc(this);
         }
    }
}

现在,如果编译器知道SomeOtherFunc()不会改变ptr的值,它可以对循环进行优化。加上const关键字后,编译器就知道了这一点,并能够做出相应的假设。没有const关键字,编译器就必须假设SomeOtherFunc会改变ptr。


这段代码编译时,SomeOtherFunc不是必须声明为const指针参数吗?这样编译器才能知道SomeOtherFunc不会改变指针的值。因此,将本地指针声明为const似乎没有什么帮助。 - Andrew Johnson
安德鲁:我认为你把ptr和this混淆了。 - Leon Timmermans
@Andrew:不是的。SomeOtherFunc完全可以更改MyClass对象的任何其他部分。 - James Curran
“const” 对优化有什么影响?任何聪明的编译器都可以确定您是否修改了变量,并根据此进行优化,无论您是否“声明”过。 - underscore_d
1
@underscore_d 1)ptr是一个公共成员。编译器很难跟踪公共变量在模块间的使用情况。许多编译器甚至不会尝试,只会将其视为volatile。2)9年前的编译器比较愚笨。 - James Curran

2

我见过一些OLE代码,其中有一个从代码外部传入的对象,要处理它,你需要访问它传入的特定内存。因此,我们使用了const指针来确保函数始终操作通过OLE接口传入的值。


1

对于这个问题,已经给出了几个好的理由(内存映射设备和简单的防御性编码),但我愿意打赌,在大多数情况下,你看到这种情况实际上是一个错误,而意图是将项目作为指向常量的指针。

我当然没有数据来支持这个直觉,但我仍然会下注。


我经常用它进行优化。 - DavidG
@DavidG,const 的优化作用是如何的?任何聪明的编译器都可以判断你是否修改了一个变量,并且根据此进行优化,无论你是否声明。 - underscore_d
当我写下这条评论时,它在某些设备上的确是这样的...但自那以后世界发生了很大变化,现在编译器已经有了很大的改进。 - DavidG

0

type*const type*看作类型本身。这样,你就能明白为什么你可能想要有这些类型的常量。


是的,那种类型基本上就是一个整数。 - DavidG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接