指针赋值概念思维块:Ptr = *array
Pointer assignment conceptual mental block: Ptr = *array
阅读本文后:
2D array and pointer in C - how to access elements?
我仍然对 'Ptr = *data' 不是取消引用感到困惑。
该问题的相关代码:
int data[4][3] = { {23,55,50},{45,38,55},{70,43,45},{34,46,60}};
int *Ptr;
Ptr = *data; // This is not a de-reference. Ptr is not 23. Why?
我在概念层面上不明白这一点。我看到'*'并且我认为
取消引用。
说 Ptr = *(data + 0), 没有帮助,对我来说它仍然意味着取消引用
我们最终取消引用地址 'data'.
任何人都可以阐明并帮助我理解吗?
感谢您的宝贵时间。
data
是二维数组,贴到int (*)[3]
指针。
*data
是三个元素 int
数组并贴花到 int *
指针。
Ptr
包含对该 int[3] 数组第一个元素的引用,即 23。
您需要取消引用此指针以获得整数 23
int main(void)
{
int data[4][3] = { {23,55,50},{45,38,55},{70,43,45},{34,46,60}};
int *ptr;
ptr = *data; // This is not a de-reference. Ptr is not 23. Why?
printf("%d\n", *ptr);
}
*data
在技术上是取消引用,但是,正如我们将看到的,它实际上不会导致对其引用的对象的访问。
data
是数组的数组。在表达式中使用数组时,它会自动转换为指向其第一个元素的指针,除非它用作 sizeof
或一元 &
的操作数,或者是用于初始化的字符串文字数组。在 *data
中,此转换有效地将 data
更改为 &data[0]
,这是指向 data
.
的第一个数组的指针
因为 &data[0]
是指向第一个数组的指针,所以 *data
就是那个数组。因此,我们取消引用 &data[0]
以获取它引用的数组。
但是,由于 *data
是一个数组,它也会自动转换为指向其第一个元素的指针。因此,不是 *data
导致对数组的访问,而是更改为 &(*data)[0]
。包括第一个转换,即 &(*&data[0])[0]
。然后,去掉自消*&
,也就是&(data[0])[0]
或者只是&data[0][0]
.
所以*data
就是&data[0][0]
,data
的第一个数组第一个元素的地址。
除非它是 sizeof
或一元 &
运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则 表达式 [= “T
的 N 元素数组”类型的 101=] 将被转换或“衰减”为“指向 T
的指针”类型的表达式,表达式的值将是地址数组中的第一个元素。
让我们画出数组及其值,显示每个元素的地址。对于此图,我们假设 4 字节 int
s 并且地址是凭空组成的:
Address Value Expression
------- +----+ ----------
0x4000 | 23 | data[0][0]
+ -- +
0x4004 | 55 | data[0][1]
+ -- +
0x4008 | 50 | data[0][2]
+----+
0x400c | 45 | data[1][0]
+ -- +
0x4010 | 38 | data[1][1]
+ -- +
0x4014 | 55 | data[1][2]
+----+
0x4018 | 70 | data[2][0]
+ -- +
0x401c | 43 | data[2][1]
+ -- +
0x4020 | 45 | data[2][2]
+----+
0x4024 | 34 | data[3][0]
+ -- +
0x4028 | 46 | data[3][1]
+ -- +
0x402c | 60 | data[3][2]
+----+
现在我们可以谈谈各种表达式的类型了。
让我们从表达式 data
开始。如声明的那样,表达式的类型为“int
的 3 元素数组的 4 元素数组”(int [4][3]
)。除非它是 sizeof
或一元 &
运算符的操作数,否则它将“衰减”为“指向 int
的 3 元素数组的指针”类型的表达式(int (*)[3]
) 并将评估地址 0x4000
.
接下来,我们看一下表达式*data
。我们已经知道 data
的类型为“指向 int
的 3 元素数组的指针”(int (*)[3]
),因此通过取消引用它,我们得到类型为“3 元素数组的表达式” int
”(int [3]
)。除非此表达式是 sizeof
或一元 &
运算符的操作数,否则它将“衰减”为“指向 int
” (int *
) 的指针,并且还会求值到地址 0x4000
.
现在让我们看一下表达式 data[0]
、data[1]
、data[2]
和 data[3]
。请记住,数组下标表达式 a[i]
完全等同于表达式 *(a + i)
- 给定起始地址 a
,偏移量 i
元素 (不是字节!)从该地址并取消引用结果。由于data
指向int
的三元素数组,data + 1
将指向int
的下一个三元素数组.因此,如果 data == 0x4000
,则 data + 1 == 0x400c
、data + 2 == 0x4018
和 data + 3 == 0x4024
。由于data[i] == *(data + i)
,每个data[i]
的类型将是int [3]
(就像上面的*data
,正好等同于data[0]
因为 *data == *(data + 0) == data[0]
).
希望每个 data[i][j]
在这一点上是显而易见的 - 每个 data[i]
都有类型 int [3]
,它“衰减”到 int *
和 data[i][j] == *(data[i] + j)
。
其他一些注意事项:
&data
具有类型“指向 int
的 3 元素数组的 4 元素数组的指针”,或 int (*)[4][3]
,其值也是 0x4000
因为数组的地址与其第一个元素的地址相同。
类似地,&data[i]
具有类型“指向 int
的 3 元素数组的指针”(int (*)[3]
);如上面的表达式,数组的地址与第一个元素的地址相同,所以&data[0] == data[0] == 0x4000
,&data[1] == data[1] == 0x400c
,等等
总结一下:
Expression Type "Decays" to Value
---------- ---- ----------- ------
data int [4][3] int (*)[3] 0x4000
*data int [3] int * 0x4000
&data int (*)[4][3] n/a 0x4000
data[i] int [3] int * 0x4000, 0x400c, 0x4018, 0x4024
*data[i] int na/ 23, 45, 70, 34
&data[i] int (*)[3] n/a 0x4000, 0x400c, 0x4018, 0x4024
data[i][j] int n/a 23, 55, 50, ...
我正在发布另一个示例,一个完整的 C 程序,这可能有助于了解其背后的逻辑
首先,一些细节
I'm still confused about the 'Ptr = *data' not being a de-reference.
嗯,这是一个取消引用,赋值中的 *
在 C 中是一个取消引用。BUT *data
不是 int
:它是int[4][3]
,保存数组第一个元素的地址,它指向一个int
,它是23
.您的代码中丢失了一级间接寻址。
Ptr is int*
*Ptr is an address, &data[0][0] or simply *data
**Ptr is 23
理解这一点的关键是行
Ptr = *data;
看看 gcc 在编译时说了什么:
toninho@DSK-2009:~/projects/dsp$ gcc -o tptr -Wall -Wextra -std=c17 tptr.c
tptr.c: In function ‘main’:
tptr.c:19:9: warning: assignment to ‘int *’ from\
incompatible pointer type int (*)[4][3]’ [-Wincompatible-pointer-types]
19 | Ptr = &data;
| ^
Microsoft 编译器说:
1>------ Build started: Project: sop-0328-a, Configuration: Debug Win32 ------
1>ptr.c
1>C:\Users\toninho\source\repos\sop-0328-a\sop-0328-a\ptr.c(19,20):
warning C4047: '=': 'int *' differs in levels of indirection from 'int (*)[4][3]'
1>sop-0328-a.vcxproj -> C:\Users\toninho\source\repos\sop-0328-a\Debug\sop-0328-a.exe
1>Done building project "sop-0328-a.vcxproj".
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
clang 编译器说:
C:\Users\toninho\source\repos\sop-0328-a\sop-0328-a>clang -Wall ptr.c
ptr.c:19:13: warning: incompatible pointer types assigning
to 'int *' from 'int (*)[4][3]'
[-Wincompatible-pointer-types]
Ptr = &data;
^ ~~~~~
1 warning generated.
而且是一样的。 您应该始终在所有编译器上启用所有警告。
I don't understand this on a conceptual level. I the see ' * ' and I
think de-reference.
Saying Ptr = *(data + 0), doesn't help, to me it still means
de-reference and we end up de-referencing the address 'data'.
Can anyone shed some light and help me understand?
正如我所说,这是取消引用。问题是您将 int*[4][3]
传递给了 int*
,没有启用编译器警告,并且 :) 没有意识到这一点。专业人士一直这样做,有时也会感到惊讶。
你应该写 Ptr = (int*) *data;
相信下面的程序能表现得更好。添加到 0 肯定没有区别。您需要添加另一个 *
请看下面的代码,还有不明白的再问。
请注意,代码末尾的行
pointer = (int*) &many;
printf("Using a cast 'pointer' now points to '(int*) many' \
and its value is %d\n", *pointer);
使用转换并打印
Using a cast 'pointer' now points to '(int*) many' and its value is 23
现在编译器很满意,输出是预期的 23
例子
程序的输出是
'one' is an int at address 00EFFDB4
'many'is int[4][3] at address 00EFFD7C
'pointer' is int* at address 00EFFDC0
'pointer' now points to 'one' and its value is 00EFFDB4
'pointer' now points to 'many' and its value is 00EFFD7C
'many' is a C array, a pointer pointing to the address 00EFFD7C ( &many[0][0] )
'many' content, an address, is 00EFFD7C ( *many )
Starting at this location --- 00EFFD7C ( *many ) --- we have the values of the array
First value is 23 ( **many ),
Second value is 55 ( *(1 + *many)... )
Usng a cast 'pointer' now points to '(int*) many' and its value is 23
您可以在调试器屏幕上看到地址,以及变量的实际类型(程序在第 23 行停止):
密码
#include <stdio.h>
int main(void)
{
int* pointer = NULL;
int one = 1;
int many[4][3] =
{
{23,55,50},
{45,38,55},
{70,43,45},
{34,46,60}
};
printf("'one' is an int at address\t%p\n", &one);
printf("'many'is int[4][3] at address\t%p\n", &many);
printf("'pointer' is int* at address\t%p\n\n", &pointer);
pointer = &one;
printf("'pointer' now points to 'one' and its value is\t%p\n", pointer);
pointer = &many;
printf("'pointer' now points to 'many' and its value is\t%p\n", pointer);
printf("\n'many' is a C array, a pointer pointing to the address\t%p ( &many[0][0] )\n", &many[0][0]);
printf("'many' content, an address, is\t%p ( *many )\n", *many);
printf("\nStarting at this location --- %p ( *many ) --- we have the values of the array\n\
First value is %d ( **many ),\n\
Second value is %d ( *(1 + *many)... )\n", *many, **many, *(1 + *many) );
pointer = (int*) &many;
printf("Using a cast 'pointer' now points to '(int*) many' \
and its value is %d\n", *pointer);
return 0;
}
阅读本文后: 2D array and pointer in C - how to access elements?
我仍然对 'Ptr = *data' 不是取消引用感到困惑。
该问题的相关代码:
int data[4][3] = { {23,55,50},{45,38,55},{70,43,45},{34,46,60}};
int *Ptr;
Ptr = *data; // This is not a de-reference. Ptr is not 23. Why?
我在概念层面上不明白这一点。我看到'*'并且我认为 取消引用。
说 Ptr = *(data + 0), 没有帮助,对我来说它仍然意味着取消引用 我们最终取消引用地址 'data'.
任何人都可以阐明并帮助我理解吗?
感谢您的宝贵时间。
data
是二维数组,贴到int (*)[3]
指针。
*data
是三个元素 int
数组并贴花到 int *
指针。
Ptr
包含对该 int[3] 数组第一个元素的引用,即 23。
您需要取消引用此指针以获得整数 23
int main(void)
{
int data[4][3] = { {23,55,50},{45,38,55},{70,43,45},{34,46,60}};
int *ptr;
ptr = *data; // This is not a de-reference. Ptr is not 23. Why?
printf("%d\n", *ptr);
}
*data
在技术上是取消引用,但是,正如我们将看到的,它实际上不会导致对其引用的对象的访问。
data
是数组的数组。在表达式中使用数组时,它会自动转换为指向其第一个元素的指针,除非它用作 sizeof
或一元 &
的操作数,或者是用于初始化的字符串文字数组。在 *data
中,此转换有效地将 data
更改为 &data[0]
,这是指向 data
.
因为 &data[0]
是指向第一个数组的指针,所以 *data
就是那个数组。因此,我们取消引用 &data[0]
以获取它引用的数组。
但是,由于 *data
是一个数组,它也会自动转换为指向其第一个元素的指针。因此,不是 *data
导致对数组的访问,而是更改为 &(*data)[0]
。包括第一个转换,即 &(*&data[0])[0]
。然后,去掉自消*&
,也就是&(data[0])[0]
或者只是&data[0][0]
.
所以*data
就是&data[0][0]
,data
的第一个数组第一个元素的地址。
除非它是 sizeof
或一元 &
运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则 表达式 [= “T
的 N 元素数组”类型的 101=] 将被转换或“衰减”为“指向 T
的指针”类型的表达式,表达式的值将是地址数组中的第一个元素。
让我们画出数组及其值,显示每个元素的地址。对于此图,我们假设 4 字节 int
s 并且地址是凭空组成的:
Address Value Expression
------- +----+ ----------
0x4000 | 23 | data[0][0]
+ -- +
0x4004 | 55 | data[0][1]
+ -- +
0x4008 | 50 | data[0][2]
+----+
0x400c | 45 | data[1][0]
+ -- +
0x4010 | 38 | data[1][1]
+ -- +
0x4014 | 55 | data[1][2]
+----+
0x4018 | 70 | data[2][0]
+ -- +
0x401c | 43 | data[2][1]
+ -- +
0x4020 | 45 | data[2][2]
+----+
0x4024 | 34 | data[3][0]
+ -- +
0x4028 | 46 | data[3][1]
+ -- +
0x402c | 60 | data[3][2]
+----+
现在我们可以谈谈各种表达式的类型了。
让我们从表达式 data
开始。如声明的那样,表达式的类型为“int
的 3 元素数组的 4 元素数组”(int [4][3]
)。除非它是 sizeof
或一元 &
运算符的操作数,否则它将“衰减”为“指向 int
的 3 元素数组的指针”类型的表达式(int (*)[3]
) 并将评估地址 0x4000
.
接下来,我们看一下表达式*data
。我们已经知道 data
的类型为“指向 int
的 3 元素数组的指针”(int (*)[3]
),因此通过取消引用它,我们得到类型为“3 元素数组的表达式” int
”(int [3]
)。除非此表达式是 sizeof
或一元 &
运算符的操作数,否则它将“衰减”为“指向 int
” (int *
) 的指针,并且还会求值到地址 0x4000
.
现在让我们看一下表达式 data[0]
、data[1]
、data[2]
和 data[3]
。请记住,数组下标表达式 a[i]
完全等同于表达式 *(a + i)
- 给定起始地址 a
,偏移量 i
元素 (不是字节!)从该地址并取消引用结果。由于data
指向int
的三元素数组,data + 1
将指向int
的下一个三元素数组.因此,如果 data == 0x4000
,则 data + 1 == 0x400c
、data + 2 == 0x4018
和 data + 3 == 0x4024
。由于data[i] == *(data + i)
,每个data[i]
的类型将是int [3]
(就像上面的*data
,正好等同于data[0]
因为 *data == *(data + 0) == data[0]
).
希望每个 data[i][j]
在这一点上是显而易见的 - 每个 data[i]
都有类型 int [3]
,它“衰减”到 int *
和 data[i][j] == *(data[i] + j)
。
其他一些注意事项:
&data
具有类型“指向int
的 3 元素数组的 4 元素数组的指针”,或int (*)[4][3]
,其值也是0x4000
因为数组的地址与其第一个元素的地址相同。类似地,
&data[i]
具有类型“指向int
的 3 元素数组的指针”(int (*)[3]
);如上面的表达式,数组的地址与第一个元素的地址相同,所以&data[0] == data[0] == 0x4000
,&data[1] == data[1] == 0x400c
,等等
总结一下:
Expression Type "Decays" to Value
---------- ---- ----------- ------
data int [4][3] int (*)[3] 0x4000
*data int [3] int * 0x4000
&data int (*)[4][3] n/a 0x4000
data[i] int [3] int * 0x4000, 0x400c, 0x4018, 0x4024
*data[i] int na/ 23, 45, 70, 34
&data[i] int (*)[3] n/a 0x4000, 0x400c, 0x4018, 0x4024
data[i][j] int n/a 23, 55, 50, ...
我正在发布另一个示例,一个完整的 C 程序,这可能有助于了解其背后的逻辑
首先,一些细节
I'm still confused about the 'Ptr = *data' not being a de-reference.
嗯,这是一个取消引用,赋值中的 *
在 C 中是一个取消引用。BUT *data
不是 int
:它是int[4][3]
,保存数组第一个元素的地址,它指向一个int
,它是23
.您的代码中丢失了一级间接寻址。
Ptr is int*
*Ptr is an address, &data[0][0] or simply *data
**Ptr is 23
理解这一点的关键是行
Ptr = *data;
看看 gcc 在编译时说了什么:
toninho@DSK-2009:~/projects/dsp$ gcc -o tptr -Wall -Wextra -std=c17 tptr.c
tptr.c: In function ‘main’:
tptr.c:19:9: warning: assignment to ‘int *’ from\
incompatible pointer type int (*)[4][3]’ [-Wincompatible-pointer-types]
19 | Ptr = &data;
| ^
Microsoft 编译器说:
1>------ Build started: Project: sop-0328-a, Configuration: Debug Win32 ------
1>ptr.c
1>C:\Users\toninho\source\repos\sop-0328-a\sop-0328-a\ptr.c(19,20):
warning C4047: '=': 'int *' differs in levels of indirection from 'int (*)[4][3]'
1>sop-0328-a.vcxproj -> C:\Users\toninho\source\repos\sop-0328-a\Debug\sop-0328-a.exe
1>Done building project "sop-0328-a.vcxproj".
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
clang 编译器说:
C:\Users\toninho\source\repos\sop-0328-a\sop-0328-a>clang -Wall ptr.c
ptr.c:19:13: warning: incompatible pointer types assigning
to 'int *' from 'int (*)[4][3]'
[-Wincompatible-pointer-types]
Ptr = &data;
^ ~~~~~
1 warning generated.
而且是一样的。 您应该始终在所有编译器上启用所有警告。
I don't understand this on a conceptual level. I the see ' * ' and I think de-reference.
Saying Ptr = *(data + 0), doesn't help, to me it still means de-reference and we end up de-referencing the address 'data'.
Can anyone shed some light and help me understand?
正如我所说,这是取消引用。问题是您将 int*[4][3]
传递给了 int*
,没有启用编译器警告,并且 :) 没有意识到这一点。专业人士一直这样做,有时也会感到惊讶。
你应该写 Ptr = (int*) *data;
相信下面的程序能表现得更好。添加到 0 肯定没有区别。您需要添加另一个 *
请看下面的代码,还有不明白的再问。
请注意,代码末尾的行
pointer = (int*) &many;
printf("Using a cast 'pointer' now points to '(int*) many' \
and its value is %d\n", *pointer);
使用转换并打印
Using a cast 'pointer' now points to '(int*) many' and its value is 23
现在编译器很满意,输出是预期的 23
例子
程序的输出是
'one' is an int at address 00EFFDB4
'many'is int[4][3] at address 00EFFD7C
'pointer' is int* at address 00EFFDC0
'pointer' now points to 'one' and its value is 00EFFDB4
'pointer' now points to 'many' and its value is 00EFFD7C
'many' is a C array, a pointer pointing to the address 00EFFD7C ( &many[0][0] )
'many' content, an address, is 00EFFD7C ( *many )
Starting at this location --- 00EFFD7C ( *many ) --- we have the values of the array
First value is 23 ( **many ),
Second value is 55 ( *(1 + *many)... )
Usng a cast 'pointer' now points to '(int*) many' and its value is 23
您可以在调试器屏幕上看到地址,以及变量的实际类型(程序在第 23 行停止):
密码
#include <stdio.h>
int main(void)
{
int* pointer = NULL;
int one = 1;
int many[4][3] =
{
{23,55,50},
{45,38,55},
{70,43,45},
{34,46,60}
};
printf("'one' is an int at address\t%p\n", &one);
printf("'many'is int[4][3] at address\t%p\n", &many);
printf("'pointer' is int* at address\t%p\n\n", &pointer);
pointer = &one;
printf("'pointer' now points to 'one' and its value is\t%p\n", pointer);
pointer = &many;
printf("'pointer' now points to 'many' and its value is\t%p\n", pointer);
printf("\n'many' is a C array, a pointer pointing to the address\t%p ( &many[0][0] )\n", &many[0][0]);
printf("'many' content, an address, is\t%p ( *many )\n", *many);
printf("\nStarting at this location --- %p ( *many ) --- we have the values of the array\n\
First value is %d ( **many ),\n\
Second value is %d ( *(1 + *many)... )\n", *many, **many, *(1 + *many) );
pointer = (int*) &many;
printf("Using a cast 'pointer' now points to '(int*) many' \
and its value is %d\n", *pointer);
return 0;
}