第七章:指针

目录

7.1 指针变量

7.1.1 内存的概述

7.1.2 地址和指针变量的关系

7.1.3 指针变量的定义

7.1.4 指针变量的初始化

7.1.5 指针变量的类型

7.1.6 综合案例分析

7.1.7 *p等价num

7.1.8 指针变量的注意事项

7.2 数组元素的指针

7.2.1数组元素的指针概述

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

7.2.3 在使用中 【】就是 *()的缩写

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

7.3 字符串与指针

7.3.1 字符数组

7.3.2 字符串指针变量

7.4 指针数组

7.4.2 字符指针数组

7.4.3 二维字符数组

7.5 指针的指针

7.6 数组指针

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

​编辑7.6.2 数组指针 本质是指针变量 保存的是数组的首地址

​编辑​编辑7.6.3 数组指针的案例

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

7.7 多维数组的物理存储

7.8 指针与函数

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

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

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

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

7.9 函数指针

7.9.1 函数指针的定义

7.9.2 函数指针变量注意

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

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


7.1 指针变量

7.1.1 内存的概述

32 位平台,每一个进程 拥有 4G 的空间。
系统为内存的每一个字节 分配一个 32 位的地址编号(虚拟地址)。这个编号称之为地址。

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

7.1.2 地址和指针变量的关系

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

7.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 *q的地址 int **p
2 、在 32 位平台任何类型的指针变量 都是 4 字节

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

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

7.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类

7.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跨度

7.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)

7.1.7 *p等价num

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

7.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;//error 越界访问非法内存

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

7.2 数组元素的指针

7.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];

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

int arr[5]={10,20,30,40,50};
int n = sizeof(arr)/sizeof(arr[0]);
int *p = arr;//p=arr
//遍历整个数组元素
int i;
for(i=0;i<n;i++)
{
  //cout<<arr[i]<<" ";
  //cout<<*(arr+i)<<" ";
  cout<<*(p+i)<<" ";
}
cout<<endl;

7.2.3 在使用中 【】就是 *()的缩写

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

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

//[]是*()的缩写 []左边的值 放在+的左边 []里面的值 放在+右边 整体取*
为啥 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;

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

7.3 字符串与指针

7.3.1 字符数组

char str1[128]="hello world";
str1 是数组 开辟 128 字节 存放字符串 "hello world"

 sizeof(str1) == 128字节

7.3.2 字符串指针变量

char *str2="hello world";

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

7.4 指针数组

指针数组:本质是数组 只是数组的每个元素为 指针
32 位平台:
char *arr1[4];
short *arr2[4];
int *arr3[4];

sizeof(arr1) ==16B
sizeof(arr2) ==16B
sizeof(arr3) ==16B
7.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;

7.4.2 字符指针数组

char *arr[4]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};

char *arr[4]={"hehehehe","xixixixi", "lalalala", "hahahahaha"};
int n = sizeof(arr)/sizeof(arr[0]);
int i;
for(i=0;i<n;i++)
{
  cout<<arr[i]<<endl;
}

7.4.3 二维字符数组

char *arr1[4]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};
char arr2[4][128]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};

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

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

7.5 指针的指针

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

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

7.6 数组指针

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

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

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

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

int arr[5]={10, 20, 30,40,50};
int (*p)[5] = NULL;//数组指针

 

int arr[5]={10, 20, 30,40,50};
int (*p)[5] = &arr;//数组指针

7.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个元素每个元素为
int)

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

1 、深入了解二维数组

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

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][k];     int (*p)[m][k]
n维数组 和n-1维的数组指针 等价

7.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;

7.8 指针与函数

7.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 修改成功
}

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

函数内部 想操作(读或写)外部数组元素,将数组名 传递给函数。
一维数组 作为函数的形参 会被优化成 指针变量。
//void ouputIntArray(int arr[5], int n)
//一维数组作为函数的参数 会被编译器 优化成 指针变量
void ouputIntArray(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
  //遍历数组
  ouputIntArray(arr, n);// 10 20 30 40 50
}

7.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);
}

7.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
}

7.9 函数指针

7.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
}

7.9.2 函数指针变量注意

函数指针变量 不要 +1 无意义
不要对函数指针变量取 * 无意义
int (*p)(int, int) = my_add;
*p会被编译器优化成p

函数指针变量 判断大小 > < 无意义
函数指针变量 可以赋值 p2=p1
函数指针变量 可以判断相等 p2 ==p1

7.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;
  cout<<"sizeof(p) = "<<sizeof(p)<<endl;//4B
  //函数指针 和 函数入口地址建立关系
  p = myAdd;
  //通过p调用myAdd函数(函数+(实参))
  cout<<p(10,20)<<endl;//30
}

7.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;//30
  cout<<myCalc(10,20, mySub)<<endl;//-10
  cout<<myCalc(10,20, myMul)<<endl;//200
  cout<<myCalc(10,20, myDiv)<<endl;//0
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值