8.1 c++ 指针

第八章 指针

8.1 指针变量

8.1.1 内存的概述

在32位平台,每一个进程拥有4G的空间。

系统为内存的每一个字节 分配一个32位的地址编号(虚拟地址)。这个编号称之为地址。

在这里插入图片描述

无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节,即任何类型的指针变量都是4个字节大小。

在这里插入图片描述

8.1.2 地址和指针变量的关系

地址 就是内存的地址编号。

指针变量:本质是变量 只是该变量保存的是内存的地址编号。(不是普通的数值)

在这里插入图片描述

8.1.3 指针变量的定义

1、定义步骤(定义的时候)

​ *修饰指针的变量p。

​ 保存谁的地址 就先定义谁。

​ 从上往下整体替换。

案例1:

定义一个指针变量p	保存int num的地址;	      int *p;
定义一个指针变量p	保存数组int arr[5]首地址;	 int (*p)[5]
定义一个指针变量p	保存函数的入口地址	       int fun(int,int);int (*p)(int,int);
定义一个指针变量p	保存结构体变量的地址	       struct stu lucy; struct stu *p;
定义一个指针变量p	保存指针变量int *p的地址	   int **p

2、在32位平台

cout<<sizeof(char *)<<endl;
cout<<sizeof(short *)<<endl;
cout<<sizeof(int *)<<endl;
cout<<sizeof(long *)<<endl;
cout<<sizeof(float *)<<endl;
cout<<sizeof(double *)<<endl;
cout<<sizeof(int ************)<<endl;

3、指针变量和普通变量建立关系

int num = 10;
int *p;
p = &num;//普通变量和指针变量建立关系
cout<<*p;//10

在这里插入图片描述

8.1.4 指针变量的初始化

指针变量 在操作之前必须指向合法的地址空间。

1、指针变量 如果不初始化 立即操作 会出现段错误

int *p;
cout<<*p<<endl;

2、指针变量 如果没有指向合法的空间 建议初始化为NULL

int *p = NULL;//NULL是赋值给p int *p; p=NULL;

不要操作 指向NULL的指针变量

3、将指针变量 初始化为合法的地址(变量的地址、动态申请的地址、函数入口地址)

int num = 10;
int *p = &num;//int *p;p=&num;
int data=10,*p=&data;//data为int类型 p为int *类型
int *p,data;//p为int *类型	data为int类型

8.1.5 指针变量的类型

1、指针变量自身的类型:将指针变量名拖黑,剩下的类型就是指针变量自身的类型

int *p;	p自身的类型为int *

指针变自身的类型 一般用于赋值语句的判断

int num = 10;
int *p = &num;

//在使用中
//num 为int	&num为int *	---->对变量名 取地址	整体类型加一个*
//p 为int * *p为int		---->对指针变量	取*	 整体类型减一个*

//在使用中 &和*相遇	从右往左 依次抵消
*&p == p

案例1:int num=10, *p=&num, **q=&p;以下结果正确的是_______abc___

int num = 10;
int *p = &num;
int **q = &p;
A:*p=100		B:*q=&num		C:p=&num		D:q=&num			

2、指针变量指向的类型(重要)

将指针变量名和离它最近的一个*一起拖黑,剩下的类型就是指针变量指向的类型

int *p; p指向的类型为int

3、指针变量的指向类型 决定了取值宽度

int num = 0x01020304;
int *p = &num;
为啥 *p == num == 0x01020304?

在这里插入图片描述

4、指针变量的指向类型 决定了+1跨度

在这里插入图片描述

8.1.6 综合案例分析

在这里插入图片描述

案例1:取出0x0102的值

short *p = (short *)&num;
*(p+1);

案例2:取出0x02的值

char *p = (char *)&num;
*(p+2);

案例3:取出0x0203的值

char *p = (char *)&num;
*(short *)(p+1)

8.1.7 *p等价num

int num = 10;
int *p = &num;
//p==&num
//*p == *&num == num

8.1.8 指针变量的注意事项

1、void不能定义普通变量

void num;//error 不能给num开辟空间

2、void *可以定义指针变量

void *p;//ok p自身类型为void *,在32为平台任意类型的指针 为4B
那么系统知道为p开辟4B空间,所以定义成功

p就是万能的一级指针变量,能保存任意一级指针的地址编号

int num = 10;
void *p = &num;

short data = 20;
p = &data;

万能指针一般用于函数的形参达到算法操作多种数据类型的目的

