- 指针变量里面存储了一个地址,可以是常量、全局变量、局部变量、堆变量(new)的地址,还可以是函数(代码)的地址
空指针
空指针又称万能指针,可以指向任何带类型的指针(地址)
p=&x; x可以是基本数据类型,也可是对象/结构体类型。
p=&table;
p=&book;
int a = 3;
void *p1;
p1 = &a;//p1指向a的纯地址,但并不理解成int
int *pi = (int*)p1;
//强制类型转换,将p1理解成pi,语法逻辑都正确
float *pf = (float*)p1;
//强制类型转换,将p1理解成pf,语法正确,逻辑错误
eg.C语言库函数qsort,快速排序,可以对任意类型的数组数据进行排序
void qsort(void *base, int nelem, unigned int width, int(*pfCompare)(const void*, const void*))
//数组起始地址,数组元素的个数,每个元素的大小(字节为单位)
int函数名(const void *d1, const void *d2);
比较元素大小的指针函数,实参是程序员自定义的函数
虽然这个也没写具体原理,,,但是从此我知道了会用就行
int cmp1(const void *a, const void *b){
return *(unsigned int *)a - *(unsigned int *)b;//从小到大排序
}
int cmp2(const void *a, const void *b){
return *(unsigned int *)b - *(unsigned int *)a;
}
int cmp3(const void *a, const void *b){
const unsigned int *p1, *p2;
p1 = (const unsigned int*)a;//把void*转化成int*
p2 = (const unsigned int*)b;//把void*转化成int*
return (*p1%10)-(*p2%10);
}
返回指针值的函数
被调函数的返回结果是一个地址,函数的类型是指针类型
int *max(int *x, int *y){
int *pt;
if(*x > *y)
px = x;
else
pt = y;
return pt;
}
不要试图返回局部变量的地址。。。它被释放了
下面为错误示例:
int *max(int *x, int *y){
int *pt;
if(*x > *y)
px = x;
else
pt = y;
return &pt;
}
局部变量地址
int *max(int *x, int *y){
int *pt;
if(*x > *y)
px = x;
else
pt = &y;
return pt;
}
局部变量(形参)的地址
指针数组
每一个元素可以存放地址
int *p[4];
[]
比*
优先级高,所以等价于int *(p[4])
p[]
是数组,p[4]
存放四个元素- 每个元素
p[i]
是一个int*
,即整数数据的地址,或者一个int数组的首地址
直观上,每个指针都可以指向一段内存空间,可以理解为二维数组,说是任意维度更恰当。
void main(){
int a[12] = {1,2,3,...,11,12};
int *p[4];//指向四段内存
for(int i = 0; i < 4; i++)
p[i] = &a[i * 3];
cout << p[3][2] << endl;
}
p[3][2] = *(p[3] + 2) = *(*(p + 3) + 2);
- 指针数组与二维数组的比较
//要注意最后一个不是*(p+3+2)
而是*(*(p + 3) + 2)
- 常见应用场景:实现字符串数组,数组中的每个字符串长度不一
char *str[] = {"China", "Japan", "America"};
指向指针的指针
int i, *p;
p = &i;
int **pp;
pp = &p;
pp=&&i;//不能连续取地址,第一次取出来的那个地址其实是个值,无法据此取到第二个地址
pp = & i; //一看就不对,pp为只想指针的指针变量,其基类型是指向整型数据的指针变量,而非整形数据
- 经典用途:字符串数组(
好像不能直接赋值全部,只能分开单独赋值。能 )
其他说明:
- 指针变量可以有空值,不指向任何地址。(虽然有#define NULL 0)(p=NULL相当于p=0但不建议写p=0)
- 两指针可以相减,不可以相加。若要进行相减运算,则两指针必须指向同一数组,结果为相距的数组元素个数
- 指向同一数组的两个指针可以比较大小:p2>p1指p2在p1的后面
动态分配内存
静态开辟的内存(栈)会存在不足或者浪费。一i那次在程序中要根据实际需要动态开辟空间——
利用new运算符开辟空间,空间的地址必须用个指针保存下来,才不会丢失
ps1. 当内存中没有足够的内存给予分配时,new运算符返回空值NULL;
ps2. new出来的空间,不能在分配空间时进行初始化,且除非重新开机,要手动用delete收回(二者配套,delete无其他使用)
int *p;
p = new int;
p = new int(6);//赋初值6
p = new int[6];//申请一个数组,首地址送给p
//开辟一个数组,每个元素都是一个int*
int **pp;
pp = new int*[100];//int*[rows]
for(int i = 0; i < 100; i++)
pp[i] = new int[5];
关于delete:
这一个示例是为了表示不要重复删除一块内存空间
int *q = new int(5);
p = q;
delete p;
cout << *q << endl;
这一个示例是为了避免野指针出现,保证删除后这个成为空指针
int *p = new int;
*p = 5;
if(p){
delete p;
p = NULL;
}
指针数组和指向指针的指针
int a[3][4];
连续存放3行4列整数,自动分配空间int(*p)[4];
连续存放未知(n)行4列整数的第一行,可以p=a;
,p = &a[i]
p是a上按行游走的指针
(int(*p)[4]
相当于指向有四个元素的一维数组,比如说a[1]
)int **a
等价于int *a[]
int *p[3];
3行整数,可以不连续,每行整数个数未知
int **p;
n行整数,可以不连续,每行整数个数未知
p = new int*[3];
p[0] = new int [4];
p[1] = new int [4];
以上都是合法的
下面非法:(因为连续性不同)
p = a;
a = p;
const指针
记得const要在定义同时必须赋值
-
const 修饰指针:1-常指针
int r = 6;
int * const pr = &r;
数据类型 * const 指针变量名
这样仅仅表明指向这个地址不变,不能再访问其他的内存单元了,但是仍然可以更改这个内存单元的值
用途:1. 数组名为常指针(禁写指针),int a[10],不能a++; 2.考试 -
const型指针2-指向常量的指针变量(不允许通过指针间接更改单元内容,但是可以直接更改)
const 数据类型 * 指针变量名
用途:日常常用 -
const型指针3-指向常量的常指针变量
const 数据类型 * const 指针变量名
定义后没不允许通过指针变量改变所指向的对象的值,也不允许改变指针变量的值,特少见
const char *str3; str3 = “qwq”;是正确的,因为“qwq”是一个常量,是不能修改的,必须要有const,这玩意儿不是拷贝的,是把变量地址送给指针。
char str[80] = {“qaq”};这才是拷贝的
char str[80]; str = “qwq”;是错的