【问题标题】:Assigning an int to a pointer, What happens?将 int 分配给指针,会发生什么?
【发布时间】:2017-02-14 08:58:25
【问题描述】:

而不是像这样初始化一个指针,

int main (){
    int *ptr;
    int x = 5;
    ptr = &x;
}

当你做这样的事情时,内存中会发生什么?

int main (){
    int *ptr = 100;
}

*ptr 是在寻找包含值 100 的随机地址,还是将 100 的值存储在 ptr 中?

【问题讨论】:

标签: c pointers


【解决方案1】:

这是一个约束冲突,编译器应该给你一个诊断信息。 (如果编译器没有说“错误”,那么我建议更改编译器标志以便它这样做)。如果编译器仍然生成可执行文件,则该可执行文件的行为是未定义的。

在标准 C 中,此代码将 100 分配给指针,正如其他几个 cmets/answers 所声称的那样。整数不能分配给指针(即使用赋值运算符或初始化语法,右侧为整数),常量表达式为零的特殊情况除外。

要尝试将指针指向地址 100,代码应为 int *ptr = (int *)100;

【讨论】:

  • 我的标准副本有所不同。 6.3.2.3 指针:“整数可以转换为任何指针类型。除非前面指定,结果是实现定义的 [..]”。
  • @LarsFriedrich 在这种情况下,需要显式转换运算符。该部分中的转换并非都是隐式转换。查找赋值运算符约束
  • 也许您应该改写答案的“整数不能分配给指针”部分,因为它会误导您陈述的意图。
  • @LarsFriedrich 在int *ptr = 100; 行中,没有任何东西可以隐式或显式地导致转换。因此,“简单赋值”的规则适用——并且将整数分配给指针不是简单赋值的有效形式,因此违反了约束。 (如果赋值是有效的,那么左操作数的类型就会“左值转换”。)如果你强制进行转换,例如添加显式强制转换,那么 6.3.2.3 中的规则就会生效在。
【解决方案2】:

首先,正如其他答案中提到的那样,代码int *ptr = 100; 甚至不是有效的C。将整数分配给指针不是简单赋值(6.5.16.1)的有效形式,因此代码是-称为“约束违反”,意思是它违反了 C 标准。

因此,您首先关心的是为什么您的编译器不遵循标准。如果您使用 gcc,请注意,不幸的是,默认情况下它没有配置为符合标准的编译器,您必须通过 -std=c11 -pedantic-errors 告诉它成为一个标准编译器。

排序后,您可以通过显式强制转换将整数转换为指针类型,从而将代码修复为有效的 C。 int *ptr = (int*)100;

这意味着将地址 100 存储在指针变量中。没有尝试访问该内存位置。

如果您尝试通过例如*ptr = 0; 访问该内存,那么您的工作就是确保100 是给定系统的对齐地址。如果没有,您将调用未定义的行为。

这就是 C 语言所关心的。 C 不知道也不关心地址 100 存储了什么。它超出了语言的范围。在某些系统上,这可能是一个完全有效的地址,而在其他系统上,这可能是无稽之谈。

【讨论】:

  • 因此,人们告诉你“呃,伙计,如果你访问地址 100,你会遇到 seg 错误”,可能会假设你使用的是 PC 或其他带有虚拟内存的系统,它阻止直接访问物理内存地址。但是问题中没有提到系统,所以这样的假设是不成立的。
  • 赋值本身会导致未定义的行为,不需要取消引用指针。 (未对齐的指针会立即导致 ub。)
  • @2501 标准还不是很清楚。 6.3.2.3/5。 “一个整数可以转换为任何指针类型。除了前面指定的,结果是实现定义的,可能没有正确对齐,可能不指向引用类型的实体,并且可能是一个陷阱表示。”跨度>
  • 其中两个,对齐和陷阱,可以立即导致ub。有什么不清楚的地方?
  • 这无疑是这里最好、最完整的答案。
【解决方案3】:
int *ptr = (int*)100; // valid
int *ptr = 100; // invalid as int to int * cast is not automatic

不鼓励使用绝对地址,因为在可重定位的程序段中,您永远不会知道指针应该在哪里作为某个地址的值,而是应该指向有效地址以避免意外。

【讨论】:

    【解决方案4】:

    编译器不会给你任何错误。但这是违反约束的。指针变量存储其他变量的地址,*ptr 用于更改存储在该地址的值。

    【讨论】:

      【解决方案5】:

      它的长短是没什么。将值100 分配给指针值p 会做一些事情;它将指针值的地址设置为100,就像将调用malloc 的值分配给它一样;但由于该值从未被使用过,它实际上并没有任何事情。我们可以看看这个值产生了什么:

      /* file test.c */
      #include <stdlib.h>
      #include <stdio.h>
      
      int main() {
        int *ptr = (int *) 100;
        printf("%p\n", ptr);
        return 0;
      }
      

      执行此操作会产生以下输出:

      0x64
      

      从某种意义上说,指针只是一个整数。它们(有时)占用大约相同大小的整数(取决于平台),并且可以轻松地转换为整数。在 C11 中甚至为与指针大小相同的整数定义了一个特定(可选)类型:intptr_t(请参阅What is the use of intptr_t?)。

      回到正题,试图对该指针执行任何类型的解引用都可能导致奇怪的行为(tm)——内存的布局取决于平台和实现,因此试图在地址 @987654330 处获取一些东西@ 可能会导致分段错误(如果幸运的话):

      /* file test.c */
      #include <stdlib.h>
      #include <stdio.h>
      
      int main() {
        int *ptr = (int *) 100;
        printf("%i\n", *ptr);
        return 0;
      }
      

      执行结果如下:

      fish: "./test" terminated by signal SIGSEGV (Address boundary error)
      

      因此,除非您确切知道自己在做什么以及为什么这样做,否则不要这样做。

      【讨论】:

      • don't put images of code or text output here。而是复制和粘贴。当外部链接失效时,您的答案将无效
      • @LưuVĩnhPhúc 图片已删除。
      • 您的示例不是有效的标准 C.
      • 在旧版本的 C 中,您不需要强制转换来将整数分配给指针,但您现在就可以了。如果我使用 gcc -std=c99 编译您的示例,我只会收到警告,但使用 -std=c11 我会收到错误:Assignment make pointer from integer without a cast.
      【解决方案6】:

      与:

      int *ptr = 100;
      

      您已将值100 分配给指针。用它做任何事情但打印它的值将是未定义的行为:

      printf ("pointer value is %p\n", (void *)ptr);
      

      【讨论】:

      • 在嵌入式上下文中你知道你在地址 100 处有一个你想摆弄的寄存器呢?
      • @Colin__s,在该特定上下文中,行为由平台定义。
      • 除了打印之外你可以用它做的事情:重新分配,减去,添加,......
      • int *ptr = 100; 所做的只是触发编译器错误。确保使用标准编译器。如果你添加一个演员 int *ptr = (int*)100; 它确实 not 导致未定义的行为 - 就 C 而言,这将是非常好的代码。
      • @Lundin,这就是我说的(或打算说的)。我打算说它的用法可能导致UB。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-18
      • 2014-08-02
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2021-03-23
      • 1970-01-01
      相关资源
      最近更新 更多