p + i 按元素跳格
int 数组 a = {3, 1, 4, 1, 5} 连续排在内存里,数组名 a 本身就代表首元素地址,所以 int *p = a 不用写 &。p + 1 地址实际加 4(sizeof(int)),落在下一个元素上——指针永远按元素跳,不按字节跳:char 指针 +1 加 1,double 指针 +1 加 8。
a[i] 的真身是 *(a + i)
编译器看到 a[i],内部就翻译成 *(a + i):先算首地址 + i × 元素大小,再解引用取值——这也是数组随机访问 O(1) 的底层原因。注意括号不能省:*p + 1 是「取 a[0] 再加 1」,*(p + 1) 才是下一格的值。指针遍历写 for (int *q = a; q < a + 5; q++),边界 a + 5 是界外第一格,只能比较不能解引用。
传数组 = 传首地址
调用 fill(a, 5) 时整个数组并没有被拷贝,递过去的只有首地址;形参写 int arr[] 只是好看,真身就是 int *arr。好处是传数组 O(1)、再大也不拷贝;代价有两条:一是函数里改 arr[0] 就是改 main 里的 a[0](同一格内存),二是长度信息丢了,必须把 n 单独传进去。