指针
代码:#include <stdio.h>
int main(void) {
void *ptr;
int p[] = {5, 9};
ptr = &p;
printf("%d", *(int *)(ptr + 1));
return 0;
}
编译:
$ gcc test.c&&./a.out
150994944$ gcc test.c&&./a.out
150994944
注:$前字符已略去
请解释现象,谢谢 哈哈,这个问题有点意思,看来你对C语言中的指针有了一些初步的接触。让鱼油来帮你解析一下吧!
首先,让我们来看看你的代码:
#include <stdio.h> // 需要包含stdio.h才能使用printf
int main(void) {
void *ptr;
int p = {5, 9};
ptr = &p; // 将p的地址赋给ptr
printf("%d", *(int *)(ptr + 1)); // 这里是有问题的
return 0;
}
**Step by step 分析:**
1. `#include <stdio.h>`:我们需要包含这个头文件才能使用`printf`函数,不过在你的代码中漏掉了。
2. `void *ptr;`:定义了一个通用指针(void类型),它可以指向任何类型的数据。
3. `int p = {5, 9};`:定义了一个包含两个整数元素的数组`p`,其值为5和9。注意,这里的`p`应该是一个数组而不是单个变量。
4. `ptr = &p;`:将数组`p`的地址赋给指针`ptr`。这实际上会将`ptr`指向一个数组的地址,而不是数组的第一个元素。
5. `printf("%d", *(int *)(ptr + 1));`:这里是问题的关键。今年鱼油来揭开谜团:
- 首先,`ptr`是一个`void*`类型的指针,在C语言中,它是没有类型信息的。
- `ptr + 1` 操作是对`void*`指针进行加1操作,但由于`void*`没有具体的大小,所以编译器不知道要加多少。
- 通过`*(int *)(ptr + 1)` 试图读取一个整数,这里发生了错误。
实际上,我们想做的是访问`p`数组的第二个元素`p`。所以正确的代码应该是:
#include <stdio.h>
int main(void) {
int *ptr;
int p = {5, 9};
ptr = p; // 这里直接将数组首元素的地址赋给ptr
printf("%d\n", *(ptr + 1)); // 访问数组的第二个元素
return 0;
}
1. 我们把`void *`换成了`int *`。
2. 直接把`p`赋值给`ptr`,因为`p`本身是一个指向数组首元素的指针。
3. 使用`*(ptr + 1)`访问数组的第二个元素。
这样改完后,程序输出应该是`9`,而不是一些神秘的巨大数字`150994944`。
希望这个解释能让你像鱼儿一样游刃有余地理解指针!请继续加油哦!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 看不懂你在说什么,喂给了gpt4:
你的代码涉及指针算术和类型转换,这在 C 语言中非常重要,但也容易出错。让我们一步步分析你的代码,以解释为什么你的输出是 150994944。
代码解析
首先,我们来看代码的每一部分:
#include <stdio.h>
int main(void) {
void *ptr;
int p[] = {5, 9};
ptr = &p;
printf("%d", *(int *)(ptr + 1));
return 0;
}
1. 定义和初始化
void *ptr;
int p[] = {5, 9};
这里定义了一个指针 ptr 和一个包含两个整数的数组 p。
2. 指针赋值
ptr = &p;
这行代码将指针 ptr 指向数组 p 的地址。注意 p 是一个数组,&p 是指向这个数组的指针(实际上是指向数组第一个元素的指针)。
3. 指针算术和类型转换
printf("%d", *(int *)(ptr + 1));
这里是问题的关键部分。让我们分解这段代码:
ptr 是一个 void * 类型的指针,指向数组 p。
ptr + 1 表示指针加1,但由于 ptr 是 void * 类型,指针算术在这里并不是以 int 大小为单位,而是以 void 类型大小为单位。对于大多数编译器,void * 指针加1相当于加1字节。
(int *)(ptr + 1) 先将 ptr + 1 结果转换为 int * 类型。
*(int *)(ptr + 1) 解引用这个 int * 指针,试图访问该地址的 int 值。
问题出在哪里?
当你执行 ptr + 1 时,实际上地址被加了1字节,而不是4字节(假设 int 是4字节)。因此,你访问的地址并不是数组中的第二个元素,而是数组地址之后的一个字节位置,并试图解释它为一个 int。
具体情况如下:
ptr 指向 p,即 ptr 指向数组的第一个元素 5。
ptr + 1 使指针向前移动一个字节,而不是一个 int。
当你将这个结果转换为 int * 时,你实际上是尝试从一个错误的地址解引用一个 int,这会导致未定义行为。
为什么输出 150994944?
未定义行为的结果是不可预测的。在你的特定情况下,地址可能对应一些内存内容,这些内容被解释为一个 int 类型,结果是 150994944。这个值没有任何特别意义,只是内存中那段数据的一个解读。 本帖最后由 人造人 于 2024-7-4 23:34 编辑
假设一个int是4个字节
那这个数组p就是下面这样的8个字节数据
05 00 00 00 09 00 00 00
ptr = &p; // 让ptr指向05这个字节,就是下面这样
05 00 00 00 09 00 00 00
.
/|\
|
|
ptr 指向这里
05 00 00 00 09 00 00 00
.
/|\
|
|
ptr + 1 就指向了这里
*(int *)(ptr + 1) // 从这个ptr + 1 的地方读取一个int
05 00 00 00 09 00 00 00
.
/|\
|
|
ptr + 1 就指向了这里
从这里取一个int,就是取4个字节
05 00 00 00
.
/|\
|
|
就是这4个字节,用这4个字节重新组成一个int
00 00 00 09 组成一个int就是 0x09000000 (这里没有问题吧?)
0x09000000 就是十进制数150994944
代码格式也不是等宽字体,也没有对齐
那就再补上一个图片吧
人造人 发表于 2024-7-4 23:32
为什么是:
05 00 00 00 09 00 00 00
而不是:
00 00 00 05 00 00 00 09
呢? 人造人 发表于 2024-7-4 23:32
是因为数据存放的位置吗? 风眠 发表于 2024-7-5 16:41
是因为数据存放的位置吗?
因为他们被放到了栈里? 风眠 发表于 2024-7-5 16:42
因为他们被放到了栈里?
这个
https://fishc.com.cn/thread-75394-1-4.html 风眠 发表于 2024-7-5 16:40
为什么是:
05 00 00 00 09 00 00 00
而不是:
因为还有一个假设,假设字节序是小端的
人造人 发表于 2024-7-5 21:05
因为还有一个假设,假设字节序是小端的
对了,可以帮我看看吗?谢谢
我复制的,好像有问题,但lsp没报错传送门
页:
[1]