指针算术

时间:2008-12-27 07:10:39

标签: c pointers pointer-arithmetic

有没有人有指针算术的好文章或解释(博客,例子)?图中的观众是一群学习C和C ++的Java程序员。

7 个答案:

答案 0 :(得分:56)

这是我学习指针的地方:http://www.cplusplus.com/doc/tutorial/pointers.html

一旦理解了指针,指针算法就很容易了。它与常规算术之间的唯一区别是,您添加到指针的数字将乘以指针所指向的类型的大小。例如,如果指针指向intint的大小为4个字节,则(pointer_to_int + 4)将计算为前面16个字节(4个字节)的内存地址。

所以当你写

(a_pointer + a_number)

在指针算术中,真正发生的是

(a_pointer + (a_number * sizeof(*a_pointer)))

在常规算术中。

答案 1 :(得分:51)

首先,binky视频可能有所帮助。这是关于指针的好视频。对于算术,这是一个例子:

int * pa = NULL;
int * pb = NULL;
pa += 1; // pa++. behind the scenes, add sizeof(int) bytes
assert((pa - pb) == 1);

print_out(pa); // possibly outputs 0x4
print_out(pb); // possibly outputs 0x0 (if NULL is actually bit-wise 0x0)

(注意,递增一个严格包含空指针值的指针是未定义的行为。我们使用NULL因为我们只对指针的值感兴趣。通常,只指向元素时使用递增/递减一个数组)。

以下显示了两个重要概念

  • 向指针添加/减去整数意味着将指针向前/向后移动N个元素。因此,如果int是4字节大,则在增加1之后,pa可以在我们的平台上包含0x4。
  • 通过另一个指针减去指针意味着通过元素测量它们的距离。因此从pa中减去pb将产生1,因为它们具有一个元素距离。

一个实际的例子。假设你编写了一个函数,人们为你提供了一个开始和结束指针(在C ++中很常见):

void mutate_them(int *begin, int *end) {
    // get the amount of elements
    ptrdiff_t n = end - begin;
    // allocate space for n elements to do something...
    // then iterate. increment begin until it hits end
    while(begin != end) {
        // do something
        begin++;
    }
}

ptrdiff_t是什么类型的(结束 - 开始)。对于某些编译器,它可能是“int”的同义词,但对于另一个编译器,它可能是另一种类型。人们不知道,所以选择泛型typedef ptrdiff_t

答案 2 :(得分:6)

应用NLP,称之为地址算术。人们担心和误解“指针”主要是因为他们是由错误的人和/或在错误的阶段以错误的方式用错误的例子教导的。难怪没有人“得到”它。

当教学指针时,教师继续讲述“p是指向a的指针,p的值是a的地址”,依此类推。它不会工作。这是您构建的原材料。用它来练习,你的学生就能得到它。

'int a',a是一个整数,它存储整数类型的值。 'int * p',p是'int star',它存储'int star'类型值。

