第九周 指针
一、&
- 获得变量的地址,其操作数必须是变量
- %p,输出变量的地址
- &不能对没有地址的东西取地址(如表达式)
- 地址从上往下分配
二、指针
-
保存地址的变量(int *p)
-
变量的值是内存的地址
-
作为参数的指针
-
借此访问外部的i
-
改变地址的变量
-
*(间接运算)
- (p指的是地址,*表示取地址对应的变量)
-
*p 整体作为一个整数
-
函数间依旧是值的传递,但是所传递的是地址,因此能够对地址上的值访问
三、使用
- 三部曲
- 说明指针
short a=0,*p;
- 指针必须指向对象后才能使用
- *与&为互补运算
- 指针指向对象
p=&a;
-
通过指针引用对象
*p=*p+2;
-
交换
#include <stdio.h>
void swap(int *a,int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a,b;
scanf("%d%d",&a,&b);
swap(&a,&b);
printf("%d %d",a,b);
return 0;
}
- 最大最小值
#include <stdio.h>
void maxmin(int a[],int length,int *max,int *min);
int main()
{
int a[10],i;
int max,min;
for (i=0;i<10;i++){
scanf("%d",&a[i]);
}
maxmin(a,sizeof(a)/sizeof(a[0]),&max,&min);
printf("%d %d",max,min);
return 0;
}
void maxmin(int a[],int len,int *max,int *min)
{
int i;
*max=*min=a[0];
for(i=0;i<len;i++){
if (a[i]>*max)
*max = a[i];
else if (a[i]<*min)
*min = a[i];
}
}
- 地址变量没有被赋值之前,不能通过“*”访问任何数据
四、指针与数组
- 数组变量本身表达地址
- 不需要用&取地址
- warning:数组的单元是变量,需要&取地址
- *运算符可以对指针做,也可以对数组做
- 数组变量是const的指针,因此不能被赋值(不能改变数组的地址,因此不能使
数组名++
的方式遍历数组)(int *const 数组)
- 数组变量是const的指针,因此不能被赋值(不能改变数组的地址,因此不能使
-
引用数组元素的步骤
-
说明指针和数组
int *p,a[10];
- 指针指向数组
p=a;
(指向首地址,也可写作p=a[0];
) - 通过指针引用数组元素
- 下标法:
a[i]
- 指针法:
*(p+i)
- 数组名法:
*(a+i)
- 下标法:
-
二维数组的地址
int a[3][4]
-
a+1一次加一行(一个一维数组)
- a[0]+1,一次加一个(一个元素)
- i行j列的数组元素可以由以下方式得到
*(a[i] + j)
p[i][j]
*(*(a + i) + j)
-
数组中,以下三种地址等价
-
a+i
(a+i)
&a[i]
- 以下数组元素等价
a[i]
*(a+i)
//通过多种方式输出数组
int a[N],*p = a;
for(i = 0;i < N;i++){ //数组名指针法
printf("%d",*(a+i));
}
for(i = 0;i < N;i++){ //指针变量下标
printf("%d",p[i]);
}
for(i = 0;i < N;i++){ //指针变量指针法
printf("%d",*(p+i));
}
for(;p < a + N;p++){ //通过指针变量访问数组
printf("%d",*p);
}
五、传入函数的数组
-
函数中的数组实际上是指针(特殊的指针)
-
c //以下的函数原型是等价的 int sum(int *ar, int n); int sum(int *, int); int sum(int ar[],int n); int sum(int [],int);
-
四种写法
//实参用数组名,形参用数组
void reverse(int a[]);
reverse(a);
//实参用数组名,形参用指针
void reverse(int *a);
reverse(a);
//实参与形参都用指针变量
void reverse(int *a);
int *p=a;
reverse(p);
//实参用指针变量,形参用数组
void reverse(int a[]);
int *p=a;
reverse(p);
六、指针与const
-
指针是const
-
表示一旦得到了某个变量的地址,不能再指向其他变量
-
c int *const q=&i; //q的值不能被改变 *q = 26; //ok,指向的是i(*q所指) q++; //error,指向的是q
-
所指是const
-
表示不能通过这个指针去修改变量
const int *p=&i;
*p = 26; //错误,*p(地址)是const(p所指向的地址是常量,不可更改)
i = 26; //ok
p = &j; //ok(给p所指向的地址赋值,而不是改变地址)
- 什么意思?(存疑,待解决)
- const在*号前:表示const所指的东西不能被修改
-
const在*号后:表示指针不能被修改
-
转换
-
总是可以把一个非const的值转化成const的(通过函数)
-
作用:当要传递的参数类型比地址大的时候,能以较少的字节数传递值给参数,又能避免函数外面对变量的修改;
-
const数组
- 数组变量已经是const的指针
- 必须通过初始化赋值
七、行指针变量
-
行指针
- 行指针是指向数组的指针,即
int (*p)[N]
- 当二维数组要作为参数进行传递时,声明如下
void fun(int (*p)[N]);
void fun(int p[][N]);
- 实例:矩阵加法
- 行指针是指向数组的指针,即
//*********************************************************
//File name : 0608.cpp
//Author :陈健达
//Date : 2021.11.18
//Student ID :2021218250
//*********************************************************
#include <iostream>
using namespace std;
#define N 4
#define M 5
void input(int (*p)[M]);
void plusMat(int (*p)[M],int (*q)[M]);
void output(int (*p)[M]);
int main(){
int iMat1[N][M],iMat2[N][M],iMatsum[N][M];
int (*p)[M] = iMat1,(*q)[M] = iMat2,(*r)[M] = iMatsum;
cout << "请输入矩阵1" << endl;
input(p);
cout << "请输入矩阵2" << endl;
input(q);
plusMat(p,q);
output(p);
return 0;
}
void input(int (*p)[M]){ //输入
int i, j;
for(i = 0;i < N;i++){
for(j = 0;j < M;j++){
cin >> *(*(p + i) + j);
}
}
}
void plusMat(int (*p)[M],int (*q)[M]){ //矩阵相加
int i, j;
for(i = 0;i < N;i++){
for(j = 0;j < M;j++){
*(*(p + i) + j) = *(*(p + i) + j) + *(*(q + i) + j);
}
}
}
void output(int (*p)[M]){ //输出
int i, j;
for(i = 0;i < N;i++){
for(j = 0;j < M;j++){
cout << *(*(p + i) + j) << "\t";
}
cout << endl;
}
}
八、指针运算
- 可用的有
- 五种算数运算
p++;
p--;
p+=n;
p-=n;
p1-p2;
-
六种关系运算
-
+1实际上是+sizeof(数据类型),其他同理
-
如果不是指向一片连续的数据,则无意义
- 指针可以做比较
-
比较地址
-
0地址
-
指针不应有
- 可做
- 返回的指针无效
- 指针没有被真正的初始化(使用0(NULL)给指针初始化)
-
NULL是一个预定定义的符号,表示0地址
-
指针的类型
-
无论指向什么类型,指针的大小都一样
-
但不同类型之间不能相互赋值
-
可以进行类型转化
- void*表示不知道指向什么东西的指针
-
指针可以做什么?
-
需要传入较大的数据时用作参数
- 传入数组后对数组进行操作
- 函数返回不止一个结果
- 通过函数修改变量
- 动态申请内存
九、动态内存分配
-
分配内存空间函数malloc (需要有stdlib.h或maloc.h头文件)
-
函数原型
void *malloc(unsigned int size)
-
函数调用形式
(类型标识符*) malloc(size)
其中:
- 类型标识符:表示把该区域用于何种数据类型
- (类型标识符*):表示把返回值强制转化为该类型指针
- size:无符号整数
-
函数功能
- 在内存的动态存储区分配一块长size字节的连续区域
- 返回值为该区域的首地址
-
分配内存空间函数calloc
-
示范内存空间函数free
-
函数原型
void free (void * ptr);
-
函数调用形式
free (ptr);
-
函数功能
- 释放ptr所指向的一块内存空间
- 被释放的区域必须是由malloc或calloc函数所分配的区域
-
示例
-
内存的动态分配和释放
-
输入某班学生的某门课程的成绩,计算器平均分并输出,班级人数由键盘输入
#include <stdio.h> #include <stdlib.h> void main(){ int n, t, *p = NULL; float sum, ave; scanf("%d",&n); //输入学生个数 p = (int*)malloc(n * sizeof(int)); //x if(p = NULL){ printf("No enough memory\n"); exit(1); } for(t = 0,sum = 0; t < n;t++){ scanf("%d",p + t); sum = sum + *(p + t); } ave = sum/n; printf("average = %5.2f\n",ave); free(p); }
十、引用
- 引用即给对象起了另一个名字
- 定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的
- 不能定义引用的引用
- 不能与字面值或某个表达式的计算结果绑定在一起
- 也常用于函数传参
- 指向指针的引用
- 指针也是一个对象,因此可以进行引用