第12章 指针

本文详细介绍了C语言中的指针概念,包括指针的基本介绍、类型、算术运算(递增、递减、+、-)、比较、指针数组、指向指针的指针、传递指针给函数、返回指针的函数、函数指针以及回调函数的应用。同时涵盖了动态内存分配的相关知识。
摘要由CSDN通过智能技术生成

以下内容是学习尚硅谷

12.1 指针基本介绍

1)指针是C语言的精华,也是C语言的难点
2)指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量。关于指针的基本使用,在讲变量的时候做了入门级的介绍
3)获取变量的地址,用&,比如:int num=10,获取num的地址:&num
4)指针类型,指针变量存在的是一个地址,这个地址指向的空间存的才是值

比如: int *ptr = # ptr就是指向 int类型的指针变量,即 ptr是 int*类型。

5)获取指针类型所指向的值,使用: * (取值符号),比如: int * ptr,使用*ptr获取 ptr指向的值

12.2 什么是指针

在这里插入图片描述
在这里插入图片描述

12.3 指针的算术运算

指针是一个用数值表示的地址。可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-

12.3.1 指针递增操作(++)

1)案例演示

#include <stdio.h>
#include <string.h>
#include <stdio.h>

const int MAX=3;

int main(){

    int var[]={10,100,200};
    int i,*ptr;

    ptr=var;
    for(i=0;i<MAX;i++){
        printf("var[%d]地址=%p\n",i,ptr);
        printf("存储值:var[%d]=%d\n",i,*ptr);
        ptr++;//ptr=ptr+1(1个int字节数)
    }

    getchar();
    return 0;
}

2)示意图
在这里插入图片描述
3)小结

  • 数组在内存中是连续分布的
  • 当对指针进行++时,指针会按照它指向的数据类型字节数大小增加

12.3.2 指针递减操作(–)

#include <stdio.h>
#include <string.h>
#include <stdio.h>

const int MAX=3;

int main(){

    int var[]={10,100,200};
    int i,*ptr;

    ptr=&var[MAX-1];//&var[2]
    for(i=MAX;i>0;i--){
        printf("ptr存放的地址=%p\n",ptr);
        printf("存储值:var[%d]=%d\n",i-1,*ptr);
        ptr--;//ptr=ptr-1(1个int字节数)
    }

    getchar();
    return 0;
}

2)小结

  • 数组在内存中是连续分布的
  • 当对指针进行–时,指针会按照它指向的数据类型字节数大小减少

12.3.3 指针+、-操作

1)案例演示

#include <stdio.h>
#include <string.h>
#include <stdio.h>

int main(){

    int var[]={10,100,200};
    int i,*ptr;

    ptr=var;
    ptr+=2;
    printf("var[2]=%d var[2]的地址=%p ptr存储的地址=%p ptr指向的值=%d",var[2], &var[2], ptr,*ptr);

    getchar();
    return 0;
}

2)小结
可以对指针按照指定的字节数大小进行+或者–的操作,可以快速定位你要的地址

12.4 指针的比较

指针可以用关系运算符进行比较,如==、<<=和>>=。如果pl和 p2指向两个变量,比如同一个数组中的不同元素,则可对pl 和 p2进行大小比较,看下面代码,说明输出什么?

案例1

#include <stdio.h>
#include <string.h>
#include <stdio.h>

int main(){

    int var[]={10,100,200};
    int *ptr;
    ptr=var;
    
    //if(ptr == var[0]){//错误,类型不一样(int *)和(int )
        //printf("ok1");
    //}

    if(ptr==&var[0]){//可以
        printf("\nok2");
    }

    if(ptr==var){//可以
        printf("\nok3");
    }

    if(ptr>=&var[1]){//可以比较,但是返回false
        printf("\nok4");//不会输出
    }
    

    getchar();
    return 0;
}

案例2

#include <stdio.h>
#include <string.h>
#include <stdio.h>

const int MAX=3;

int main(){

    int var[]={10,100,200};
    int i,*ptr;
    ptr=var;
    i=0;
    while (ptr<=&var[MAX-2]) {
        printf("Address of var[%d]=%x\n",i,ptr);
        printf("Value of var[%d]=%d\n",i,*ptr);
        ptr++;
        i++;
    }
    
    getchar();
    return 0;
}

