【问题标题】:Why do I need to define a reference to a pointer in C with *?为什么我需要用 * 定义对 C 中指针的引用?
【发布时间】:2018-10-08 21:29:33
【问题描述】:

为什么这是有效的:

int a = 5;
int *aPtr = &a;
printf("%i", *aPtr);

但这不是:

int a = 5;
int aPtr = &a;
printf("%i", *aPtr);

我不是在寻找它引发的错误。我试图理解为什么这在概念上不起作用。

【问题讨论】:

  • 多么奇怪的问题。如果语言规则要求您使用星号声明指针,那么您需要星号 声明指针。

标签: c pointers memory reference pass-by-reference


【解决方案1】:

案例1:-

int a = 5;
int *aPtr = &a; /*this is valid bcz aptr is pointer and it need address and `&a` is valid address*/
printf("%i", *aPtr);/* here you can do *aptr, bcz aptr is pointer and
                     we can dereference pointer variable */ 

案例 2 :- 当您通过使用 -Wall 编译来读取编译器警告 int aptr = &a; 时,它说明了一切。

int a = 5;
int aPtr = &a;/* aptr is normal variable, you are assigning address to it ,but you can't dereference it like *aptr bcz aptr is not pointer */     

由于aptr 是普通变量,当您执行*aptr 时,它会抛出错误,因为解引用运算符* 适用于指针变量,而不适用于普通变量。

printf("%i", *aPtr); /* invalid */ 

【讨论】:

  • 我认为您的意思是 r-value 而不是 constant
【解决方案2】:

第一个无效,因为 & 是一元运算符,需要左值(变量),不能应用于右值(常量)。

由于同样的原因,这会阻止第二个有效。但假设您将 5 替换为您创建的变量:

int a = 5;
int aPtr = &a;
printf("%i", *aPtr);

然后C的静态类型检查将看到aPtrint类型,aint类型(因此&aint*类型),然后强制转换(带有警告) &aint*int,然后将该地址作为 int 存储到aPtr。当* 取消引用aPtr 时,您将遇到错误,因为aPtr 的类型为int。由于您已从 int* 转换为 int,因此 aPtr 现在是 int,而不是 int*

如果您按以下方式修复此问题:

int a = 5;
int aPtr = &a;
printf("%i", aPtr);

然后它会编译,打印a(分配在堆栈上的变量)的地址,尽管它仍然会为从int*int 的转换发出警告。

编辑:被打败了。

【讨论】:

    【解决方案3】:

    您的示例不起作用,因为:

    sizeof(int)  == 4; // Bytes (usually)
    sizeof(int*) == 8; // Bytes (assuming x86_64)
    

    因此,在分配int aPtr = &a; 时,最重要的 4 个字节会丢失。

    这只会找到(尽管不建议):

    #include <stdint.h>
    int main()
    {
            int a = 5;
            uint64_t aPtr = (uint64_t)&a;
            printf("%i", *(int*)aPtr);
    }
    

    【讨论】:

      【解决方案4】:

      int aPtr = &amp;a; 声明了一个名为aPtr 的对象,其类型为int,因此它不是指针。

      更糟糕的是,sizeof(int) != sizeof(int *) 在几个常见平台上(例如,自 Windows XP 以来的所有 x64 版本的 Windows),因此您会丢失指针包含的一些信息,可能会使存储的值变得无用。

      假设它有效,但是,aPtr 仍然是一个类型为int 的对象,因此您不能使用指针间接来获取存储在aPtr 中的地址的值,这意味着表达式*aPtr 将不编译。没有什么能阻止您将 aPtr 转换为正确的指针类型:

      int a = 5;
      int aPtr = &a;
      printf("%i", *(int *)aPtr);
      

      再次假设aPtr 存储a 的完整地址。无论如何,这只是 C 的类型安全,这是一件好事(参见上面的 sizeof(int) != sizeof(int *) 段)。

      如果“概念上”您的意思是“假设地址正确存储在aPtr”中,那么您的代码除了它不是有效的 C 之外没有任何问题,如上面的转换示例所示。如果地址是 0x80,你甚至可以将它存储在一个很小的 ​​unsigned char 对象中,如果你愿意的话。它存储在内存中的该地址处,无论 C 通过拒绝编译语法上无效的代码来阻止您使用它做什么。当然,只使用正确的变量类型要容易得多,而不是与编译器和强制转换。

      然后,您可能会问,“如果&amp;a 存储在aPtr 中,为什么编译器不能识别并简单地理解*aPtr*&amp;a 的含义相同?”如果这就是问题所在,那么答案很简单:一旦将&amp;a 存储在某个类型为T 的对象中,编译器就会知道该对象用于存储T 类型的值,仅此而已。它不知道它包含一个内存地址,无论您是否在该对象中存储了一个;它只知道里面存储了一个T 类型的值。同样,这只是 C 的类型系统按预期工作。

      【讨论】:

      • JFYI:如果要将内存指针转换为整数,C for that 中有专门的类型:intptr_tuintptr_t。这些保证足够大以存储内存指针而不会截断它的值。
      • 是的,这是真的。然而,重要的是要了解they're not required to be declared(“这些类型是可选的。”),就像exact-width integer types such as int64_t and uint8_t。也就是说,如果您将代码限制在您知道提供它们的一组特定平台上,那么它们是非常棒的工具。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-02
      • 2013-04-01
      • 2012-11-21
      • 2018-05-21
      • 2019-12-14
      相关资源
      最近更新 更多