记住:不要直接对void *p的指针变量 取*

int num = 10;
void *p = &num;
*p;//err p指向的类型为void 无法确定p的取值宽度 所以不能*p

对p取*之前 对p先进行指针类型强转

int num = 10;
void *p = &num;
cout<<*(int *)p<<endl;

3、指针变量 未初始化 不要取*

int *p;
*p;//err 段错误

4、指针变量 初始化NULL 不要取*

int *p = NULL;
*p;//err 段错误

5、指针变量 不要越界访问

char ch = 'a';
int *p = &ch;
*p;//err 越界访问非法内存

在这里插入图片描述

int num = 10;
int *p = &num;
p++;
*p;//越界访问

8.2 数组元素的指针

8.2.1 数组元素的指针概述

数组元素的指针变量 是用来保护数组元素的地址

int arr[5] = {10,20,30,40,50};
//需要定义一个指针变量 保存 数组元素的地址
int *p;
p = &arr[0];
p = arr;//arr作为地址 第0个元素的地址 arr==&arr[0]
p = &arr[3];

8.2.2 数组元素的指针变量和数组名(作为地址)等价

在这里插入图片描述

8.2.3 在使用中[]就是*()的缩写

int arr[5] = {10,20,30,40,50};
int n = sizeof(arr)/sizeof(arr[0]);

cout<<"arr[1]="<<arr[1]<<endl;
cout<<"*(arr+1)="<<*(arr+1)<<endl;
cout<<"-----------------"<<endl;
cout<<"*(arr+1) = "<<*(1+arr)<<endl;
cout<<"1[arr] = "<<1 [arr]<<endl;

//[]是* ()的编写 []左边的值 放在+的左边 []里面的值 放在+右边 整体取*

为啥arr==&arr[0]?

&arr[0] == &*(arr+0) == arr+0 == arr

案例1:p[-1]的值_30__

int arr[5] = {10,20,30,40,50};
int *p = arr+3;

案例2:p[1]的值__50__

int arr[5] = {10,20,30,40,50};
int *p = arr+3;

8.2.4 指向同一数组元素的两个指针变量间的关系

在这里插入图片描述

8.3 字符串与指针

8.3.1 字符数组

char str1[128] = "hello world";

str1是数组开辟128字节 存放字符串"hello world"

在这里插入图片描述

sizeof(str1) == 128字节

8.3.2 字符串指针变量

char *str2 = "hello world";

在这里插入图片描述

sizeof(str2) == 4字节 或 8字节

str2是指针变量 在栈区/全局区 保存的是字符串常量"hello world"的首元素地址,而"hello world"在文字常量区

char* str2 = (char *) "hello world";
cout<<str2<<endl;//hello world
cout<<str2<<endl;//'w'
str2[6] = 'H';//不允许 给文字常量区 赋值

8.4 指针数组

指针数组:本质是数组 只是数组的每个元素为指针

32位平台:

char *arr1[4];
short *arr2[4];
int *arr3[4];

sizeof(arr1) == 16B
sizeof(arr2) == 16B
sizeof(arr3) == 16B

8.4.1 数值的指针数组

在这里插入图片描述

int num1 = 10;
int num2 = 20;
int num3 = 30;
int num4 = 40;

int *arr[4] = {&num1,&num2,&num3,&num4};
int n = sizeof(arr)/sizeof(arr[0]);

int i;
for(i=0;i<n;i++)
{
cout<<*arr[i]<<" "; // 10 20 30 40
}
cout<<endl;

8.4.2 字符指针数组

char *arr[4] = {"hehehehe","xixixixixi","lalalala","hahahaha"};

在这里插入图片描述

char *arr[4] = {"hehehehe","xixixixi","lalalala","hahahaha"};
int n = sizeof(arr)/sizeof(arr[0]);

int i;
for (int i = 0; i < n; ++i) {
    // cout<<arr[i]<<endl;
    cout<<*(arr[i]+3)<<endl; // 字符串的第4个字符
}

8.4.3 二维字符数组

char *arr1[4]={"hehehehe","xixixixi","lalalala","hahahaha"};
char arr2[4][128] = {"hehehehe","xixixixi","lalalala","hahahaha"};

arr1是在指针数组 存放的是每个字符串的元素的地址

arr2是二维字符数组 存放的是每个字符串

8.5 指针的指针

int num = 10;
int *p = &num;
int **q = &p;

n级变量可以保存 n-1级指针变量的地址

在这里插入图片描述

8.6 数组指针