12.5 指针数组

12.5.1 基本介绍

要让数组的元素指向int或其他数据类型的地址(指针)。可以使用指针数组。

12.5.2 指针数组定义

数据类型 *指针数组名[大小];

1)比如:int *ptr[3];
2)ptr声明为一个指针数组
3)由3个整数指针组成。因此,ptr中的每个元素,都是一个指向int值的指针

12.5.3 指针数组快速入门和内存布局示意图

#include <stdio.h>
#include <string.h>
#include <stdio.h>

const int MAX=3;

int main(){

    int var[]={10,100,200};
    int i,*ptr[3];
    for(i=0;i<MAX;i++){
        ptr[i]=&var[i];
    }
    for(i=0;i<MAX;i++){
        printf("Value of var[%d]=%d ptr[%d]本身的地址=%p \n",i,*ptr[i],i,&ptr[i],&ptr[i]);
    }
    getchar();
    return 0;
}

在这里插入图片描述

12.6 指向指针的指针(多重指针)

12.6.1 基本介绍

在这里插入图片描述

12.6.2 多重指针(二级、三级)快速入门案例

1)一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int类型指针的指针:
int **ptr;//ptr的类型是int **
2)当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,比如 **ptr
3)案例演示+内存布局图

#include <stdio.h>
#include <string.h>

int main(){

    int var;
    int *ptr;//一级指针
    int **pptr;//二级指针
    int ***ppptr;//三级指针
    var=3000;

    ptr=&var;
    pptr=&ptr;
    ppptr=&pptr;
    printf("var的地址=%p var=%d \n",&var,var);
    printf("ptr的本身的地址=%p ptr存放的地址=%p *ptr=%d \n",&ptr,ptr,*ptr);
    printf("pptr的本身的地址=%p pptr存放的地址=%p **pptr=%d \n",&pptr,pptr,**pptr);
    printf("ppptr的本身的地址=%p ppptr存放的地址=%p ***ppptr=%d \n",&ppptr,ppptr,***ppptr);

    getchar();
    return 0;
}

12.6.3 对应的内存布局图

在这里插入图片描述

12.7 传递指针(地址)给函数

当函数的形参类型是指针类型时,是使用该函数时,需要传递指针,或者地址,或者数组给该形参,举例说明:

12.7.1 案例1-传地址或指针给指针变量

#include <stdio.h>
#include <string.h>

void test2(int *p);//函数声明,接受int *
int main(){

    int num=90;
    int *p=&num;
    test2(&num);//传地址
    printf("\nmain()中的num=%d",num);
    test2(p);//传指针
    printf("\nmain()中的num=%d",num);

    getchar();
    return 0;
}

void test2(int *p){
    *p+=1;
}

在这里插入图片描述

12.7.2 案例2-传数组给指针变量

数组名本身就代表该数组首地址,因此传数组的本质就是传地址

#include <stdio.h>
#include <string.h>

double getAverage(int *arr,int size);
double getAverage2(int *arr,int size);
int main(){

    int balance[5]={1000,2,3,17,50};
    double avg;
    avg=getAverage(balance, 5);
    printf("Average value is :%f\n",avg);
    avg=getAverage2(balance, 5);
    printf("Average value is :%f\n",avg);

    getchar();
    return 0;
}

double getAverage(int *arr,int size){
    int i,sum=0;
    double avg;
    for(i=0;i<size;++i){
        sum+=arr[i];
        printf("\narr存放的地址=%p",arr);
    }
    avg=(double)sum/size;
    return avg;
}

double getAverage2(int *arr,int size){
    int i,sum=0;
    double avg;
    for(i=0;i<size;++i){
        sum+=*arr;
        printf("\narr存放的地址=%p",arr);
        arr++;//指针的++运算,会对arr存放的地址做修改
    }
    avg=(double)sum/size;
    return avg;
}

在这里插入图片描述
思路题?
如果在getAverage()函数中,通过指针修改了数组的值,那么main函数的balance数组的值是否会相应改变?会的,因为getAverage()函数中的指针,指向的就是main函数的数组。

12.8 返回指针的函数

C语言允许函数的返回值是一个指针(地址),这样的函数称为指针函数。

12.8.1 快速入门案例

