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

时间:2010-07-12 12:52:56

标签: c pointers

我将一个指针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():它只是填补空间
  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():它只是填补空间
  foo():蒂姆   main():它只是为了填补空间

显然,str指向的内存更改为包含"Tim"的内存位置,而foo()位于strcpy()。虽然这次我没有使用const 是不是const应该阻止这个?或者我的理解是错的?

对我而言,即使使用{{1}},我也可以更改内存引用和内存引用的内容。那有什么用呢?

你能给我一个例子,编译器会给我一个错误,我试图改变一个const指针吗?

感谢大家的时间和精力。

5 个答案:

答案 0 :(得分:17)

您的理解是正确的,const char*是一个合同,意味着您无法通过此特定指针更改内存。

问题是C类型转换的非常 lax。 strcpy获取指向非const 字符的指针,并且隐式const char*转换为char*(正如编译器有用地告诉我们)您)。您可以轻松传递整数而不是指针。因此,您的函数无法更改ptr指向的内容,但strcpy可以,因为它会看到非const指针。你没有崩溃,因为在你的情况下,指针指向一个足够大小的实际缓冲区,而不是一个只读字符串文字。

要避免这种情况,请查找编译器警告,或使用-Wall -Werror进行编译(如果您使用的是gcc)。

此行为特定于C. C ++,例如,不允许这样做,并且需要显式强制转换(C样式强制转换或const_cast)来剥离const限定符,就像你想要的那样合理地期待。

回答扩展问题

您正在将字符串文字分配到非const char中,,不幸的是,在C甚至C ++中是合法的!它隐式转换为char*,即使通过此指针写入现在将导致未定义的行为。这是一个不推荐使用的功能,到目前为止只有C ++ 0x才允许这种情况发生。

话虽如此,为了停止更改指针本身,你必须将它声明为char指向char(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; /* OK, changing the pointer */
    *a = 'a'; /* OK, changing contents pointed by pointer */

    b = buf; /* OK, changing the pointer */
    *b = 'b'; /* error, changing contents pointed by pointer */

    c = buf; /* error, changing pointer */
    *c = 'c'; /* OK, changing contents pointed by pointer */

    d = buf; /* error, changing pointer */
    *d = 'd'; /* error, changing contents pointed by pointer */
}

对于所有错误行,GCC给出了“错误:分配只读位置”。

答案 1 :(得分:3)

“const”实际上是一个编译时的东西,所以不要指望一个段错误,除非指针指向一些无效的内存。当使用const指针的方式可能会改变它们所指向的内容时(在这种情况下将其传递给接受非const的strcpy),将生成警告。

答案 2 :(得分:1)

const char*char const*相同,而不是char* const。所以这意味着指向一些你无法改变的东西。

编辑后的精心设计。第一种形式禁止数据被更改(除非你隐式或显式地转换)第二种形式禁止指针本身被改变。

您在编辑版本中执行的操作是更改指针。这在这里是合法的。如果你禁止两者,你必须写char const* const

答案 3 :(得分:1)

IIRC const表示参数的值可能不会改变。我的情况是,值是指针指向的地址。 strcpy不会更改指针指向的地址,而是指针指向的内存。

在此处使用const可确保不更改内存引用(地址)。但是,引用的内存可能会更改。这就是这里发生的事情。为指针分配新地址会导致错误。

SIDE NOTE
我认为你的foo方法不安全。你最好还是传递str的最大长度并执行长度检查,否则你将打开缓冲区溢出。

修改
我从this site获取了以下内容:

  

const char *Str告诉编译器   指针指向的DATA也是如此   是const。这意味着,Str可以   在Func中更改,但*Str不能。   作为指针的副本传递给   Func,对Str所做的任何更改都不是   看到主要......

答案 4 :(得分:1)

  

1-原型中的const char * str实际上意味着什么?   这是否意味着函数不能改变str的内容?

是的!这意味着我们无法更改str指向的某物chararray of chars)的内容。

  

如果是这样,那么上面的程序如何更改值?

那是因为strcpy()的原型是char * strcpy ( char * destination, const char * source );

在您的代码中,存在从const char*char*类型的隐式转换,因为strcpy()要求其第一个参数属于char*类型。

从技术上讲,您的代码不正确而非常危险。如果你在C ++中尝试相同的代码,你肯定会收到错误。 C ++不允许这样的隐式转换,但C允许。