【问题标题】:pointers, conversion of char ** to char *指针,char ** 到 char * 的转换
【发布时间】:2015-04-08 20:31:02
【问题描述】:

我正在研究指针,但我被下面的示例程序难住了。它应该是将char** 转换为char*,但我不明白程序背后的逻辑。程序在做什么?

#include <iostream>
using namespace std;

int main() {

    char *notes[] = {"cpp","python","java","mariadb"};
    void * base = notes; // notes and base, holds the address of note's first element
    void * elemAddr = (char*) base + 3* sizeof(char *); // i didn't understand this line???
    cout << *(char **)elemAddr;   // and this line

    return 0;
}

【问题讨论】:

  • 我真的不明白这个例子的意义。试图通过混淆来教授一个概念似乎是一个糟糕的策略。
  • @RawN char *notes[] 衰减为 char**,转换为 void *base,转换为 (char*)
  • 这段代码纯属邪恶,在暗处等着咬你。提示:(char*)base + 3 * sizeof(char*) 行也可以写成(char**) base + 3(后者更有意义)。然而,前者将base 视为指向字符的指针(它只是保存一个地址),因此它需要添加3 * sizeof(char*) 而不是仅添加3 来获取最后一个C 样式字符串的地址数组
  • 绘制内存图可能会有所帮助。跟踪事物指向的位置,并记住指针添加的字节数取决于指针的类型。
  • @metis,一旦你有了void*,初始类型就会丢失,除非你在别处有它。这是类型擦除的一种形式。此时的转换是从void*char*

标签: c++ pointers casting


【解决方案1】:

这些行:

char *notes[] = {"cpp","python","java","mariadb"};
void * base = notes; 
void * elemAddr = (char*) base + 3* sizeof(char *);
cout << *(char **)elemAddr;

是混淆的等价物:

char *notes[] = {"cpp","python","java","mariadb"};
cout << notes[3];

说明:

void * base = notes; 
void * elemAddr = (char*) base + 3* sizeof(char *);

等同于:

char * base = (char*)notes; 
char * elemAddr = base + 3 * sizeof(char *);

由于指针通常大小相同,因此这些行类似于:

char ** base = notes; 
char ** elemAddr = base + 3;

这使得elemAddr == &amp;notes[3]。这导致了这条线

cout << *(char **)elemAddr;

相同
cout << notes[3];

【讨论】:

    【解决方案2】:

    好吧,我咬一口:

    char *notes[] = {"cpp","python","java","mariadb"};
    

    声明一个指向char * 的指针数组。 (实际上应该是const char *notes[],因为我们永远无法修改内容)

    void * base = notes; // notes and base, holds the address of note's first element
    

    因此将数组notes的地址分配给base,并在此过程中丢失任何类型信息。

    void * elemAddr = (char*) base + 3* sizeof(char *); // i didn't understand this line???
    

    base 转换为char *,这意味着每个元素现在都是sizeof(char) == 1。将3 * sizeof(char *) 添加到该指针-> 3 个元素到notes 数组中,并将其分配回elemAddr

    cout << *(char **)elemAddr;   // and this line
    

    由于elemAddr 指向notes 中的一个元素,即char*,它实际上是一个指向char 的指针,我们想打印它指向的内容,因此@ 987654339@最开始。

    它的可读性不是很好,写起来会简单得多

    const char* notes[] =  { ... };
    cout << notes[3];
    

    但是你不会在这里发帖......

    【讨论】:

      【解决方案3】:

      显然,示例代码旨在说明使用数组索引时的幕后情况。

      重复代码(就像我写这篇文章时一样):

      #include <iostream>
      using namespace std;
      
      int main() {
      
          char *notes[] = {"cpp","python","java","mariadb"};
          void * base = notes; // notes and base, holds the address of note's first element
          void * elemAddr = (char*) base + 3* sizeof(char *); // i didn't understand this line???
          cout << *(char **)elemAddr;   // and this line
      
          return 0;
      }
      

      一、声明

      char *notes[] = {"cpp","python","java","mariadb"};
      

      声明一个指向char的指针数组。每个指针都用一个字符串字面量初始化。此语言功能在原始 C++ 标准 C++98 中已弃用,最终在 C++11 中被删除,因此对于现代 C++(在撰写本文时为 C++14)它只是无效 代码,不能用符合标准的编译器编译的代码。

      在标准 C++ 中可能是

      char const *notes[] = {"cpp","python","java","mariadb"};
      

      但是让我们忽略const 的问题,假设 C++03 或 C++98。

      然后是声明

      void * base = notes;
      

      声明一个名为basevoid*指针,初始化为数组notes的第一项地址。这通过数组表达式decay起作用,其中引用数组的表达式会生成指向其第一项的指针。

      声明

      void * elemAddr = (char*) base + 3* sizeof(char *); 
      

      显然是为了说明 [3] 索引在幕后发生的事情

      auto p = & notes[3];
      

      这通过面向字节的地址算术工作(char 及其变体是最小可寻址单元的 C++ 概念,即字节)。从数组的基地址开始,每一项的大小增加 3 倍。这将使您进入第三个项目的开头。

      最后是表达式

      *(char **)elemAddr
      

      使用该项目。由于使用了低级类型,这很丑陋。但本质上,该项目是char*,因此项目的地址被转换为char**,然后该指针被取消引用,产生char*指针本身,这是表达式的结果(并传递给cout)。

      【讨论】:

        【解决方案4】:

        第三行说: “将base转换为char指针,并将char指针大小的3倍添加到结果中。然后,将结果隐式转换为void指针并将其存储在elemAddr中”

        最后,elemAddr 指向了“mariadb”的地址。强制转换为 (char*) 不是执行指针运算所必需的,但它可以防止编译器打印警告。相当于

        &notes[3]
        

        第四行告诉编译器将指针解释为指向 char 指针的指针。然后取消引用该指针,以便将 elemAddr 指向的数据视为 char 指针,std::cout 会将其解释为 C 字符串。

        我不知道你是从哪里得到这个例子的,但是这种类型的编程对于非常可怕的错误和难以调试的代码来说是一个蜜罐。

        【讨论】:

          【解决方案5】:

          让我们逐行进行: 第一行:我们创建一个指向字符串数组的指针......这是一个指向数组基地址的指针......指针。

          第二行:我们把char*变成了一个void*,这根本不会改变指针的值,只是编译器关联的类型。

          第 3 行:当我们对基指针进行操作时,我们将其转换为 char*,这基本上撤销了我们在第 2 行所做的操作。我们将 3 * sizeof(char*) 添加到我们的指针中。这使得指向指针数组基数的指针现在指向数组中的第 4 个指针。

          第四行:现在我们双重尊重我们的指针,我们的指针指向什么?指针数组中的第三个元素。那个指针指向什么?在第一行创建的字符串。

          【讨论】:

          • 第一行创建了一个指针数组(被解释为字符串)。那里没有二级指针。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-03-31
          • 1970-01-01
          • 2017-02-12
          • 2021-01-26
          • 1970-01-01
          • 2018-04-05
          • 2015-09-27
          相关资源
          最近更新 更多