'a'是你如何得到'什么'整数存储在一个(尽量不使用'a'的值') '& a'是你如何得到自己存储的'地方'(试着说'地址')

'b = a'为此起作用,双方必须属于同一类型。如果a是int,则b必须能够存储int。 (所以______ b,空白填充'int')

'p =& a'为此起作用,双方必须属于同一类型。如果a是整数,& a是地址,则p必须能够存储整数的地址。 (所以______ p,空白填充'int *')

现在以不同方式编写int * p以显示类型信息:

int * | P

什么是'p'? ans:它是'int *'。所以'p'是整数的地址。

int | * P

什么是'* p'? ans:它是'int'。所以'* p'是一个整数。

现在进行地址算术:

int a; 一个= 1; A = A + 1;

我们在'a = a + 1'中做了什么?把它想象成“下一个”。因为a是一个数字,这就像说“下一个数字”。由于持有1,说“下一步”将使其成为2。

//谬误的例子。你被警告了!!! int * p int a; p =& a; P = P + 1;

我们在'p = p + 1'做什么?它还在说'下一个'。这次,p不是数字而是地址。所以我们所说的是“下一个地址”。下一个地址取决于数据类型,更具体地说取决于数据类型的大小。

printf(“%d%d%d”,sizeof(char),sizeof(int),sizeof(float));

所以地址的'next'将向前移动sizeof(数据类型)。

这对我和我曾经教过的所有人都有用。

答案 3 :(得分:3)

我认为指针算法的一个很好的例子是以下字符串长度函数:

int length(char *s)
{
   char *str = s;
   while(*str++);
   return str - s;
}

答案 4 :(得分:1)

因此,要记住的关键是指针只是一个字型大小的变量,用于解除引用。这意味着无论它是一个void *,int *,long long **,它仍然只是一个字大小的变量。这些类型之间的区别是编译器认为解除引用的类型。只是为了澄清,字大小意味着虚拟地址的宽度。如果您不知道这意味着什么,请记住在64位机器上,指针是8个字节,而在32位机器上,指针是4个字节。理解指针时,地址的概念非常重要。地址是能够唯一地识别存储器中的特定位置的数字。记忆中的一切都有一个地址。出于我们的目的,我们可以说每个变量都有一个地址。这不一定总是正确的,但编译器让我们假设这一点。地址本身是字节粒度,意味着0x0000000指定内存的开头,0x00000001是内存中的一个字节。这意味着通过向指针添加一个,我们将一个字节向前移动到内存中。现在,让我们采取数组。如果你创建一个类型为quux的数组,其大小为32个元素,它将从它的分配开始到它的分配开始加上32 * sizeof(quux),因为数组的每个单元都是sizeof(quux)big。所以,实际上当我们用array [n]指定一个数组元素时,这只是*(array + sizeof(quux)* n)的语法糖(简写)。指针算法实际上只是改变了你所指的地址,这就是我们可以用

实现strlen的原因
while(*n++ != '\0'){
  len++;
}

因为我们只是逐字节扫描,直到我们达到零。希望有所帮助!

答案 5 :(得分:0)

有几种方法可以解决它。

大多数C / C ++程序员都想到的直观方法是指针是内存地址。 litb的例子采用了这种方法。如果你有一个空指针(在大多数机器上对应于地址0),并且你添加一个int的大小,你得到地址4.这意味着指针基本上只是花哨的整数。

不幸的是,这有一些问题。首先,它可能无法正常工作。 空指针不能保证实际使用地址0.(尽管将常量0赋给指针会产生空指针)。

此外,不允许增加空指针,或者更一般地说,指针必须始终指向已分配的内存(或过去的一个元素),或特殊的空指针常量0。

因此,更正确的思考方式是指针只是迭代器,允许您迭代已分配的内存。 这实际上是STL迭代器背后的关键思想之一。它们被建模为非常像指针,并提供修补原始指针以作为正确迭代器的特化。

例如,here给出了更详细的解释。

但后一种观点意味着你应该真正解释STL迭代器,然后简单地说指针是这些的一个特例。您可以将指针递增以指向缓冲区中的下一个元素,就像std::vector<int>::iterator一样。它可以将一个元素指向数组末尾,就像任何其他容器中的结束迭代器一样。您可以减去指向同一缓冲区的两个指针以获取它们之间的元素数量,就像使用迭代器一样,就像使用迭代器一样,如果指针指向单独的缓冲区,则可以有意义地比较它们。 (有关其原因的实际示例,请考虑分段存储空间中发生的情况。指向不同段的两个指针之间的距离是多少?)

当然,在实践中,CPU地址和C / C ++指针之间存在非常密切的关联。但他们并非完全同样的事情。指针有一些限制,可能在您的CPU上不是必需的。

当然,大多数C ++程序员在第一次理解时都会混淆,即使它在技术上是不正确的。它通常足够接近你的代码如何最终表现出人们认为他们得到它,并继续前进。

但是对于那些来自Java的人,只是从头开始学习指针,后一种解释可能同样容易理解,并且稍后会减少对它们的惊喜。

答案 6 :(得分:0)

这是关于指针算术的链接here非常好的

例如:

指针和数组

用于计算ptr + i的地址的公式,其中ptr具有类型T *。那么地址的公式是:

addr(ptr + i)= addr(ptr)+ [sizeof(T)* i]

对于32位平台上的int类型,addr(ptr + i)= addr(ptr)+ 4 * i;

<强>减法

我们还可以计算ptr - i。例如,假设我们有一个名为arr的int数组。     int arr [10];     int * p1,* p2;

p1 = arr + 3 ; // p1 == & arr[ 3 ] 
p2 = p1 - 2 ; // p1 == & arr[ 1 ]