【问题标题】:In C, why can't the value of a pointer-to-char variable be changed after it has been assigned?在 C 中,为什么指针指向 char 变量的值在分配后不能更改?
【发布时间】:2015-10-16 04:50:53
【问题描述】:

我不明白这种情况的区别:

#include <stdio.h>

int main()
{
  int i = 0;
  i = 1;

  return 0;
}

还有这个案例:

#include <stdio.h>

int main()
{
  char *mychar = "H";
  *mychar = "E";

  return 0;
}

这会产生编译器警告“赋值从没有强制转换的指针中生成整数”。

不应该 *mychar = "E" 取消引用 mychar 来为其分配“E”的值吗?

非常感谢。

【问题讨论】:

  • 注意:通过将"E" 更改为'E' 来修复该警告后,当您的进程因写入只读内存而崩溃时,请不要感到惊讶。您正在调用 UB。

标签: c pointers variable-assignment


【解决方案1】:

你混淆了一些东西。

  • 注意“E”实际上是const char[],其中存储了'E''\0'。它不是一个单一的字符。对于单个字符,您使用 '',例如 'E'
  • mychar 指向字符串文字,您不能更改字符串文字。

如果你的想法是这样的:

 char *mychar = "H";
 mychar = "E"; 

这没关系,您没有更改字符串文字,只是第一次指针mychar 指向字符串文字“H”,然后指向“E”。

这是你不能做的:

  char *mychar = "Hello";
  *mychar = 'E'; // Can't modify the string literal

但是你可以这样做:

  char c = 0;
  char *mychar = &c;
  *mychar = 'E'; // This is ok

【讨论】:

    【解决方案2】:

    “E”是字符串文字(char*),“E”是字符文字(char)。

    请注意,您要比较的两段代码并不相似!两段代码(int vs char*)的区别是你写的更清楚

    char* mychar = "H";
    *mychar = "E";
    

    int例子对应的类型是(char*)。也就是说,类似于“int”示例的代码是

    char* mychar = "H";
    mychar = "E";
    

    【讨论】:

      【解决方案3】:

      字符串文字可能存储在内存的只读部分。修改字符串文字会调用未定义的行为。你不能修改它。

      添加const 限定符让您的编译器知道该字符串是不可修改的

      char const *mychar = "H";  
      

      你还应该注意声明

      *mychar = "E";  
      

      本身就是错误的。您正在将 char * 类型分配给 char

      【讨论】:

      • 问题更多的是他将const char *分配给(const) char; UB是一个“次要”问题。注意:这不等效,因为他可以稍后将指针更改为指向非const 字符串。
      • @Olaf;是的。添加到答案中。
      • 旁注:char const * 已弃用(至少从 C11 开始)。如果稍后将指针更改为非 const char(通常用于指针的默认字符串),您应该使用相同的 const char *. const 可能会导致问题。
      • @Olaf;感谢您告诉我,但我认为 char const * 易于阅读:指向常量字符的指针
      • “常量字符”并没有那么清楚,我想。许多初学者将char const *cpchar * const cp 混淆了。但更重要的是this。 (不过,这只是一个提示;我没有理由不投票。)
      【解决方案4】:

      我认为你的意思是以下

      #include <stdio.h>
      
      int main()
      {
        char *mychar = "H";
        mychar = "E";
      
        return 0;
      }
      

      赋值后指针指向字符串文字"E"

      如您所见,可以重新分配指针。只有您应该使用正确的语法。表达式*mychar 表示取消引用指针。它的值不是存储在指针中的值,而是指针所指向的对象的值。

      至于你的原始代码然后在这个语句中

        *mychar = "E";
      

      赋值*mychar 的左操作数具有char 类型,而右操作数"E" 具有指针char * 的类型(对应于字符串文字的数组类型隐式转换为指向其第一个element) 并且编译器会警告您您正在尝试做错事。

      考虑到字符串文字可能不会更改。所以例如这个语句

        *mychar = 'E';
      

      具有未定义的行为(这里使用整数字符常量'E'

      【讨论】:

        【解决方案5】:

        char *mychar = "H" 告诉编译器mychar 指向一个字符数组。 *mychar 取消引用数组中的第一个字符,字面意思是 'H'(注意单引号)。

        当你写作时:

        *mychar = "E" 你试图将一个字符串(字面意思是一个指向字符数组的指针)放在一个字面字符应该去的地方,所以技术上正确的代码是:

        *mychar = 'E'; // assign a character-literal, not a string

        请注意,虽然字符串通常是在只读内存中创建的,所以如果您确实将'E' 写入'H' 当前所在的只读内存位置,您的程序可能会崩溃。

        如果您想避免编译器警告(但仍然编写完全不正确的代码并崩溃),您可以这样写:

        (char *)(*mychar) = "E"; // make the compiler think *mychar is pointer-to-char, very bad, will crash, but won't warn any more

        【讨论】:

          【解决方案6】:

          这里有两个问题 - 了解指针的类型,以及了解可以修改的内存部分。

          最新版本的 gcc 编译器对以下行发出警告:

          char *mychar = "H";
          

          虽然警告可能难以理解:

           warning: deprecated conversion from string constant to ‘char*’
          

          尽管有警告,那行代码仍然可以编译和运行。基本上,他们不鼓励你这样做—— mychar 现在是一个指针,指向内存中编译器放置两个字符“H”和“\0”的某个位置。如果您执行 mychar[3] = 'X' 之类的操作,那么您正在编写一个您一无所知且无法控制的内存区域;根据实现,这可能会导致运行时错误。如果添加 const:

          const char *mychar = "H";
          

          char const *mychar = "H";
          

          警告消失了,但现在您已经清楚自己不能使用此指针来更改内存。如果你以后这样做

          *mychar = 'E';
          

          这将导致编译时错误(不是警告),因为您不能使用 const char* 来更改内存。

          现在,关于你实际得到的错误信息,是因为你写了

          *mychar = "E";
          

          而不是

          *mychar = 'E';
          

          当编译器看到“E”时,它会在可执行文件中用字符“E”和“\0”留出一些内存,“E”的值是指向内存中该点的指针(char*) . *mychar 是 mychar 指向的地址处的字符。所以

          *mychar = "E";
          

          将 char* 指针指向“E”字符串,并将其放在内存中 mychar 指向的位置。您的错误消息真正令人困惑的是它指的是整数。我猜这是因为 char 可以被认为是一种整数(unsigned char 可以取 0 到 255 之间的任何值)。

          您正在尝试将字符的值设置为指针的值。将指针转换为整数是合法的(虽然很少是一个好主意),所以你可以尝试

          *mychar = (char)"E";
          

          但是使用我的 gcc (4.9.3) 版本会产生编译时错误,因为强制转换会降低精度——您将采用 32 位或 64 位值并将其转换为 8 位值。同样,使用我的 gcc 版本,

          *mychar = (int)"E";
          

          编译(如果我没有将 mychar 设为 const char*),但会产生运行时错误,因为它试图更改只读内存。

          也许你真正的意思是

          mychar = "E";
          

          这会将 mychar 从指向内存中包含“H”的位置更改为内存中包含“E”的位置。这可以正确编译和运行。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-11-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-11-26
            • 1970-01-01
            • 2022-01-15
            相关资源
            最近更新 更多