8.6.1 数组首元素地址和数组首地址

数组首元素地址:&arr[0] == arr arr+1跳过一个元素

int arr[5] = {10,20,40,50};
//arr数组元素的地址 +1 跳过一个元素
cout << "arr = "<<arr<<endl;
cout << "arr+1 = "<<arr+1<<endl;

数据的首地址:&arr &arr+1跳过整个数组

int arr[5] = {10,20,40,50};
//&arr数组首地址 +1 跳过整个数组
cout<<"&arr = "<<&arr<<endl;
cout<<"&arr+1 = "<<&arr+1<<endl;

8.6.2 数组指针 本质是指针变量保存的是数组的首地址

int arr[5] = {10,20,30,40,50};
int (*p)[5] = NULL;//数组指针
//数组指针 本质是指针变量,只是该变量存储的是数组的首地址
int (*p)[5] = NULL;
cout<<"sizeof(p) = "<<sizeof(p)<<endl;//4B
cout<<"p="<<p<<endl;
cout<<"p+1 = "<<p+1<<endl;
int arr[5] = {10,20,30,40,50};
int (*p)[5] = &arr;//数组指针

在这里插入图片描述

int arr[5] = {10,20,40,50};
//数组指针 本质是指针变量,只是该变量存储的是数组的首地址
int (*p)[5] = NULL;
cout<<"sizeof(p)"<<sizeof(p)<<endl;//4B

//数组指针和数组建立关系
p = &arr;
cout<<"arr[2] = "<<*(*p+2)<<endl; // 40

8.6.3 数组指针的案例

int arr[5] = {10,20,30,40,50};
int (*p)[5] = &arr;//数组指针
cout<<*((int *)(p+1)-2) <<endl;//40 分析为啥是40

总结:

int *arr[5];//指针数组 本质是数组 每个元素为int *
int (*arr)[5];//数组针对 本质是指针变量 保存的是数组的首地址(数组必须5个元素 每个元素为int00)

8.6.4 二维数组和数组指针的关系

1、深入了解二维数组

在这里插入图片描述

arr[1] => *(arr+1)	第一行第0列的列地址
&arr[1] => &*(arr+1) =>arr+1 第1行的行地址
*arr+1 => 第0行第1列的列地址
arr[1]+2 => *(arr+1)+2 =>第1行第2列的列地址
**arr == *(*(arr+0)+0) == arr[0][0]

2、二维数组和一维数组指针的关系

在这里插入图片描述

int arr[n];			int *p;
int arr[n][m]		int (*p)[m]
int arr[n][m];		int	(*p)[m][k]
n维数组	和n-1维的数组指针 等价

8.7 多维数组的物理存储

不管几维数组在物理上 都是一维存储,在逻辑上是多维的。

在这里插入图片描述

int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int row = sizeof(arr)/sizeof(arr[0]);
int col = sizeof(arr[0])/sizeof (arr[0][0]);

int *p = &arr[0][0];

int i = 0;
for(i=0;i<row*col;i++)
{
	cout<<p[i]<<" ";
}
cout<<endl;

8.8 指针与函数

8.8.1 指针变量作为函数的参数

如果想在函数内部 修改外部变量的值 需要将外部变量的地址 传递给函数。(重要)

案例1:单向传递之 传值

函数内部 不能修改外部变量的值

void setNum01(int data)
{
    data = 100;
}
void test01()
{
    int num = 0;
    setNum01(num); //单向传递之 传值
    cout<<"num = "<<num<<endl;//0 修改不成功
}

案例2:单向传递之 传地址

函数内部就可以修改 外部变量的值

void setNum02(int *p)   //int *p = &num;
{
//    *p == num
    *p = 100;
}
void test01()
{
    int num = 0;
    setNum02(&num); //单向传递之 传地址
    cout<<"num = "<<num<<endl;//100 修改成功
}

8.8.2 一维数组作为函数的参数

函数内部想操作(读或写)外部数组元素,将数组名 传递给函数。

一维数组 作为函数的形参 会被优化成 指针变量。

//void outputIntArray(int arr[5],int n)
//一维数组组为函数的参数 会被编译器 优化成 指针变量
void outputIntArray(int *arr,int n)
{
    cout<<"内部sizeof(arr) = "<<sizeof(arr)<<endl;//4B
    int i = 0;
    for (i=0;i<n;i++)
    {
        //cout<<*(arr+i)<<" ";
        cout<<arr[i]<<" ";//推荐
    }
    cout << endl;
}