请编写一个函数strlong(),返回两个字符串中较长的一个

#include <stdio.h>
#include <string.h>

char* strlong(char *str1,char *str2){
    printf("\nstr1的长度%d str2的长度%d",strlen(str1),strlen(str2));
    if(strlen(str1)>=strlen(str2)){
        return str1;
    }else {
        return str2;
    }
}

int main(){

    char str1[30]="abc";
    char str2[30]="dddddd";
    printf("\nLonger string:%s\n",strlong(str1,str2));
    
    getchar();
    return 0;
}

12.8.2 指针函数注意事项和细节

1)用指针作为函数返回值时需要注意,函数运行结束后会销毁在它内部定义的所有局部数据、局部数组和形式参数,函数返回的指针不能指向这些数据
2)函数运行结束后会销毁该函数所有的局部数据,这里所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存
3)C语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为static变量
4)代码演示

#include <stdio.h>
#include <string.h>

int* func(){
    //int n=100;//局部变量,在func返回时,就会销毁
    static int n=100;//如果这个局部变量是static性质的,那么n存放数据的空间在静态数据区
    return &n;
}

12.8.3 应用实例

编写一个函数,它会生成10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int* f1(){
    static int arr[10];

    int i=0;
    for(i=0;i<10;i++){
        arr[i]=rand();
    }

    return arr;
}

void main(){
    int *p;
    int i;
    p=f1();//p指向是在f1生成的数组的首地址(即第一个元素的地址)
    for(i=0;i<10;i++){
        printf("\n%d",*(p+i));
    }

    getchar();
}

12.9 函数指针(指向函数的指针)

12.9.1 基本介绍

1)一个函数总是在占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似
2)把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针

12.9.2 函数指针定义

在这里插入图片描述

12.9.3 应用案例

用函数指针来实现对函数的调用,返回两个整数中的最大值.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int max(int a,int b){
    return a>b?a:b;
}

void main(){
    int x,y,maxVal;

    //说明 函数指针
    //1. 函数指针的名字 pmax
    //2. int 表示 该函数指针指向的函数是返回int类型
    //3. (int,int) 表示 该函数指针指向的函数形参是接受两个int
    //4. 在定义函数指针时,也可以写上形参名 int (*pmax)(int x,int y)=max;
    int (*pmax)(int,int)=max;
    printf("Input two numbers:");
    scanf("%d %d",&x,&y);
    maxVal=(*pmax)(x,y);
    printf("Max value:%d pmax=%p pmax本身的地址=%p\n",maxVal,pmax,&pmax);

    getchar();
}

12.9.4 函数指针的内存布局

在这里插入图片描述

12.10 回调函数

12.10.1 基本介绍

1)函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数
2)简单的讲:回调函数是由别人的函数执行时调用你传入的函数(通过函数指针完成)

12.10.2 应用实例

使用回调函数的方式,给一个整型数组 int arr[10]赋10个随机数

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//回调函数
//1.int (*f)(void)
//2.f就是函数指针,它可以接收的函数是(返回int,没有形参的函数)
//3.f在这里被initArray调用,充当了回调函数的角色
void initArray(int *array,int arraySize,int (*f)(void)){
    int i;
    for(i=0;i<arraySize;i++){
        array[i]=f();
    }
}

int getNextRandomValue(void){
    return rand();
}

void main(){
    int myarray[10],i;
    initArray(myarray, 10,&getNextRandomValue);
    for(i=0;i<10;i++){
        printf("%d ",myarray[i]);
    }

    getchar();
}

12.11 指针的注意事项和细节

1)指针变量存放的是地址,从这个角度看指针的本质就是地址
2)变量声明的时候,如果没有确切的地址赋值,为指针变量赋一个NULL值是好的编程习惯
3)赋为NULL值的指针被称为空指针,NULL指针是一个定义在标准库<stdio.h>中的值为零的常量#define NULL 0

12.12 动态内存分配

12.12.1 C程序中,不同数据在内存中分配说明

在这里插入图片描述
在这里插入图片描述

12.12.2 内存动态分配的相关函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
6)返回类型说明
在这里插入图片描述

12.12.3 应用实例

在这里插入图片描述

12.12.4 动态分配内存的基本原则

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李贺梖梖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值