为什么我能够更改const char *ptr的内容?
为什么我能够更改const char *ptr的内容?
我把一个指针ptr
传递给了一个原型将其作为const
参数的函数。
foo( const char *str );
根据我的理解,这意味着它不能改变传递的ptr
的内容,就像foo( const int i )
的情况一样。如果foo()
试图改变i
的值,编译器会报错。
但是在这里,我发现它可以轻易地改变ptr
的内容。
请看以下代码:
foo( const char *str ) { strcpy( str, "ABC" ) ; printf( "%s(): %s\n" , __func__ , str ) ; } main() { char ptr[ ] = "Its just to fill the space" ; printf( "%s(): %s\n" , __func__ , ptr ) ; foo( const ptr ) ; printf( "%s(): %s\n" , __func__ , ptr ) ; return; }
在编译时,我只得到一个警告,没有错误:
warning: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type
而当我运行它时,我得到一个输出,而不是Segmentation Fault
main(): Its just to fill the space
foo(): ABC
main(): ABC
现在,我的问题是
1- 原型中的const char *str
实际上是什么意思?
这是否意味着函数不能更改str
的内容?如果是这样,那么上面的程序如何改变值?
2- 我如何确保我传递的指针的内容不会被更改?
在上述问题中,通过“指针的内容”我指的是“指针所指向的内存的内容”,而不是“指针中包含的地址”。
编辑
大多数回答都说这是因为strcpy
和C的隐式类型转换。但是现在我尝试了以下内容:
foo( const char *str ) { str = "Tim" ; // strcpy( str, "ABC" ) ; printf( "%s(): %s\n" , __func__ , str ) ; }
这次的输出是,没有警告:
main(): Its just to fill the space
foo(): Tim
main(): Its just to fill the space
所以显然,在foo()
中,str
指向的内存更改为包含"Tim"
的内存位置。虽然这次我没有使用strcpy()
。
难道const
不应该阻止这种情况吗?还是我的理解有误?
在我看来,即使有const
,我仍然可以更改内存引用和内存引用的内容。那这有什么用呢?
你能给我一个编译器会报错的示例,说明我试图更改一个const指针吗?
感谢大家的时间和努力。
为什么我能够更改const char *ptr的内容?
"const"实际上是一个编译时的概念,所以除非指针指向一些无效的内存,否则不会出现段错误。当以可能改变其指向内容的方式使用const指针(在本例中传递给接受非const参数的strcpy函数)时,会生成一个警告。
const
可以在运行时进行强制执行。const
变量可以放置在.rodata
或类似的部分,并由操作系统加载到标记为只读的内存页中,在这种情况下,尝试更改它们肯定会导致段错误。在某些嵌入式架构中,const
数据甚至可能存储在EEPROM中,CPU无法对其进行物理写入。
解决这个问题的方法是使用const
关键字来修饰指针,以确保不会意外地更改它所指向的内容。如果需要修改指针所指向的内容,可以声明一个非const指针来进行操作。但需要注意的是,如果尝试更改指针所指向的只读内存,仍然会导致段错误。
总结起来,虽然const关键字可以在编译时提供一定程度的保护,但在特定情况下,如指针指向只读内存或特定硬件限制下,仍然可能导致段错误。因此,在使用const指针时,需要谨慎处理,确保不会意外地更改其所指向的内容。
为什么我能够更改const char *ptr的内容?
首先,我们来理解一下const char *str在函数原型中的意思。它表示我们不能更改由str指向的内容,无论是一个char还是一个char数组。
那么,为什么上面的程序能够更改这个值呢?
这是因为strcpy()的原型是char * strcpy ( char * destination, const char * source )。在你的代码中,有一个从const char*到char*类型的隐式转换,因为strcpy()要求它的第一个参数是char*类型。
严格来说,你的代码是不正确的,甚至是危险的。如果你在C++中尝试相同的代码,你肯定会得到一个错误。C++不允许这种隐式转换,但是C允许。
解决这个问题的方法是使用const修饰符来确保我们不会更改const char *ptr指向的内容。这样可以提高代码的可读性和安全性。
例如,我们可以将代码修改为:
const char *ptr = "Hello World";
这样就明确了我们不能更改ptr指向的内容,如果我们尝试去更改,编译器会报错。
总之,虽然C允许我们在const char *ptr中更改内容,但这是不安全的行为。在编写代码时,我们应该尽量避免这种隐式转换,并使用const修饰符来确保我们不会更改const指针指向的内容。这样可以提高代码的可读性和安全性。
你的理解是正确的,const char *是一个约定,意味着你不能通过这个特定的指针改变内存。
问题在于C语言在类型转换上非常宽松。strcpy函数接受一个指向非常量字符的指针,并且它会将const char*隐式转换为char*(正如编译器友好地告诉你的那样)。你甚至可以传递一个整数而不是指针。结果是,你的函数不能改变ptr指向的内容,但是strcpy可以改变,因为它看到的是一个非const指针。你的代码没有崩溃,是因为在你的情况下,指针指向一个足够大的缓冲区,而不是只读字符串字面量。
为了避免这种情况,可以查找编译器警告,或者使用例如-Wall -Werror进行编译(如果你使用的是gcc)。
这种行为是特定于C的。例如,C++不允许这样做,它要求使用显式转换(C风格的转换或const_cast)来去除const限定符,正如你合理地期望的那样。
对于扩展问题的答案
你正在将一个字符串字面量赋值给一个非const char,这在C甚至C++中都是合法的!它会被隐式转换为char*,即使通过这个指针写入会导致未定义行为。这是一个已弃用的特性,只有C++0x到目前为止不允许发生这种情况。
为了停止改变指针本身,你必须将其声明为指向char的const指针(char *const)。或者,如果你希望既不改变指针指向的内容,也不改变指针本身,可以使用const指针指向const char(const char * const)。
示例:
void foo ( char *a, const char *b, char *const c, const char *const d) { char buf[10]; a = buf; /* 可以,改变指针 */ *a = 'a'; /* 可以,改变指针指向的内容 */ b = buf; /* 可以,改变指针 */ *b = 'b'; /* 错误,改变指针指向的内容 */ c = buf; /* 错误,改变指针 */ *c = 'c'; /* 可以,改变指针指向的内容 */ d = buf; /* 错误,改变指针 */ *d = 'd'; /* 错误,改变指针指向的内容 */ }
对于所有错误行,GCC给出了"error: assignment of read-only location"的错误。
请看一下我问题的编辑,并进一步指导我。谢谢。
-dufresne, 我添加了一些示例。
请注意,在C中,字符串字面量的类型是char数组,而不是const char(就像在C++中一样)。
我刚刚了解到,在C++0x之前,C++也是如此。在C和C++中,将字符串字面量赋值给非const char不会产生任何错误或警告,即使使用了-Wall -Wextra -ansi -pedantic!
我相信你的话 - 我当然不会声称自己是C++专家!至少在C中是这样的原因是,字符串字面量(和使用它们的C程序)的存在早于const的存在。