我认为我理解了指针语法,但是有一些小事件让我失望,例如:
int *ptr;
int array[4] = {1,2,3,4};
int *arrayEnd = array + n;
for (ptr = array; ptr < arrayEnd; ++ptr)
sum += *ptr;
return sum;
在定义ptr = array
循环时,我们怎么做*ptr = array
而不是for
?
我也不明白为什么当函数的参数是指针时,我们不使用星号。
我在书中看到了function(ptr);
,但为什么不这样:function(*ptr);
。
答案 0 :(得分:7)
让我们从代码的第一行开始:
int *ptr;
如果您已宣布
int a;
这是什么意思?这意味着a
是int
。同样,由于您已声明int *ptr;
,这意味着*ptr
是int
。
如果*ptr
是int
,那么ptr
是什么? ptr
(没有星号)是指向 int
的指针。 指针是一个保存值地址的变量。在这种情况下,指针是指向int
的指针,这意味着ptr
所持地址的值为int
。
然后你有int array[4] = {1,2,3,4};
。声明和定义数组时,数组标识符(数组的名称)会告诉您数组在内存中的位置。在任何表达式中使用时,数组的名称会衰减到数组的第一个元素的地址。
所以当你有
时ptr = array;
您要将数组的第一个元素的地址分配给指针ptr
。由于这是int
的数组,因此您将int
的地址分配给可以保存int
地址的变量。赋值的左侧和右侧是相同类型的值。
为什么ptr
而不是*ptr
?在这种情况下,*
是什么?它是一元解除引用/间接运算符。所以
*ptr = array;
意味着:取右侧的值,并使用此右侧值替换 ptr
所占地址的值。出于两个原因,这是错误的。
array
是一个地址。它不完全是整数值。它只是记忆中的一个位置。当然,该位置可以用整数表示,美国的位置可以用整数邮政编码表示,但就像你通常不关心邮政编码的数值是什么一样,你可能不会我真的关心给定内存地址的整数值是什么。ptr
未初始化。回到我们之前的例子:int a;
。 a
的价值是多少?我们还没有为它设置一个值,所以它只是垃圾。同样,ptr
还没有指向任何东西。 可以指向一个整数,但是我们还没有告诉它指向什么整数。 ptr
没有地址。我们不能说“用这个新值替换ptr
指向的当前值”,因为ptr
尚未指向任何值;我们还没有告诉它从哪里开始。 总结一下:通过编写ptr = array;
您正在做的事情是完全正确的 - 将int
的指针指定给pointer to an int
类型的变量。通过编写*ptr = array;
,您将为整数分配一个地址,这可能不是您的意思;其次,由于整数本身没有在内存中的任何地方分配,因此分配无处可去。
这就是为什么ptr = array;
是正确的,*ptr = array
会出错。
关于函数指针的问题是一个单独的问题,因此请询问有关func(ptr)
与func(*ptr)
的其他问题。
答案 1 :(得分:3)
您可以将传统的,基于索引的迭代转换为仅使用指针算法的迭代:
T array[N];
// traditional:
for (size_t i = 0; i != N; ++i)
{
foo(array[i]);
}
// using pointer arithmetic
for (T * p = array; p != array + N; ++p)
{
foo(*p);
}
指针T * p = array
是通过将数组名称衰减到指向其第一个元素的指针获得的。这两种形式之间的联系是基本身份array[i]
== *(array + i)
。
答案 2 :(得分:1)
int *ptr, i;
int array[20];
ptr = &i; // make sure ptr points somewhere valid
ptr = array; // put the address of array[0] in the variable ptr
*ptr = array; // put the address of array[0] into the location ptr points to
ptr = array[0]; // put the integer in array[0] into the variable ptr
*ptr = array[0]; // put the integer in array[0] into the location ptr points to.
答案 3 :(得分:1)
让我们看看你建议的代码:
*ptr = array
请记住,*
是间接运算符。它返回指针指向的内存位置的对象。此外,在此上下文中,array
将转换为指针。因此,上面的行将指针(即内存地址)分配给对象ptr
指向。这只有在ptr
指向指针时才有效。但是,ptr
会指向int
。
另一方面,ptr = array
是正确的,因为指定ptr
指向数组中的第一个元素。
答案 4 :(得分:1)
假设你有类似下面的代码。
int x=10;
这里x是一个保存值为10的变量。变量x只是某个内存地址的标签。
x
+-------+
| 10 | 4050 which is x
+-------+
因此值10存储在4050中。它总是很难记住内存地址。因此,您可以通过命名并使用该名称来轻松实现。这里的x只是内存地址4050的标签。
int *x_ptr;
这里x_ptr是一个指针变量,可以存储任何整数变量的内存地址。
+-------+
| GBV | 5000 which is x_ptr
+-------+
这里x_ptr是地址5000的标签。最初该变量将包含垃圾值(GBV)。
x_ptr = &x;
以上语句在x_ptr中存储x的地址。
+-------+
| 4050 | 5000
+-------+
x_ptr = &x = 4050
printf("%u",x_ptr); // 4050
printf("%d", *x_ptr); // 10
*x_ptr = *(4050) = valueAt(4050) = 10
现在让我们讨论一下你的代码。
int *ptr;
+-------+
| GBV | 6000 which is ptr
+-------+
int array[4] = {1,2,3,4};
+-----------+
| 1 | 7824 which is array+0 or array[0]
+-----------+
| 2 | 7828 which is array+1 or array[1]
+-----------+
| 3 | 7832 which is array+2 or array[2]
+-----------+
| 4 | 7836 which is array+3 or array[3]
+-----------+
这里array = 7824
int *arrayEnd = array + n;
根据数据类型增加内存位置将增加字节数。
array+0 = 7824 + 0 = 7824
array+1 = 7824 + 1(4 bytes in case of integer) = 7824 + 4 = 7828
when n=4
arrayEnd = 7824 + 16 = 7840
+-------+
| 7840 | 6010 which is arrayEnd
+-------+
for (ptr = array; ptr < arrayEnd; ++ptr)
sum += *ptr;
这里ptr用数组的基地址初始化(7824)。所以7824将存储在内存6000中。
6000 6000
+-------+ +-------+
| GBV | => | 7824 | 6000 which is ptr
+-------+ +-------+
所以现在ptr = 7824
For Loop
+-------+--------------+-------------+
| ptr | *ptr | sum + =*ptr | // 7824 < 7840
+-------+--------------+-------------+
| 7824 | *(7824) = 1 | sum += 1 | // 7828 < 7840
+-------+--------------+-------------+
| 7828 | *(7828) = 2 | sum += 2 | // 7832 < 7840
+-------+--------------+-------------+
| 7832 | *(7832) = 3 | sum += 3 | // 7836 < 7840
+-------+--------------+-------------+
| 7836 | *(7836) = 4 | sum += 4 | // 7840 !< 7840 so loop terminates
+-------+--------------+-------------+
为什么ptr = array而不是* ptr = array?
valueAt(*) has to be used when you have to store some value in the memory address.
ptr= array; //initializing base address of array to ptr.
6000 6000
+-------+ +-------+
| GBV | => | 7824 | 6000 which is ptr
+-------+ +-------+
*ptr = array; // ptr already has a address pointed to, and you store array's base address inside that memory location.
6000
+-------+
| GBV | *ptr= *(GBV) = Unknown = array // No guarantee that GBV is a proper memory address
+-------+
如果ptr已经有一些正确的内存地址说1000,那就像
6000
+-------+
| 1000 | *ptr= *(1000) = valueAt(1000) = array
+-------+
which makes
1000
+-------+
| 7824 |
+-------+
多指针的简单示例。
#include<stdio.h>
int main()
{
int x=10;
int *y=&x;
int **z=&y;
printf("%d --> %d --> %d",x,*y,**z);
return 0;
}
OUTPUT:
10 --> 10 --> 10
答案 5 :(得分:0)
请注意,星号含义在定义上有所不同。当你写:
int *arrayEnd = array + n;
它实际上与:
相同int *arrayEnd;
arrayEnd = array + n;
在这两种情况下,都会初始化指针的地址,而不是指针引用的值
另请注意,在定义数组时,数组名称可以被视为数组的第一个内存位置,而括号中的数字是其偏移量,即接下来的两行是相同的:
ptr = array;
ptr = &array[0];
以及接下来的两行:
array[2] = 5;
*(array+2) = 5;
关于最后一个例子的有趣事实是它可以解释你有时可以写:
2[array]
代替array[2]
*(2+array)
与*(array+2)
相同
所以当你写:
int *ptr;
int array[4] = {1,2,3,4};
int *arrayEnd = array + n;
for (ptr = array; ptr < arrayEnd; ++ptr)
sum += *ptr;
return sum;
第三行将最后一个array
元素的内存地址放在一个名为arrayEnd
for
循环从第一个array
位置到最后一个array
位置
sum
添加数组元素