指针数组和数组指针

目录

1 指针的理解

1.1 内存和地址

1.2 通俗理解指针

2 指针步长

3 数组指针和指针数组

3.1 指针引用数组

3.2 理解数组指针和指针数组

3.3 指针数组

3.4 数组指针


1 指针的理解

1.1 内存和地址

计算机按字节编址,每一个字节都有自己的地址,如图所示:每一个正方形都是一个字节

1.2 通俗理解指针

很多教材上,指针是“指向”某某某的。初学者最好不要用“指向”二字理解指针。

第一,“指针”是一个变量,用于保存一个地址,这个地址的数据类型在定义指针变量时确定。

第二,指针变量可以赋值。是将其它变量的地址赋值给指针,或者其他指针的值(也是地址)赋值给指针。

例子:

int a = 123; //定义一个变量a,用于保存一个int类型。

int * b; //定义一个指针变量b,用于保存一个地址,这个地址所保存的数据应该是int类型。

b=&a; //变量a的地址赋值给b。“&”操作是取变量的地址。

int * c; //定义一个指针c

c=b; //将b的值赋值给c,b指针的值是a的地址,因此c指针的值是a的地址。

上述代码的图解如下:

可以看到:

  1. 指针保存了变量a的地址,而由于指针b和指针c是变量,所以也需要内存来保存指针b和指针c,所以指针b和指针c本身也有地址
  2. 由于指针b和指针c保存了变量a的地址,就相当于指向了变量a,这也是教材“指向”的含义
  3. 仍然建议不要理解为“指向”,指针的本质是变量,存储的是地址

更深层次理解指针,实际上指针变量存储的是首字节的地址,如图所示:

 

2 指针步长

 指针+1:逻辑上加1,实际上需要根据指针类型而定。char * 类型指针,步长是1个字节;int * 类型指针,步长是4个字节;double * 类型指针,步长是8个字节  

void test02()
{
	char c = 'a';
	char *p1 = &c;
	printf("p1 = %d\n", p1);
	// 指针可以和整数进行运算,运算结果就是另外一个地址
	printf("p1 = %d\n", p1 + 1);

	printf("--------------\n");

	int a = 10;
	int *p2 = &a;
	printf("p2 = %d\n", p2);
	printf("p2 = %d\n", p2 + 1);
}

结果如下:

int*指针跳了4个字节,char*指针跳了一个字节

实际上这种功能非常适用于对数组的操作,例子如下:

void test03()
{   
	int arr[] = { 1, 2, 3, 4 };  // 16个字节
	// &(arr[0])

	// p 指针指向数组的第一个元素
	int* p = &(arr[0]);
	printf("p = %d %d\n", *p, p);
	printf("p = %d %d\n", *(p+1), p+1);//跳到arr[1] 
	printf("p = %d %d\n", *(p + 2), p + 2);//跳到arr[2] 
	printf("p = %d %d\n", *(p + 3), p + 3); 跳到arr[3]  
}

3 数组指针和指针数组

3.1 指针引用数组

int a[5] = {1,2,3,4,5};
int *p;
p = &a[0]; //将a[0]地址赋值给指针p 

数组名是个指针常量,因此 p = a 也是把数组a的首地址赋值给指针p,因此也可以写成

int a[5] = {1,2,3,4,5};
int *p;
p = a; //将a[0]地址赋值给指针p 

通过指针步长,可以操作数组,例子如下:

void test03()
{   
	int arr[] = { 1, 2, 3, 4 };  // 16个字节
	// &(arr[0])

	// p 指针指向数组的第一个元素
	int* p = &(arr[0]);
	printf("p = %d %d\n", *p, p);
	printf("p = %d %d\n", *(p+1), p+1);//跳到arr[1] 
	printf("p = %d %d\n", *(p + 2), p + 2);//跳到arr[2] 
	printf("p = %d %d\n", *(p + 3), p + 3); 跳到arr[3]  
	for (int i = 0;i < 4;i++) printf("%d",*(p+i));//遍历数组
}

