为什么我能够更改const char *ptr的内容?

9 浏览
0 Comments

为什么我能够更改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指针吗?

感谢大家的时间和努力。

0
0 Comments

为什么我能够更改const char *ptr的内容?

"const"实际上是一个编译时的概念,所以除非指针指向一些无效的内存,否则不会出现段错误。当以可能改变其指向内容的方式使用const指针(在本例中传递给接受非const参数的strcpy函数)时,会生成一个警告。

const可以在运行时进行强制执行。const变量可以放置在.rodata或类似的部分,并由操作系统加载到标记为只读的内存页中,在这种情况下,尝试更改它们肯定会导致段错误。在某些嵌入式架构中,const数据甚至可能存储在EEPROM中,CPU无法对其进行物理写入。

解决这个问题的方法是使用const关键字来修饰指针,以确保不会意外地更改它所指向的内容。如果需要修改指针所指向的内容,可以声明一个非const指针来进行操作。但需要注意的是,如果尝试更改指针所指向的只读内存,仍然会导致段错误。

总结起来,虽然const关键字可以在编译时提供一定程度的保护,但在特定情况下,如指针指向只读内存或特定硬件限制下,仍然可能导致段错误。因此,在使用const指针时,需要谨慎处理,确保不会意外地更改其所指向的内容。

0
0 Comments

为什么我能够更改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指针指向的内容。这样可以提高代码的可读性和安全性。

0
0 Comments

你的理解是正确的,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的存在。

0