指针算术

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

首先, binkyvideo可能会有帮助。 这是一个关于指针的好video。 对于算术,这里是一个例子:

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是什么types的(结束 – 开始)。 它可能是某些编译器的“int”的同义词,但可能是另一种types的另一种types。 人们不知道,所以selectgenericstypedef ptrdiff_t

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

一旦你理解了指针,指针运算就很容易。 它和常规算术的唯一区别就是你添加到指针的数字将乘以指针指向的types的大小。 例如,如果你有一个指向int的指针,并且int的大小是4个字节, (pointer_to_int + 4)将会提前计算出一个16字节(4 ints)的内存地址。

所以,当你写

 (a_pointer + a_number) 

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

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

在常规算术中。

应用NLP,称之为地址算术。 “指针”恐怕被误解,主要是因为错误的人和/或错误的错误的例子来错误地教导他们。 难怪没有人“得到”它。

当教学指针时,教师继续讲“p是指向a的值,p的值是a的地址”等等。 它只是不会工作。 这里是你build立的原料。 用它练习,你的学生将会得到它。

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

'a'是你如何得到存储在'什么'的整数(尽量不要用'a'的值)&a'是你如何得到'它'存储在哪里'(试着说'地址')

'b = a'为这个工作,双方必须是相同的types。 如果a是int,则b必须能够存储int。 (所以______ b,空白填充'int')

'p =&a'这个工作,双方必须是相同的types。 如果a是一个整数,&a是一个地址,p必须能够存储整数的地址。 (所以______ p,空白填充'int *')

现在写不同的int * p来显示types信息:

int * | p

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

int | * p

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

现在到地址算术:

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

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

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

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

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

所以下一个地址会向前移动sizeof(数据types)。

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

我认为下面的string长度函数是一个很好的指针算术例子:

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

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

 while(*n++ != '\0'){ len++; } 

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

有几种方法来解决它。

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

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

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

因此,更正确的思考方式是指针只是迭代器,允许您遍历分配的内存。 这实际上是STL迭代器背后的关键思想之一。 他们的行为非常像指针,并且提供了专门的技术来修补原始的指针,作为合适的迭代器工作。

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

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

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

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

但是对于来自Java的人来说,只是从头开始学习指针,后面的解释可能就像被简单地理解一样,并且稍后会减less对它们的意外。

这里有一个相当不错的关于指针算术的链接

例如:

指针和数组

计算ptr + i地址的公式,其中ptr的types为T *。 那么地址的公式是:

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

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

减法

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

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