*号
定义时
*号在定义时并不是取值的意思
int* p对于初学者来说较难为理解。尤其是“ * ”符号。我们用定义整形变量类比
int p = a; //定义一个整型变量,类型为int,里面存放的是a的值
cout << p; //输出a的值
int* p = &a; //定义一个整型指针变量,类型为int* ,里面存放的是a的地址,定义时,*其实是和int放一块
cout << p; // 输出a的地址
取值时
*号如果不在定义变量时使用就是取值符,取出一个地址中的值
int a = 5;
int* p = &a;
cout << *p; //输出5
我们也可以通过这个符号直接修改该地址上的值
int a = 3;
int* p = &a;
*p = 5;
cout << a; //输出5 因为a所在地址的值被修改
指向变量的指针
这种指针是最基本的指针
int a = 3; //定义整形变量a
int* p = &a; //定义整形指针p存放a的地址
printf("a的值是%d\n" , a);
printf("a的值是%d\n" , *p); //* 取值运算符 取出p地址里的值
printf("a的地址是%p\n" , &a);
printf("a的地址是%p\n" , p);
指向一维数组的指针
定义
变量申请内存是线性连续的
我们可以申请一个指针指向数组的第一个元素
int a[] = {1 , 2 , 3 , 4 , 5};
int* p = &a[0] //a[0]是数组的第一个元素,把他的地址赋给了指针
数组名称是首元素的地址
所以这样定义是一样的
int* p = a;
指针的移动
指针可以通过计算实现在内存上的移动
int a[] = {1 , 2 , 3};
int* p = a;
printf("%p" , p); //008FFB14
printf("%d" , *p); //1
p = p + 1; //指针向下移动一格
printf("%p" , p); //008FFB18
printf("%d" , *p); //2
我们看到,指针加1,但在内存上移动了四个字节。
因为这是int型指针,其实就是移动到了数组的下一个数(int类型四个字节)
指向二维数组的指针
二维数组的指针怎么定义呢
假设我们定义一个3 * 4的数组a
int a[3][4]
二位数组的内存分配也是线性的
他的内存的分配是这样的
这里我们可以将a数组看成一个一维数组
普通一维数组a[3] 每一个数组存放着一个int类型的值
这里的一维数组a[3] 每一个数组存放着一个int[4]类型的值
这样说有点晦涩难懂
我们对一位数组定义指针时是这样的
int a[] = {1 , 2 , 3};
int *p = a; //p指向a[0] 是一个int类型
同理二维数组
int a[][3] = {{1 , 2 , 3} , {4 , 5 , 6}};
int (*p)[3] = a; //p指向a[0],是int[3]类型
//简单来说就是p指向一个长度为3个int , 刚才定义一维数组指针长度为一个int
注意的地方
int(*p)[3] 为什么要加括号
因为[]优先级与*一样,但是结合律从右向左,如果写成int *p[3]
就成了一个长度为3的指针数组,也就是一个长度为三的数组其中每个数都是指针。
定义二维数组时,列数必须确定。
二维指针的取值
我们都知道,数组名是数组首元素的地址
所以
a = &a[0]
a[0] = &a[0][0]
这两个都是正确的
我们可以通过两次取值获得a[0][0]
int a[3][3] = {0};
int (*p)[3] = a;
printf("%d" , **p); //两层取值
p = a
所以p中存放的是a[0]
的地址,对他第一次取值*p
就是a[0][0]
的地址,再取一次才是a[0][0]
的值
实现二维指针的移动
因为定义二维指针时,指针一指就是一行
所以
int a[3][3] = {0};
int (*p)[3] = a;
p = p + 1;
这样的操作移动三个int 相当于直接移动到了下一行,本来指向a[0] , 现在指向了a[1]
我们可以通过取一次地址获得a[1][0]的地址
int a[3][3] = {0};
int (*p)[3] = a;
int *p1 = *p;//p1指向a[0][0],因为p取了一次地址
通过这些例子加深一下
int a[3][3] = {{1 , 2 , 3} , {4 , 5 , 6} , {7 , 8 , 9}};
int (*p)[3] = a;
**(p + 1) // p先移到a[1]再取出a[1][0]的地址 再取出a[1][0]的地址 输出4
*(*p + 1) // p先取值,去除a[0][0]的地址,再移动到a[0][1] ,再取出a[0][1]得值,是2
*(*(p + 1) + 2) //p先移到a[1]在取出a[1][0]的地址,再移动到a[1][2],取出a[1][2]的值,是6
指向结构体的指针
我们定义一个结构体
struct Node
{
int f , t , v;
};
然后我们可以用结构体来定义变量,每个变量有f,t,v三个成员
例如
struct Node f; //定义单个变量
struct Node e[23]//定义结构体数组
我们可以通过以下方式访问成员
f.v
e[i].t
定义指针
我们类比int指针
int a , b[23];
int *p1 = &a
int *p2 = b;
结构体指针
struct Node
{
int f , t , v;
};
struct Node a , b[23];
struct Node *p1 = &a;
struct Node *p2 = b;
访问
对接刚才的代码,还是类比int型指针
*p1//访问a的值
*p2//访问b[0]的值
(*p1).f //访问a的成员f
(*p2).f //访问e[0]的成员f
*(p2 + 1).t //访问e[1]的成员t
结构体访问成员还有更简单的方法
使用成员运算符->
与上面代码对比
p1 -> f //访问a的成员f
p2 -> f //访问e[0]的成员f
(p2 + 1) -> t //访问e[1]的成员t
结构体指针和数组指针类似,只是结构体多了成员,访问时加上成员运算符就好了
指针与字符串
我们都学过,字符串是存在字符数组里的
char a[] = "fuck you";
char * ptr = a; //指向a[0];
字符串也是一个地址
我们还可以直接这样做
char *ptr = "deam";