void test02()
{
    int arr[5] = {10,20,30,40,50};
    int n = sizeof(arr)/sizeof(arr[0]);
    cout<<"外部sizeof(arr) = "<<sizeof(arr)<<endl;//20B

    //遍历素组
    outputIntArray(arr,n); // 10 20 30 40 50
}

8.8.3 二维数组作为函数的参数

函数内部想操作函数外部的二维数组 需要将二维数组名传递给函数

二维数组作为函数的形参会被优化一维的数组指针。

//void outputIntDoubleArray(int arr[3][4],int row,int col)
void outputIntDoubleArray(int (*arr)[4],int row,int col)
{
    cout<<"内部sizeof(arr) = "<<sizeof(arr)<<endl;//4B
    int i=0,j=0;
    for (i=0;i<row;i++) {
        for(j=0;j<col;j++)
        {
            cout<<arr[i][j]<<" ";
        }
        cout<<endl;
    }
}

void test03()
{

    int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
    int row = sizeof(arr)/sizeof(arr[0]);
    int col = sizeof(arr[0])/sizeof(arr[0][0]);
    cout<<"外部sizeof(arr) = "<<sizeof(arr)<<endl;//48B

    outputIntDoubleArray(arr,row,col);
}

8.8.4 函数的返回值类型为指针类型

将函数内部的合法地址 通过返回值 返回给函数外部使用

注意:函数不要返回值 普通局部变量的地址

int* getAddr(void)
{
    //int data = 100;//不要返回普通局部变量的地址
    static int data = 100;
    return &data;
}
void test04()
{
    int *p = NULL;
    p = getAddr();
    cout<<"*p = "<<*p<<endl;//100
}

8.9 函数指针

8.9.1 函数指针的定义

函数名 代表函数的入口地址

函数指针:本质是一个指针变量 只是该变量 保存的是函数的入口地址

//函数指针 p只能保存 有两个int形参以及int返回值 的函数入口地址
int (*p)(int,int) = NULL;
int myAdd(int x,int y)
{
    return x+y;
}

void test05()
{
    int (*p)(int x,int y) = NULL;
    cout <<"sizeof(p) = " << sizeof(p)<<endl;//4B

    //函数指针 和 函数入口地址建立关系
    p = myAdd;

    //通过p调用myAdd函数(函数+(实参))
    cout<<p(10,20)<<endl;//30
}

8.9.2 函数指针变量注意

函数指针变量 不要+1 无意义

不要对函数指针变量取* 无意义

int (*p)(int,int) = my_add
*p会被编译器优化成p
int myAdd(int x,int y)
{
    return x+y;
}
void test05()
{
    int (*p)(int x,int y) = NULL;
    cout <<"sizeof(p) = " << sizeof(p)<<endl;//4B

    //函数指针 和 函数入口地址建立关系
    p = myAdd;

    //通过p调用myAdd函数(函数+(实参))
    cout<<(********p)(10,20)<<endl;//30
    	//(********p)会被编译器优化成p
}

函数指针变量 判断大小 > < 无意义

函数指针变量 可以赋值 p2=p1

函数指针变量 可以判断相等 p2==p1

8.9.3 函数指针变量 使用typedef定义

int myAdd(int x,int y)
{
    return x+y;
}
void test05()
{
    //给函数指针类型取别名
    typedef int (*FUN_TYPE)(int x,int y);
    FUN_TYPE p=NULL;
    
    /*int (*p)(int x,int y) = NULL;*/
    cout <<"sizeof(p) = " << sizeof(p)<<endl;//4B

    //函数指针 和 函数入口地址建立关系
    p = myAdd;

    //通过p调用myAdd函数(函数+(实参))
    cout<<p(10,20)<<endl;//30
}

8.9.4 函数指针作为函数的参数

目的:让算法功能多样化

案例1:设计一个算法,完成加减乘除

int myAdd(int x,int y)
{
    return x+y;
}
int mySub(int x,int y)
{
    return x-y;
}
int myMul(int x,int y)
{
    return x*y;
}
int myDiv(int x,int y)
{
    return x/y;
}
int myCalc(int x,int y, int (*func)(int,int))
{
    return func(x,y);
}
void test06()
{
    cout<<myCalc(10,20,myAdd)<<endl;
    cout<<myCalc(10,20,mySub)<<endl;
    cout<<myCalc(10,20,myMul)<<endl;
    cout<<myCalc(10,20,myDiv)<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值