跳转至

第九周 指针

一、&

  1. 获得变量的地址,其操作数必须是变量
  2. %p,输出变量的地址
printf("%p",&i);
//or
p=(int)&i;
printf("0x%x",p);
  1. &不能对没有地址的东西取地址(如表达式)
  2. 地址从上往下分配

二、指针

  • 保存地址的变量(int *p)

  • 变量的值是内存的地址

  • 作为参数的指针

    void f(int *p);
    
    int i = 0;
    f(&i);
    
  • 借此访问外部的i

  • 改变地址的变量

  • *(间接运算)

    • (p指的是地址,*表示取地址对应的变量)
    *(指针)=值
    
  • *p 整体作为一个整数

  • 函数间依旧是值的传递,但是所传递的是地址,因此能够对地址上的值访问

三、使用

  1. 三部曲
  2. 说明指针short a=0,*p;
    1. 指针必须指向对象后才能使用
    2. *与&为互补运算
  3. 指针指向对象p=&a;
  4. 通过指针引用对象*p=*p+2;

  5. 交换

#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;
} 
  1. 最大最小值
#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];
    }
}
  • 地址变量没有被赋值之前,不能通过“*”访问任何数据

四、指针与数组

  1. 数组变量本身表达地址
  2. 不需要用&取地址
  3. warning:数组的单元是变量,需要&取地址
  4. *运算符可以对指针做,也可以对数组做
    1. 数组变量是const的指针,因此不能被赋值(不能改变数组的地址,因此不能使数组名++的方式遍历数组)(int *const 数组)
  5. 引用数组元素的步骤

  6. 说明指针和数组int *p,a[10];

  7. 指针指向数组p=a;(指向首地址,也可写作p=a[0];)
  8. 通过指针引用数组元素
    1. 下标法:a[i]
    2. 指针法:*(p+i)
    3. 数组名法:*(a+i)
  9. 二维数组的地址int a[3][4]

  10. a+1一次加一行(一个一维数组)

  11. a[0]+1,一次加一个(一个元素)
  12. i行j列的数组元素可以由以下方式得到
    1. *(a[i] + j)
    2. p[i][j]
    3. *(*(a + i) + j)
  13. 数组中,以下三种地址等价

  14. a+i

  15. (a+i)
  16. &a[i]
  17. 以下数组元素等价
  18. a[i]
  19. *(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);
}

五、传入函数的数组

  1. 函数中的数组实际上是指针(特殊的指针)

  2. c //以下的函数原型是等价的 int sum(int *ar, int n); int sum(int *, int); int sum(int ar[],int n); int sum(int [],int);

  3. 四种写法

//实参用数组名,形参用数组
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

  1. 指针是const

  2. 表示一旦得到了某个变量的地址,不能再指向其他变量

  3. c int *const q=&i; //q的值不能被改变 *q = 26; //ok,指向的是i(*q所指) q++; //error,指向的是q

  4. 所指是const

  5. 表示不能通过这个指针去修改变量

const int *p=&i;
*p = 26;     //错误,*p(地址)是const(p所指向的地址是常量,不可更改)
i = 26;      //ok
p = &j;      //ok(给p所指向的地址赋值,而不是改变地址)
  1. 什么意思?(存疑,待解决)
int i;
const int* p1 = &i;      
int const* p2 = &i;      
int *const p3 = &i;      
  • const在*号前:表示const所指的东西不能被修改
  • const在*号后:表示指针不能被修改

  • 转换

  • 总是可以把一个非const的值转化成const的(通过函数)

void f(const int* x);
int a = 15;
f(&a);
const int b = a;

f(&b);       //const
b=a+1;       //error
  1. 作用:当要传递的参数类型比地址大的时候,能以较少的字节数传递值给参数,又能避免函数外面对变量的修改;

  2. const数组

const int a[]={1,2,3,4,5,6,};
  • 数组变量已经是const的指针
  • 必须通过初始化赋值

七、行指针变量

  1. 行指针

    1. 行指针是指向数组的指针,即int (*p)[N]
    2. 当二维数组要作为参数进行传递时,声明如下
      1. void fun(int (*p)[N]);
      2. void fun(int p[][N]);
    3. 实例:矩阵加法
//*********************************************************
//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;
     }
 }

八、指针运算

  1. 可用的有
  2. 五种算数运算
    1. p++;
    2. p--;
    3. p+=n;
    4. p-=n;
    5. p1-p2;
  3. 六种关系运算

  4. +1实际上是+sizeof(数据类型),其他同理

  5. 如果不是指向一片连续的数据,则无意义

  6. 指针可以做比较
  7. 比较地址

  8. 0地址

  9. 指针不应有

  10. 可做
    1. 返回的指针无效
    2. 指针没有被真正的初始化(使用0(NULL)给指针初始化)
  11. NULL是一个预定定义的符号,表示0地址

  12. 指针的类型

  13. 无论指向什么类型,指针的大小都一样

  14. 但不同类型之间不能相互赋值

  15. 可以进行类型转化

    1. void*表示不知道指向什么东西的指针
    void* q=(void*)p;
    
  16. 指针可以做什么?

  17. 需要传入较大的数据时用作参数

  18. 传入数组后对数组进行操作
  19. 函数返回不止一个结果
  20. 通过函数修改变量
  21. 动态申请内存

九、动态内存分配

  1. 分配内存空间函数malloc (需要有stdlib.h或maloc.h头文件)

  2. 函数原型

    void *malloc(unsigned int size)

  3. 函数调用形式

    (类型标识符*) malloc(size)

    其中:

    • 类型标识符:表示把该区域用于何种数据类型
    • (类型标识符*):表示把返回值强制转化为该类型指针
    • size:无符号整数
  4. 函数功能

    1. 在内存的动态存储区分配一块长size字节的连续区域
    2. 返回值为该区域的首地址
    char *pc;
    pc = (char *)malloc(100);
    //表示分配100个字节的内存空间,若申请失败则返回NULL
    
  5. 分配内存空间函数calloc

  6. 示范内存空间函数free

  7. 函数原型

    void free (void * ptr);

  8. 函数调用形式

    free (ptr);

  9. 函数功能

    1. 释放ptr所指向的一块内存空间
    2. 被释放的区域必须是由malloc或calloc函数所分配的区域
  10. 示例

  11. 内存的动态分配和释放

    #include <stdio.h>
    #include <stdlib.h>
    void main(){
        int *p = NULL;
        p = (int *)malloc(sizeof(int));
        *p = 100;
        printf("%d\n",*p);
        free(p);
    }
    
  12. 输入某班学生的某门课程的成绩,计算器平均分并输出,班级人数由键盘输入

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

十、引用

  1. 引用即给对象起了另一个名字
    int iVal = 1024;
    int &refval = iVal;
    
  2. 定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的
  3. 不能定义引用的引用
  4. 不能与字面值或某个表达式的计算结果绑定在一起
  5. 也常用于函数传参
  6. 指向指针的引用
    1. 指针也是一个对象,因此可以进行引用
      int i = 2;
      int *p = &i;
      int *& = p;              //int *是数据类型