指针简介
- 指针式一个值为内存地址的变量
补充:地址和变量名的关系
在内存中地址是唯一的而变量名可能是重复的。变量名其实就是地址的一个标识,就像身份证号和我们的名字一样,身份证号可以唯一地标识一个人,而人名可能是重复的。
既然变量名是重复的,为什么还要用变量名不直接使用地址呢?
这是因为我们很难记住地址的大小及其存储的数据的含义,但是一方面变量名的见名知意可以是我们很快速的知道其中存储的数据的意义,另一方面它又对应着地址,可以方便地将数据存储进去。
指针的定义和赋值
- 定义:
数据类型* 指针名;
- 赋值:
eg:
int * p=&year;
&:是取地址符号。
int * p=&year;就表示变量p指向变量year;
注意不能直接把地址数字赋值给 p,因为地址数字就只是一个 int 型的数据,不是int * 型的,要用&取地址符赋值。
取地址符&
&用于获取变量的地址
eg:
int num =1024;
int* p;
//取num变量的地址赋值给ptr_num
p=#
间接运算符*
使用间接运算符通过地址找到变量值。
int num =1024;
int* p;
//取num变量的地址赋值给ptr_num
p=#
cout<<*p;
输出:
1024
虽然 *p 和 num 都可以获取变量的值,但是 num 的效率更高,因为它是直接访问。
使用
#include <iostream>
using namespace std;
int main()
{
double num=104.5;
double* p=#
cout<<"p的值:"<<p<<endl;
cout<<"p指向的值:"<<*p<<endl;
}
输出:
p的值:0x61fe10
p指向的值:104.5
char*问题
关于char*的使用这里有一个小问题
#include <iostream>
using namespace std;
int main()
{
char ch='a';
char* ptr_ch=&ch;
//输出a:
cout<<*ptr_ch<<endl;
cout<<ptr_ch<<endl;
}
按照我们之前的理解 cout<<ptr_ch<<endl;的输出应该是一个地址,但是实际输出确实下面这样的
a
a
这是因为在c中没有String,我们使用char*来定义字符串,在c++中也适用
如:
char* s="你真好看";
cout<<s<<endl;
输出:
你真好看
解决:
强制转换(void *)
#include <iostream>
using namespace std;
int main()
{
char ch='a';
char* ptr_ch=&ch;
//输出a:
cout<<*ptr_ch<<endl;
cout<<(void *)ptr_ch<<endl;
return 0;
}
(void *)的含义:代表任意类型的指针类型,说明是一个指针类型。
空指针NULL Pointer
- 空指针不指向任何对象,在试图使用一个指针之前可以首先检查是否为空
- 用法:
int* p=nullptr;
它等价于 int* p=0;
nullptr 和 C 中的 NULL 类似,NULL 也代表0。
如果·定义一个指针不赋值,那它依然会指向一个地址,但是我们不确定是那个,直接用的话就会出现各种问题。很危险。——野指针
void*指针类型
一种特殊的指针类型,可以存放任意对象的地址
eg:
#include <iostream>
using namespace std;
int main()
{
double num=3.14;
double * ptr_num1=#
void * ptr_num2=#
cout<<(ptr_num1==ptr_num2)<<endl;
return 0;
}
输出:
true
- void* 指针存放一个内存地址,地址指向的是什么类型不能确定
- void指针的作用:
和别的函数作比较、作为函数的输入输出、赋值给另一个void指针。
和别的函数作比较:如一个函数返回 void * 类型,可以通过和不同类型的指针做比较来确定是哪一个指针类型。
指针的一些特点
- 指针同样是一个变量,只不过该变量中存储的是另一个对象的内存地址
- 如果一个变量存储另一个对象的地址,则称该变量指向这个对象
- 指针变量可以赋值,指针的指向在程序执行中可以改变
- 指针可任何基本数据类型、数组和其他所有高级数据结构的地址
- 若指针已声明为指向某种类型数据的地址,则它不能用千存储其他类型数据的地址
指针和数组
数组:
- 存储在一块连续的内存空间中
- 数组名就是这块连续内存空间的首地址
- 注意:数组名存储的地址不能改变,即数组名不可以参与算术运算
使用指针访问数组:
#include <iostream>
using namespace std;
int main()
{
double score[] {11,12,34,23,43};
double *ptr_score=score;
for(int i=0; i<sizeof(score)/sizeof(double); i++)
cout<<ptr_score[i]<<" ";
cout<<endl;
return 0;
}
输出:
11 12 34 23 43
但是要注意的是这里的 ptr_score 只是一个指针,只占4个字节。
指针统一占 4 或 8 个字节,跟编译器的模式有关
- 在64位编译模式下,指针的占用内存大小是8字节
- 在32位编译模式下,指针占用内存大小是4字节
cout<<sizeof(score)<<"\t"<<sizeof(ptr_score)<<endl;
我的电脑是64位编译模式,输出:40 8
这里 sizeof (source) =40,虽然我们总说数组名存放的是数组的首地址,但是实际上他还是代表着整个数组,sizeof (source) 就是整个数组的长度。
例子
- 指针遍历数组:
#include <iostream>
using namespace std;
int main()
{
int arrays[]{12,34,32,69,43};
int * ptr_arrays=arrays;
for(int i=0;i<5;i++){
cout<<*ptr_arrays++<<" ";
}
cout<<endl;
return 0;
}
输出:
12 34 32 69 43
*与++优先级的问题可以参照 https://blog.csdn.net/mantou_riji/article/details/123568064
- 数组逆序
#include <iostream>
using namespace std;
int main()
{
int arrays[]{12,34,32,69,43,66};
int n=sizeof(arrays)/sizeof(int);
int * ptr_start=arrays;
int * ptr_end=arrays+(n-1);
int temp;
while(ptr_start <= ptr_end){
temp = *ptr_start;
*ptr_start=*ptr_end;
*ptr_end=temp;
ptr_start++;
ptr_end--;
}
for(int i=0;i<n;i++){
cout<<arrays[i]<<" ";
}
cout<<endl;
return 0;
}
输出:
66 43 69 32 34 12
指针的运算符
注:一个指针类型为T的指针的移动以== sizeof(T)== 为移动单位
- 指针的递增和递减运算
#include <iostream>
using namespace std;
int main()
{
int source[5]{1,23,32,44,5};
int * ptr_source=source;
cout<<*ptr_source<<endl;
cout<<*(++ptr_source)<<endl;
return 0;
}
输出:
1
23
- 指针加上或减去某个值
int source[5]{1,23,32,44,5};
int * ptr_source=source;
cout<<*ptr_source<<endl;
cout<<*(++ptr_source)<<endl;
cout<<*(ptr_source+2)<<endl;
输出:
1
23
44
二维数组和指针
- 使用指针创建二维数组
#include <iostream>
using namespace std;
int main()
{
//创建一维数组
int *p =new int[10];
//使用指针创建二维数组
//注意不可以这样写: int ** p2 =new int[5][3];
int (*p2)[3] =new int[5][3];
p2[2][2]=100;
for(int i=0;i<5;i++){
for(int j=0;j<3;j++){
cout<<*(*(p2+i)+j)<<" ";
}
cout<<endl;
}
return 0;
}
输出:
1727920 0 1709952
0 0 0
0 0 100
0 218103821 33395
1727920 0 1704272