在此代码中:
#include<stdio.h>
int main()
{
int num[2] = {20, 30};
printf("%d", num);
printf("%d", &num[0]);
return 0;
}
据我所知,printf语句都将打印num
中第一个元素的地址,因为在第一个语句中,num
是指向int的指针。
但如果num
是一个指针,那么它也应该有任何地址,但在打印地址时(printf("%d", &num)
),它会显示第一个元素的地址。
在二维数组中,整个事情也变得混乱:
#include<stdio.h>
int main(void)
{
int num[ ] [2]={20,30,40,50};
printf("%d",*num);
return 0;
}
该程序正在打印第0个地址,即num[0][0]
的地址。但为什么会这样呢?为什么不打印存储在其中的值,因为它们都具有相同的地址(num,num[0]
和num[0][0]
)?
答案 0 :(得分:5)
首先要做的事情;数组变量不是指针;他们不存储任何地址。
用于声明,例如
T a[N];
记忆将被列为
+---+
a[0]: | |
+---+
a[1]: | |
+---+
...
+---+
a[N-1]: | |
+---+
对于2D MxN阵列,它看起来像
+---+
a[0][0]: | |
+---+
a[0][1]: | |
+---+
...
+---+
a[0][N-1]: | |
+---+
a[1][0]: | |
+---+
a[1][1]: | |
+---+
...
+---+
a[M-1][N-1]: | |
+---+
3D和更高阵列的模式应该是显而易见的。
如您所见,没有为包含第一个元素的地址的单独变量a
留出存储空间;相反,在C语言中有一条规则,即“{元素数组T
”类型的表达式将被转换(“衰变”)到类型为“T
的指针”的表达式,当数组表达式为以下之一时,表达式的值将是数组的第一个元素的地址,除之外: / p>
sizeof
运算符的操作数&
运算符的操作数_Alignof
运算符的操作数(C99及更高版本)所以给出了声明
T a[N];
以下所有情况均属实:
Expression Type Decays to Value
---------- ---- --------- -----
a T [N] T * address of first element, &a[0]
*a T n/a value stored in first element
&a T (*)[N] n/a address of the array, which is
the same as the address of the
first element of the array
a[i] T n/a value stored in the i'th element
&a[i] T * n/a address of the i'th element
sizeof a size_t n/a total number of bytes used by the
array
sizeof *a size_t n/a total number of bytes used by the
first element of the array
sizeof &a size_t n/a total number of bytes used by a
pointer to the array
表达式a
的类型为“{-1}}的N元素数组”;它不是一元T
或&
运算符的操作数,因此它被转换为指向数组第一个元素的指针,并且它的值是该元素的地址。
表达式sizeof
具有类型“指向&a
的N元素数组的指针”;由于T
是一元a
运算符的操作数,因此不应用上面的转换规则(这就是表达式具有&
而不是T (*)[N]
的原因)。但是,由于数组的地址与数组的第一个元素的地址相同,因此它产生与表达式T **
相同的值。
表达式a
具有类型“指向&a[0]
的指针”,并明确指向数组的第一个元素。同样,此值将与前两个表达式相同。
对于2D数组
T
以下所有情况均属实:
T a[M][N];
最后注意事项:要打印出指针值,请使用Expression Type Decays to Value
---------- ---- --------- -----
a T [M][N] T (*)[N] address of first subarray, a[0]
*a T [N] T * address pf first subarray, a[0]
&a T (*)[M][N] n/a address of the array, which is
the same as the address of the
first subarray, which is the same
as the address of the first element
of the first subarray.
a[i] T [N] T * address of first element of i'th
subarray
*a[i] T n/a value of first element of i'th subarray
&a[i] T (*)[N] n/a address of the i'th subarray
sizeof a size_t n/a total number of bytes used by the
array
sizeof *a size_t n/a total number of bytes used by the
first subarray
sizeof &a size_t n/a total number of bytes used by a
pointer to the array
转换说明符并将参数强制转换为%p
(这几乎是唯一一次将指针显式转换为{ {1}}):
(void *)
修改强>
回答评论中的问题:
num,num []和num [] []都是不同的东西。类型是不同的。这些数字衰减并成为指针的指针,num []衰减并成为指向int的指针,而num [] []是一个int。正确?
不完全。
假设声明如
void *
然后表达式printf( " &a yields %p\n", (void *) &a );
printf( " a yields %p\n", (void *) a );
printf( "&a[0] yields %p\n", (void *) &a[0] );
将衰减为int arr[10][10];
(指向arr
的10个元素数组的指针),而不是int (*)[10]
;再次参考上表。否则你是对的; int
会拒绝输入int **
,arr[i]
会输入int *
类型。
“{元素数组arr[i][j]
”类型的表达式衰减以键入“指向int
的指针”;如果T
是数组类型,则结果是“指向数组的指针”,而不是“指针指针”。
答案 1 :(得分:1)
在第二个例子中,num
是一个二维数组,或者说一个数组数组。确实*num
是它的第一个元素,但第一个元素本身就是一个数组。
要获得num[0][0]
,您需要**num
。
printf("%d\n", **num);
答案 2 :(得分:1)
查看数组的外观:
int num[ ] [2]={20,30,40,50};
最好写成
int num[][2]={{20,30},{40,50}};
这是一个包含2个元素的数组。这两个元素同样是具有2个整数的数组。
在记忆中,它们看起来像
20 30 40 50
但区别在于num
指的是整个数组,num[0]
指向第一个“part-array”,num[0][0]
指向第一个数组的第一个元素。
他们有相同的地址(因为他们从同一个地方开始),但他们有不同的类型。
也就是说,地址不是指针唯一重要的东西,类型也很重要。
答案 3 :(得分:0)
实际上,数组并不是指针,尽管它们往往采用类似的方式,但并非总是如此。
假设你有这个数组和一个指针:
int a[] = {1, 2, 3};
int i = 19;
int *ptr = &i;
现在a is equal to &a
,但指针(ptr is not equal to &ptr
)也是如此。
现在提出问题:
考虑一个单维数组:
int arr[] = {11, 19, 5, 9};
这里,这些数组元素存储在连续的存储单元中。比方说,起始地址为0
:
---------------------
| 11 | 19 | 5 | 9 |
---------------------
0 4 8 12 16
现在,当您编写数组名称arr(对于此示例)时,您将获得1 st 元素的起始地址。虽然如果你写&arr
,那么你得到整个块的起始地址(这包括数组的所有元素)。现在当你写*arr
时,你实际上得到了这个数组的1 st 元素内的值。
现在考虑这个二维数组arr [] [4] = {{11,19,5,9},{5,9,11,19}}:
0 4 8 12 16 -> These are memory addresses
---------------------
| 11 | 19 | 5 | 9 | ----> These values represent the values inside each index
---------------------
| 5 | 9 | 11 | 19 |
---------------------
16 20 24 28 32
这里,当您编写数组的名称时,arr
,您获得的是此数组的1 st 元素的地址,在这种情况下将是地址这个0 th 指数:
0 16 32
----------------------------------------------
| 0<sup>th</sup> index | 1<sup>st</sup> index |
----------------------------------------------
现在当你做&arr
时,你得到的是整个块的基地址,即基地址:
0 4 8 12 16
---------------------
| 11 | 19 | 5 | 9 |
---------------------
| 5 | 9 | 11 | 19 |
---------------------
16 20 24 28 32
现在,如果你做*arr
,在1维数组中它会给出1 st 元素内的值,尽管在二维数组中,每个索引中的值是实际上是一个1维数组,因此你将得到这个数组的地址:
0 4 8 12 16
---------------------
| 11 | 19 | 5 | 9 |
---------------------
现在,如果您执行**arr
,那么您实际上将获得1 st 元素内的值,即11
。
我希望它能解决一些疑问: - )
编辑1:
由于我的参与者,同事们看来,某处似乎有点混乱,尽管我已经详细解释了什么是什么意思。但只是为了证明这一说法:
现在这里__a等于&amp; a__,但对于指针(__ptr不等于&amp; ptr__),情况也是如此。
如前所述,答案中a
和&a
的类型将有所不同。如果一个人执行指针算术,人们就能知道。尝试执行a + 1
和&a + 1
,他们对指针算术的反应肯定会给出一个好主意。
考虑一维数组:
int arr[] = {11, 19, 5, 9};
---------------------
| 11 | 19 | 5 | 9 |
---------------------
0 4 8 12 16
我们不能a++
,但对于指针:
int i = 4;
int *ptr = &i;
我们可以执行ptr++
,这会使ptr
指向下一个内存位置。
答案 4 :(得分:-2)
我认为结果意味着数组实际上并不是指针,而是在某些上下文中转换为指针,这是指向指针,就像传递给期望指针参数的函数一样。
请参阅此代码:
void test(int* num) {
printf("test\n");
printf("%p\n",num);
printf("%p\n",&num);
printf("%p\n",&num[0]);
}
int main(){
int num[2]={20,30};
test(num);
printf("main\n");
printf("%p\n",num);
printf("%p\n",&num);
printf("%p\n",&num[0]);
//other();
return 0;
}
输出结果为:
test
0x7fff7a422300
0x7fff7a4222e8 //LOOK THIS! Is diferent from main!
0x7fff7a422300
main
0x7fff7a422300
0x7fff7a422300
0x7fff7a422300