【问题标题】:C Pointer ReferencingC 指针引用
【发布时间】:2012-12-05 23:12:46
【问题描述】:

我有以下代码,它只是打印出一个人名的介绍。

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char* firstname;
    char* lastname;
}Person;

void intro(void *person){
    printf("The person you are looking for is %s %s\n", ((Person *)person)->firstname, ((Person *)person)->lastname);
}

int main()
{
    Person *a = NULL;
    a = (Person *)malloc(sizeof(Person));

    char *first = NULL, *last = NULL;
    first = (char *)malloc(sizeof(char)*20);
    strncpy(first,"Bob", 20);
    last = (char *)malloc(sizeof(char)*20);
    strncpy(last,"Newmonson", 20)
    a->firstname = first;
    a->lastname = last;

    intro(a);

    return 0;
}

产生输出

The person you are looking for is Bob Newmonson

但是将intro(a) 更改为intro(&amp;a) 会产生

The person you are looking for is �@ Newmonson

当我在 GDB 中打开第一次尝试并在第 10 行中断时,我找到了person=0x601010 的地址。名字和姓氏都存储在我期望的位置,0x04006b90x4006bd,因为它们在堆栈中较早声明的位置。

当我使用对intro(&amp;a) 所做的更改运行 GDB 时,我得到了启发。 person的地址现在是0x7fffffffffdd38,名字指向0x601010,姓氏指向0x4006db

谁能帮忙解释一下发生了什么以及为什么我仍然可以在第二次测试中访问姓氏的正确地址。

编辑:

正如每个人似乎一直在问这个问题,void * 用于此代码的线程部分,我没有包括在内。

【问题讨论】:

  • 仅供参考:first = (char *)malloc(sizeof(char)*20); first = "Bob"; 没有做你认为它正在做的事情。
  • 另外,你为什么要声明intro() 使用void*,而唯一有希望使用它的是Person*
  • @MichaelBurr 我想和void* 一起练习,结果却遇到了这个问题。
  • 将 person 转换为 int 并打印出来。您将看到指针保存的值。它应该是您之前看到的十六进制值。
  • @TimCooper 一开始我并没有想到你在说什么,但后来我突然意识到我的 malloc 正在被堆栈上完全覆盖 BobNewmanson 而不是使用strncpy 将我想要的内容复制到分配的内存中。

标签: c memory


【解决方案1】:

这是因为a 已经是指向Person 结构的指针;因此intro(&amp;a) 传递一个指向该指针的指针,但intro() 将其参数视为指向Person 的指针。

另外,如果 intro() 打算作用于 Person,它应该声明 Person * 参数,而不是 void *

【讨论】:

  • void * 位是巧合,因为它最初与此问题无关。
  • @Blackninja543:实际上,void * 很重要——如果函数声明得更正确,编译器会在使用intro(&amp;a) 时警告您指针类型不匹配。应该避免使用void * - 它需要一个很大的危险信号,表明有问题。
【解决方案2】:

指针变量a 中保存的地址与a 占用的实际内存位置的地址不同。

0x601010 这样的地址是一个“低”内存地址,通常位于堆上的某个位置。像0x7fffffffffdd38 这样的地址是一个非常“高”的地址,通常会在堆栈上。所以&amp;a 给你变量a 在堆栈上的实际地址,它把那个值传递给函数,而不是值0x601010 存储在指针变量a 中,代表第一个从malloc返回的分配内存缓冲区的地址。

【讨论】:

    【解决方案3】:

    首先:

    /* allocate space for 20 characters and make 'first' point to that space. */
    first = (char *)malloc(sizeof(char)*20);
    /* now make 'first' point to the string Bob. Leak the memory that was allocated before. */
    first = "Bob";
    /* Rinse, lather, repeat. */
    last = (char *)malloc(sizeof(char)*20);
    last = "Newmonson";
    

    现在解释一下为什么使用intro(&amp;a) 会给你带来意想不到的结果:

    intro 需要一个指针,它假定指向Person 类型的结构。您创建一个指向Person 的指针,然后为其分配空间。

    调用 intro(a) 会导致指向 Person 的指针作为指向 void 的指针传递给 intro,然后将其视为指向 Person 的指针,一切都很好。

    调用intro(&amp;a) 但是会获取a地址 并将其传入。同样,intro 尝试将其视为指向Person 的指针,但这不起作用,因为你传递的是一个指向Person 的指针。您正在将西红柿传递给一个认为它正在获取橙子的函数。两者都是水果,你会得到果汁,虽然当你的早餐是美味的番茄汁而不是美味的橙汁时,你可能不会很高兴。

    如果你问为什么调用intro(&amp;a) 会导致名字被破坏而姓氏被打印出来,答案是纯粹的运气:带有姓氏的字符串恰好位于正确的位置记忆。

    【讨论】:

      【解决方案4】:

      试试这个:

      #include <stdio.h>
      #include <stdlib.h>
      
      typedef struct {
          char* firstname;
          char* lastname;
      }Person;
      
      void intro(Person *person){
          printf("The person you are looking for is %s %s\n", (person)->firstname,   (person)->lastname);
      }
      
      int main()
      {
          Person *a = NULL;
          char *first = NULL, *last = NULL;
          a = (Person *)malloc(sizeof(Person));
      
      
          first = (char *)malloc(sizeof(char)*20);
          first = "Bob";
          last = (char *)malloc(sizeof(char)*20);
          last = "Newmonson";
          a->firstname = first;
          a->lastname = last;
      
          intro(a);
      
          return 0;
      }
      

      希望对你有帮助……

      【讨论】:

      • 为什么要将Person * 转换为Person *
      • 我知道它是不必要的,但我相信它的好习惯,可以让编译器平静下来:P
      • 再来一次?我不知道那是什么意思。
      • 它不会伤害代码(我认为),我只是不想改变他太多。但你有权利这是不必要的
      • 这绝对不是不是一个好习惯,事实上这是一个非常非常糟糕的习惯。仅当编译器抱怨并且您知道您比编译器更了解时才在 C 中使用强制转换......即使那样,请仔细检查您的假设。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-19
      • 2019-05-20
      相关资源
      最近更新 更多