什么是指针?
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中的另一个地方的值。由于通过地址能找到所需的变量单位,可以说,地址指向该变量单元。因此,降低至形象化的称为“指针”。意思是通过它能找到以它位地址的内存单元。
图解指针:
代码解析
#include <stdio.h>int main()
{int a = 20;
//在内存中开辟一段空间int *p = &a;
//“&a”:将变量a的地址取出,使用“&”取地址操作符
//将a的地址存放在p变量中,p就是一个指针变量return 0;
}
总结
指针就是变量,用来存放地址的变量。所以说,存放在指针中的值都会被当做地址来处理
为什么存在指针?
生活中例子
现实生活中我们管理土地的方式是:
国家→省→市→县(区)→街道→楼→房间
这样做我们可以很好地将土地分区管理,然后通过一个地址就能找到对应的位置。可以方便有快捷的管理。
其实计算机中的内存管理也是采用了这样的思路:
将计算机中的内存分成很多小单元,每个单元都对应一个独一无二的地址,这样就一个地址表示一块空间。
指针的运算
指针±整数
#include <stdio.h>int main()
{int a = 10;char *pc = (char*)&n;int *pi = &n;printf("%p\n", &n); //0x00FFF121printf("%p\n", pc); //0x00FFF121printf("%p\n", pc+1); //0x00FFF122printf("%p\n", pi); //0x00FFF121printf("%p\n", pi+1); //0x00FFF125return 0;
}
指针-指针
用模拟实现strlen解析
int my_strlen(char *s)
assert(s != NULL);
{char *p = s;while(*p = '\0')p++;return p-s;
}
总结
指针的类型决定了指针向前或者向后走一步有多大,也就是说指针的类型决定了在对指针进行使用时的权限有多大(能操作几个字节)
指针之间相加减是在计算两个指针之间相差多少元素,并且只能在同一块空间中进行。
在关系运算中,有以下标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
二级指针
指针变量也是变量,是变量就有地址,那么指针变量的地址存放在哪里?
这就是二级指针!!!!
图解二级指针
a的地址存放在pa中,pa的地址存放在ppa中。所以说pa是一级指针,而ppa是二级指针。通过对ppa中的地址解应用可以找到pa,所以*ppa访问的就是pa,再对pa进行解引用就可以找到a,也就是说:**ppa就是a
指针表达式解析
当有代码
char ch = 'a';
char *cp = &ch;
下面的代码那些可以做左值?那些可以做右值?
&ch; //&ch是地址常量,不能做左值,只能做右值
cp; //cp是变量,可以做左值,也可以做右值
&cp; //&cp是地址常量,不能做左值,只能做右值
*cp+1; //*cp+1表达式的结果是常量,不能做左值,但可以做右值
*(cp+1); //*(cp+1)表示的是ch之后的一块空间,可以做左值,也可以做右值
++cp; //++cp在c语言中变量的前置++不能作为左值,只可以作为右值,但在c++中变量的前置++可以作为左值
cp++; //cp++在c语言中变量的后置++不能作为左值,只可以作为右值
*++cp; //*++cp表示的是ch的下一块空间,可以做左值,也可以做右值
*cp++; //*cp++中++是后置的,是先用*cp在做++,所以既可以做左值,也可以做右值
++*cp; //++*cp表达式是对*cp的前置++,所以不能做左值,只能做右值
(*cp)++; //(*cp)++表达式是对*cp的后置++,所以不能做左值,只能做右值
++*++cp; //++*++cp是对ch的下一块空间的内容做的前置++,所以不能做左值,只能做右值
++*cp++; //++*cp++是对ch的内容做前置++,同时也是对cp进行后置++,所以不能做左值,只能做右值
左值:强调的是空间/位置
右值:强调的是内容/结果
指针数组与数组指针
指针数组
指针数组是一个存放指针的数组,例如:int arr[20],是一个类型为"int*"、名为“arr”、存放了20各元素的数组
数组指针
数组指针是一个指针,我们知道整形指针“int *point”是一个能够指向整形数据的指针,当然数组指针“int (*p)[10]”就是一个能够指向数组的指针
代码解析
int (*p)[10];
//1、p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整数的数组,所以平时一个指针,指向一个数组,叫做数组指针
//要注意的是,[]的优先级要高于*,所以必须要加上()来保证p先和*结合,否则p将先和[]结合成为“int* p[10] ”,这样就成了一个指针数组
数组指针的使用
先分析一下以下代码
int arr[10] = {0};
//arr: 表示数组首元素的地址
//&arr:表示数组的地址
//具体差异在哪里?
printf("%p\n", arr);//输出的是第一个元素的地址
printf("%p\n", arr+1);//输出的是第二个元素的地址
printf("%p\n", &arr+1);//输出的是整个之后的地址
从这里我们可以看出产生的结果是截然不同的,虽然数组的地址和首元素的地址值是相同的,但是意义不相同。既然出现了数组的地址,就需要存储数组地址的容器,这时我们会考虑到用数组指针来存放数组的地址,由数组指针的定义得知,使用数组指针来存放数组的地址是再合适不过了。
现在我们要将一个二维数组传参,我们应该怎么做?
void test()
{int arr[3][5] = {0};print(arr);//这里print函数的参数应该如何设计?
}
void print(int arr[3][5])
{}
//这样可以吗?当然可以!只是最原始的方式
void print(int arr[][5])//ok?
{}
//这样可以吗?当然可以!因为二维数组的行可以省略,列不可以省略
void print(int **arr)//ok?
{}
//这样可以吗?当然不可以!因为这是一个二级指针,二级指针存放的是一级指针的地址,而不是数组的地址
void print(int (*arr)[5])//ok?
{}
//这样可以吗?当然可以!这是一个典型的数组指针,用来存放数组的地址,所以可以作为print的参数
通过上面的认识,来看一下下面的代码
int arr[10];//数组
int *parr1[10];//指针数组
int (*parr2)[10];//数组指针
int (*parr3[10])[10];//存放数组指针的数组
//parr3是名字先和[10]结合,说明这是一个拥有10个元素的数组,
//对于数组来说,去掉名字和[],其他的就是这个数组中存放的元素的类型,由(*)[]可知,这个数组中存放的是数组指针