数组名
数组名是一个地址常量,不允许赋值。它表示数组首元素的地址。
指针操作数组元素
指针访问数组
指针类型变量\常量+1
等同于指针保存的内存地址+sizeof(指针指向的数据类型)
。
2个相同类型的指针相减,得到的结果是2个指针的偏移量。其中偏移单位(也叫步长)为sizeof(指针指向的数据类型)
。
#include<stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int* p = arr;
int length = sizeof(arr) / sizeof(int);
for (size_t i = 0; i < length; i++)
{
printf("arr[%d]=%d, p[%d]=%d, *(arr+%d)=%d, *(p+%d)=%d\n",
i, arr[i], i, p[i], i, *(arr+i), i, *(p+i));
}
int* q = arr;
for (size_t i = 0; i < length; i++)
{
printf("第%d项=%d\n", i, *q);
q++;
}
/*
下面计算得出,q与arr的偏移量为10(个步长)
*/
int step = q - arr;
printf("step=%d\n", step);
return 0;
}
运行上面的代码,结果如下:
PS:指针操作数组时推荐使用p[i]
,因为p[i]
比*(p+i)
简单明了。
[i]
相当于偏移+取值,p[i]
就表示以p
保存的地址为起点,偏移i个步长并且取值。
数组名与指针的区别
1、数组名是常量,指针是变量;
2、sizeof
的结果不同;
#include<stdio.h>
int main() {
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* p = arr;
printf("sizeof(arr) = %u\n", sizeof(arr));
printf("sizeof(p) = %u\n", sizeof(p));
return 0;
}
执行上面代码,结果如下:
数组名退化
数组作为函数参数时,数组名退化为指针变量,丢失数组长度。
#include<stdio.h>
/*
printArrayLen(int arr[])等价于printArrayLen(int* arr)
*/
void printArrayLen(int arr[]) {
int len = sizeof(arr) / sizeof(arr[0]);
printf("参数数组的长度:%d\n", len);
/*
* 数组作为函数参数时,数组名退化为指针,
* 所以此处arr相当于指针变量。
*/
printf("sizeof(arr) = %d\n", sizeof(arr));
}
int main() {
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printArrayLen(a);
return 0;
}
运行上面代码,结果如下:
指针加减运算
指针自增或自减的步长只和指针指向的数据类型有关,与实际赋值给指针变量的数据类型无关。
#include<stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
//给p和p2赋上相同的值
int* p = &arr[9];
char* p2 = &arr[9];
printf("p保存的地址:%p\n", p);
printf("p2保存的地址:%p\n", p2);
p--;
p--;
p--;
p2--;
p2--;
p2--;
//p自减的步长为4字节,p2自减的步长为1字节
printf("\"p--\"3次后, p保存的地址:%p\n", p);
printf("\"p2--\"3次后, p2保存的地址:%p\n", p2);
}
运行上面代码,结果如下:
指针的运算符,仅支持“指针与整数的加减运算”、“同类型指针相减”、“两指针比大小”、“逻辑运算符”,其余指针运算都是没有意义的。
指针数组
指针数组是一个数组,它的每一个元素都是指针类型。
#include<stdio.h>
void main() {
int a = 10;
int b = 20;
int c = 30;
int* arr[3] = {&a, &b, &c};
printf("a = %d\n", *arr[0]);
return 0;
}
运行上面代码,结果如下:
指针数组相当于一个二维数组。
#include<stdio.h>
void main() {
int a[] = { 1, 2, 3 };
int b[] = { 4, 5, 6 };
int c[] = { 7, 8, 9 };
int* d[] = {a, b, c};
for (int i = 0; i < sizeof(d) / sizeof(d[0]); i++) {
printf("%d %d %d\n", *d[i], *(d[i]+1), *(d[i]+2));
}
printf("=====\n");
for (int i = 0; i < sizeof(d) / sizeof(d[0]); i++) {
printf("%d %d %d\n", d[i][0], d[i][1], d[i][2]);
}
printf("=====\n");
for (int i = 0; i < sizeof(d) / sizeof(d[0]); i++) {
printf("%d %d %d\n", (*(d+i))[0], (*(d + i))[1], (*(d + i))[2]);
}
}
运行上面代码,结果如下:
多级指针
#include<stdio.h>
int main() {
int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int c[] = {7, 8, 9};
int* arr[] = {a, b, c};
/*
p保存的是&(int*),也就是int*型变量的地址,而不是int*型变量的值
arr是数组首元素a的地址,而a类型是int*,所以arr是int*的地址。
所以可以将arr赋值给p。
从而指针数组和二级指针建立关系。
二级指针指向指针数组的首元素。
*/
int** p = arr;
printf("p最终指向的整形为:%d\n", **p);
printf("*p = %p\n", *p);
printf("数组a的首地址=%p\n", a);
printf(" *(p+1) = %p\n", *(p+1));
printf("数组b的首地址=%p\n", b);
printf(" *(p+2) = %p\n", *(p + 2));
printf("数组c的首地址=%p\n", c);
printf("====遍历指针数组====\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//下面三行代码等价
printf("%d ", p[i][j]);
//printf("%d ", *(p[i]+j));
//printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
return 0;
}
运行上面代码,结果如下
#include<stdio.h>
int main() {
int a = 10;
int b = 20;
int* p = &a;
int** pp = &p;
//下面2行代码输出“*p = 20”
//*pp = &b;
//printf("*p = %d\n", *p);
//下面3行代码输出
//*p = 30
//a = 30
**pp = 30;
printf("*p = %d\n", *p);
printf("a = %d\n", a);
return 0;
}
int main() {
int a = 10;
int* p = &a;
int** pp = &p;
int*** ppp = &pp;
//下列等式成立
//*ppp == pp == &p
//**ppp == *pp == p == &a
//***ppp == **pp == *p == a
}