3.2 理解数组指针和指针数组

先明确运算符优先顺序:()>[]>*(如下图所示)

int (*p)[n]:根据优先级,先看括号内,可知p是一个指针,这个指针指向一个一维数组,数组长度为n。称为“数组的指针”,即数组指针;

int *p[n]:根据优先级,先看[],则p是一个数组,该数组是一个int *型,因此数组的元素是指针类型,共n个元素。称为“指针的数组”,即指针数组。

例如:

int *p[5];
int (*q)[5];

对于语句“int*p[5]”,它是一个指针数组,该数组包含 5 个int*型的指针,如图所示

对于语句“int(*p2)[5]”,它是一个数组指针,它指向数组的首地址,数组在这里并没有名字,是个匿名数组,如图所示

运算符优先级如图所示:

3.3 指针数组

指针数组:装着指针的数组。

引用别人的例子:

int main()
{
    int x = 1;
    int y = 2;
    int *p[2];
    p[0] = &x;
    p[1] = &y;

    printf("%p\n", p[0]); // x 的地址
    printf("%p\n", &x);    // x 的地址
    printf("%p\n", p[1]); // y 的地址
    printf("%p\n", &y);    // y 的地址
    printf("%d\n", *p[0]); // p[0] 表示 x 的地址,则 *p[0] 表示 x 的值
    printf("%d\n", *p[1]); // p[1] 表示 y 的地址,则 *p[1] 表示 y 的值

    return 0;
}

同时,指针数组还可以操作二维数组,如下:

int *p[3]; // 一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值
int arr[3][3];
for (int i = 0; i < 3; i++)
    p[i] = arr[i];

通过图形理解,如图所示:

3.4 数组指针

数组指针:指向数组的指针。

引用别人的一个例子:

int main()
{
	//一维数组
	int a[5] = { 1, 2, 3, 4, 5 };
	//数组里有5个元素
	int (*p)[5];
	//把数组a的地址赋给p,则p为数组a的地址,则*p表示数组a本身
	p = &a;
 
	//%p输出地址, %d输出十进制
	printf("%p\n", a); //输出数组首元素地址
	printf("%p\n", p); //根据上面,p为数组a的地址,输出数组a的地址
	printf("%p\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组
	printf("%p\n", &a[0]); //a[0]的地址
	printf("%p\n", &a[1]); //a[1]的地址
	printf("%p\n", p[0]); //数组首元素的地址
	printf("%d\n", **p); //*p为数组a本身,即为数组a首元素地址,则*(*p)为值,**p表示首元素的值1
	printf("%d\n", *p[0]); //根据优先级,p[0] 表示首元素地址,则*p[0]表示首元素本身,即首元素的值1
	printf("%d\n", *p[1]); //这是错误的,没有意义
    return 0;
}

问题一:为什么p = &a而不是p=a

a 和 &a 的数据类型不同,前者是指向数组首元素的指针,后者是指向数组的指针。但它们的值都是首元素地址,区别在于数据类型不同,而p是数组指针,因此是p=&a。

问题二:*p[0]有意义,而*p[1]没有意义

把数组指针理解成一个装着数组指针的数组,如图所示:

该数组只有一个元素即p[0],它指向了数组a。由于没有第二个元素,所以p[1]没意义

同时,数组指针还可以操作二维数组,如下:

int arr[3][4];
int (*p)[4]; // 定义一个数组指针,指向含有4个元素的一维数组
p = arr; // 将该二维数组的首地址赋给 p,也就是 arr[0] 或 &arr[0],二维数组中 p = arr 和 p = &arr[0] 是等价的
p++; // p = p + 1,该语句执行过后 p 的指向从行 arr[0][] 变为了行 arr[1][]

问题一:为什么p=arr而不是p=&arr

arr是一个二维数组,如果将arr的每一行抽象成一个元素,本质上arr就可以抽象成一个一维数组,所以arr代表首元素-即第一行。简单来说,arr代表首行的地址

  • 25
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值