【27】C语言 | 指针进阶

news2024/11/14 14:18:50

目录

一、指针概念

二、字符指针

三、指针数组

四、数组指针

五、数组参数、指针参数

六、函数指针

七、函数指针数组

八、回调函数


一、指针概念

  • 1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  • 2.指针的大小是固定的4/8个字节(32位平台/64位平台)。
  • 3.指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限

二、字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char*;

一般使用:

int main()
{
    char ch = 'q';    
    char* cp = &ch;    //* 说明cp是一个指针变量,char 说明指针指向的数据类型是char
    return;
}

 字符指针不仅指向字符,还可以指向一个字符串

int main()
{
    char arr[] = "hello bit"; //这里是把“hello bit”全部放到arr字符串中了
    char* ps = "hello bit";//本质上是把“hello bit”这个字符窜的首字符的地址存储在了ps中
    
    printf("%c\n",*ps);  //按字符打印:输出h
    printf("%s\n",ps);   //按字符串打印:输出hello bit
    printf("%s\n",arr);  //按字符串打印:输出hello bit
    return 0;
}

案例:下面输出结果是什么 

int main()
{
    char strl[] = "hello bit.";
    char str2[] = "hello bit.";
    char* str3 = "hello bit.";
    char* str4 = "hello bit.";
    if (strl == str2)
        printf("strl and str2 are same\n");
    else
        printf("strl and str2 are not same\n");
    if (str3 == str4)
        printf("str3 and str4 are same\n");
    else
        printf("str3 and str4 are not same\n");
    return 0;
}

输出为: 

strl and str2 are not same
str3 and str4 are same
//因为当char strl[] = "hello bit.";char str2[] = "hello bit.";和的时候
//内存开辟了两份空间比较strl1和strl2,他们的地址不同

//char* str3 = "hello bit.";时候,"hello bit."属于常量字符串,内存中只存一份
//所以比较他们的指针变量指向的地址时是相同的

三、指针数组

在《指针》章节我们也学了指针数组,指针数组是一个存放指针的数组

int main()
{
    //指针数组
    //数组 - 数组中存放的是指针(地址)
    //int* arr[3];
    int a = 10;
    int b = 20;
    int c = 30;
    int i = 0;
    int* arr[3] = {&a,&b,&c};
    for(i=0; i<3; i++)
    {
        printf("%d ",*(arr[i]));
    }

    return 0;
}

再看一个案例:

int main()
{
    int a[6] = {1,2,3,4,5,6};
    int b[] = {2,3,4,5,6,7};
    int c[] = {3,4,5,6,7,8,};

    int i = 0;
    int* arr[3] = {a,b,c};
    for(i=0; i<3; i++)
    {
        int j = 0;
        for(j=0; j<6; j++)
        {
            printf("%d ",*(arr[i]+j));
        }
        printf("\n");
    }
    return 0;
}

和上面是一样的

int main()
{
    int a[6] = {1,2,3,4,5,6};
    int b[] = {2,3,4,5,6,7};
    int c[] = {3,4,5,6,7,8,};

    int i = 0;
    int* arr[3] = {a,b,c};
    for(i=0; i<3; i++)
    {
        int j = 0;
        for(j=0; j<6; j++)
        {
            //printf("%d ",*(arr[i]+j));
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

四、数组指针

int main()
{
    int a = 10;
    int* pa = &a;  //返回的类型为整形,所以是整形指针

    char ch = 'w';
    char* pc = &ch;  //返回的类型为字符类型,所以是字符指针

    int arr[10] = {1,2,3,4,5,};//arr - 数组名是首元素的地址 - arr[0]的地址
    int (*parr)[10] = &arr;//取出的是数组的地址
    //parr就是一个数组指针,*parr说明是一个指针,int加上[10]说明指针指向的类型是一个数组,里面放的是整形
    //parr存放的是数组的地址     

    return 0;
}

下列输出什么:

int main()
{
    int arr[10] = {0};

    int* p1 = arr;
    int (*p2)[10] = &arr;

    printf("%p\n",p1);
    printf("%p\n",p1+1);

    printf("%p\n",p2);
    printf("%p\n",p2+1);

    return 0;
}

数组名是数组首元素的地址

但是有2个例外:

sizeof(数组名) - 数组名表示整个数组,计算的是整个数组大小,单位是字节

&数组名 - 数组名表示整个数组,取出的是整个数组的地址

 下面用整形指针输出数组每个元素的地址

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int *p = arr;
    int i = 0;
    for(i=0; i<10; i++)
    {
        printf("%d ",*p+i);
    }
    return 0;
}

然后再用数组指针输出数组每个元素的地址

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int (*pa)[10] = &arr;
    int i = 0;
    for(i=0; i<10; i++)
    {
        printf("%d ",*((*pa)+i)); //这里的*pa相当于arr(数组名的地址)
    }                                //i相当于下标为i的地址
    return 0;
}

以前的打印一个二维数组:

void print(int arr[3][5],int r,int c)
{
    int  i =0;
    for(i=0; i<r; i++)
    {
        int j = 0;
        for(j=0; j<c; j++)
        {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5] = {{1,2,3,4,5,},{2,3,4,5,6},{4,5,6,7,8}};
    print(arr,3,5);

    return 0;
}

理解下列:

int main()
{ 
    int arr[5];      //整形数组

    int *parr1[10];  //整形指针的数组,
                    //数组里有10个元素,每个元素的返回类型是int*

    int (*parr2)[10]; //数组指针,该指针能够指向一个数组,
                      //数组10个元素,每个元素的类型是int

    int (*parr3[10])[5];  //是一个存放数组指针的数组
                          //该数组能够存放10个数组指针
                          //每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型

    return 0;
}

五、数组参数、指针参数

在写代码的时候难免要把[数组]或者[指针]传给函数,那函数的参数该如何设计呢?

void test(int arr[])//数组传参,数组接受
{}
void test(int arr[10])//和上面一样的,这里的10没有任何意义
{}
void test(int *arr)//实参arr是首元素的地址,是int,这里也是int,用指针来接收
{}
void test2(int *arr[20])//实参arr2是一个整形指针数组,存放int*的数组,正好指针的数组接收
{}
void test2(int **arr)//实参arr2传过来是第一个int*的地址,实际上是取了一个一级指针的地址,所以可以用二级指针接收
{}
int main()
{
    int arr[10] = {0};
    int *arr2[20] = {0};
    
    test(arr);
    test2(arr2);

    return 0;
}

 一维数组传参

void print(int* p,int sz)
{
    int i = 0;
    for(i=0; i<sz; i++)
    {
        printf("%d ",*(p+i));
    }
    printf("\n");
}

int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int* p = arr;
    int sz = sizeof(arr) / sizeof(arr[0]);
    print(p,sz);
    return 0;
}

二级指针传参

void test(int** p2)
{
    **p2 = 20;
}
int main()
{
    int a = 0;
    int* pa = &a;  //pa是一级指针
    int ** ppa = &pa;  //ppa是二级指针

    test(ppa);
    test(&pa); //传一级指针的变量的地址
    printf("%d\n",a);

    return 0;
}

六、函数指针

  • 函数指针:指向函数的指针,存放函数地址的指针~
  • 数组名 != &数组名 数组名是数组首元素地址,&数组名是整个数组的地址
  • 函数名 == &函数名
int Add(int x,int y)
{
    return x + y;
}
int main()
{
    int a = 0;
    int* pa = &a;

    char ch = 'w';
    char* pc = &ch;

    int arr[10] = {0};
    int (*parr)[10] = &arr;

    //函数指针 - 存放函数地址的指针
    int (*pf)(int, int) = &Add;

    printf("%p\n",&Add);
    return 0;
}
int Add(int x,int y)
{
    return x + y;
}
int main()
{
    //函数指针 - 存放函数地址的指针
    //int (*pf)(int, int) = &Add;
    int (*pf)(int, int) = Add;
    int ret = (*pf)(3,5);
    int ret = pf(3,5);

    printf("%d\n",ret);
    return 0;
}

七、函数指针数组

存放函数指针的数组

int Add(int x,int y)
{
    return x + y;
}
int Sub(int x,int y)
{
    return x - y;
}
int main()
{
    int (*pf1)(int ,int) = Add;
    int (*pf2)(int ,int) = Sub;

    int (*pfarr[2])(int, int) = {Add,Sub}; //pfarr:函数指针数组

    return 0;
}

举例:

首先按以前的方法写一个加减乘除的计算器

void menu()
{
    printf("****************************\n");
    printf("**  1.add     2.sub  *******\n");
    printf("**  3.mul     4.div  *******\n");
    printf("*******  0.exit  ***********\n");
    printf("****************************\n");
    printf("****************************\n");
}
int Add(int x, int y)
{
    return x + y;
}
int Sub(int x, int y)
{
    return x - y;
}
int Mul(int x, int y)
{
    return x * y;
}
int Div(int x, int y)
{
    return x / y;
}

int main()
{
    int x = 0;
    int y = 0;
    int input = 0;
    do{
        menu();
        printf("请选择>:\n");
        scanf("%d",&input);
        switch(input)
        {
        case 1:
                printf("请输出2个操作符>:\n");
                scanf("%d %d",&x,&y);
                printf("%d\n",Add(x,y));
                break;
        case 2:
                printf("请输出2个操作符>:\n");
                scanf("%d %d",&x,&y);
                printf("%d\n",Sub(x,y));
                break;
        case 3:
                printf("请输出2个操作符>:\n");
                scanf("%d %d",&x,&y);
                printf("%d\n",Mul(x,y));
                    break;
        case 4:
                printf("请输出2个操作符>:\n");
                scanf("%d %d",&x,&y);
                printf("%d\n",Div(x,y));
                break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("输入错误,重新输出\n");
            break;
        }
    }while(input);
    return 0;
}

利用指针数组

void menu()
{
    printf("****************************\n");
    printf("**  1.add     2.sub  *******\n");
    printf("**  3.mul     4.div  *******\n");
    printf("*******  0.exit  ***********\n");
    printf("****************************\n");
    printf("****************************\n");
}
int Add(int x, int y)
{
    return x + y;
}
int Sub(int x, int y)
{
    return x - y;
}
int Mul(int x, int y)
{
    return x * y;
}
int Div(int x, int y)
{
    return x / y;
}
int main()
{
    int input = 0;
    do
    {
        menu();
        int x = 0;
        int y = 0;
        int (*pfarr[5])(int,int) = {NULL,Add,Sub,Mul,Div};
        printf("请选择>:\n");
        scanf("%d",&input);
        if(input>=1 && input<=4)
        {
            printf("请输入2个操作符>:\n");
            scanf("%d %d",&x,&y);
            printf("%d\n",(pfarr[input](x,y)));
        }
        else if(input == 0)
        {
            printf("退出程序\n");
            break;
        }
        else
        {
            printf("输入错误\n");
        }
    }while(input);
    return 0;    
}
int arr[5];         //整形数组
int (*p1)[5] = &arr;//*p1说明这是一个指针,(*p1)[5]:一个指向数组的指针,这个数组有5个元素,每个元素的返回类型是int

int* arr[5];  //整形指针的数组,arr[5]:这是一个数组,数组有5个元素,每个元素的返回类型是int*
int* (*p2)[5] = &arr;// *p2:说明是一个指针,这个指针指向的是一个数组,这个数组有5个元素,每个元素的返回类型是int*
                     //也可以说成,它是一个指向【整形指针数组】的指针

//函数指针数组
//&函数指针数组
int(*p)(int,int);  //函数指针
int(*p2[4])(int,int); //它是一个数组,数组有4个元素,每个元素的返回类型是函数指针,所以它是要给函数指针数组
int(*(*p3)[4])(int,int) = &p2;//它是一个指针,指向一个数组,这个数组有4个元素,每个元素是一个函数指针数组
                                  //p3是一个指向【函数指针数组】的指针

八、回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

例如:

void menu()
{
    printf("****************************\n");
    printf("**  1.add     2.sub  *******\n");
    printf("**  3.mul     4.div  *******\n");
    printf("*******  0.exit  ***********\n");
    printf("****************************\n");
    printf("****************************\n");
}
int Add(int x, int y)
{
    return x + y;
}
int Sub(int x, int y)
{
    return x - y;
}
int Mul(int x, int y)
{
    return x * y;
}
int Div(int x, int y)
{
    return x / y;
}
int Calc(int (*pf)(int,int ))
{
    int x = 0;
    int y = 0;
    printf("请输出2个操作符>:\n");
    scanf("%d %d",&x,&y);
    return pf(x,y);
}

int main()
{
    int input = 0;
    do{
        menu();
        printf("请选择>:\n");
        scanf("%d",&input);
        switch(input)
        {
        case 1:
                printf("%d\n",Calc(Add));
                break;
        case 2:
                printf("%d\n",Calc(Sub));
                break;
        case 3:
                printf("%d\n",Calc(Mul));
                    break;
        case 4:
                printf("%d\n",Calc(Div));
                break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("输入错误,重新输出\n");
            break;
        }
    }while(input);
    return 0;
}

再写一个冒泡排序

void bubble_sort(int arr[],int sz)
{
    int i = 0;
    for(i=0;i<=sz-1;i++)
    {
        int j = 0;
        for(j=0; j<sz-1-i ; j++)
        {
            if(arr[j] > arr[j+1])
            {
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}
void print(int arr[],int sz)
{
    int i = 0;
    for(i=0; i<sz; i++)
    {
        printf("%d ",arr[i]);
    }
    printf("\n");
}
int main()
{
    int arr[10] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);

    print(arr,sz);
    bubble_sort(arr,sz);

    print(arr,sz);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182549.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java ccflow 代码

草稿规则目录概述需求&#xff1a;设计思路实现思路分析1.URL管理参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,challenge Survive. happ…

盖子的c++小课堂——第十四讲:指针

前言 作者&#xff1a;大家好鸭&#xff0c;想必大家看到标题都有感到有一丝奇怪吧&#xff0c;其实&#xff0c;今天主要讲一些运算符 粉丝&#xff1a;啊……嗯嗯嗯 作者&#xff1a;那开始吧~~ 内存地址运算符& 粉丝&#xff1a;讲这个干嘛&#xff0c;我都会了~~ …

Jmeter场景组合测试——多个线程组的设计方案

我们绝大多数同学在使用jmeter进行性能测试时都会在一个线程组中完成测试工作&#xff0c;今天我来重点讲解一下jmeter多个线程组在测试中的应用&#xff0c;这也是关于jmeter性能测试面试过程中的进阶问题&#xff0c;希望能够帮到大家来解决工作中不同的测试需求。线程组中的…

Rust个人学习之有意思的所有权

在Rust中是没有内存垃圾回收机制(GC)的&#xff0c;那Rust是如何保障内存安全的呢&#xff1f;这就引出了“所有权”这个概念。 我们看下下面这段伪代码 let s "helloString"; t s; print(s); 在之前我们学习的语言中&#xff0c;比如C语言&#xff0c;对于上述伪…

人工智能原理复习 | 产生式系统

文章目录 一、概述二、八数码问题三、特殊的产生式系统四、一些补充CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 通过学习人工智能原理课程了解基本的人工智能问题的求解方法和原理。 一、概述 产生式系统(Production System):是构造知识型系统和建立认知模型时常用的知…

用canvas绘制微信小程序海报页面并保存相册-适用微信原生

微信小程序绘制海报并保存相册 tip&#xff1a;代码中使用的是uni的api 如果使用原生微信小程序开发&#xff0c;可以把uni更换成wx使用 文章目录微信小程序绘制海报并保存相册前言一、分析需求二、准备数据三、编码开始html部分解析&#xff1a;js部分1、准备好数据后开始绘制…

王道操作系统笔记(三)———— 处理机调度

文章目录一、调度的概念1.1 调度的基本概念1.2 调度的层次1.3 补充&#xff1a;七状态模型二、调度的时机、切换与过程2.1 进程调度的时机2.2 狭义进程调度、进程切换、广义进程调度2.3 进程切换的过程2.4 补充&#xff1a;闲逛进程三、进程的调度方式四、调度算法的评价指标五…

【通信原理(含matlab程序)】实验四 升余弦滚降基带传输

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; 本人持续分享更多关于电子通信专业内容以及嵌入式和单片机的知识&#xff0c;如果大家喜欢&#xff0c;别忘点个赞加个关注哦&#xff0c;让我们一起共同进步~ &#x…

【Novel AI】使用绘画AI构建unity游戏资源

请勿在商业用途中使用下面的示例图中的任何资源&#xff01; 1. 设想&#xff1a; 首先根据自己的设想&#xff0c;创造一些角色原型&#xff0c;角色特点等等 这里我根据网络上找的一些参考&#xff0c;我大概想要的是比较特别的一个警察的猫人形的动物角色&#xff1b; 2…

网络编程(TCP+UDP)(1)

咱们的一个TCP服务器&#xff0c;是否可以让一个UDP客户端连接上呢&#xff1f; 1)TCP和UDP&#xff0c;他们无论是API代码&#xff0c;还是协议底层的工作过程&#xff0c;都是差异巨大的&#xff0c;不是单纯的把流转化成数据包就可以的&#xff1b; 2)描述一次通信&#xff…

武器系统中的自主性(国防部指令3000.09)

批准:负责政策的国防部副部长办公室2023年1月25日可以公开发布。可在指令司网站https://www.esd.whs.mil/DD/查阅。国防部指令3000.09&#xff0c;“武器系统中的自主性”&#xff0c;2012年11月21日国防部副部长凯瑟琳希克斯(Kathleen H. Hicks)目的:本指令:•建立开发和使用武…

教育数字化转型 看低代码怎么构建实现

数字经济和数字社会的发展&#xff0c;推动教育培养目标和内容的发展与变革。经过教育信息化1.0和2.0的建设&#xff0c;我国数字技术与教育经历了起步、应用、融合、创新四个阶段&#xff0c;目前正处于融合与创新并存的时期。教育数字化教育数字化转型是教育信息化的特殊阶段…

C 程序设计教程(20)—— 数组和指针(三):数组与指针的关系

C 程序设计教程&#xff08;20&#xff09;—— 数组和指针&#xff08;三&#xff09;&#xff1a;数组与指针的关系 该专栏主要介绍 C 语言的基本语法&#xff0c;作为《程序设计语言》课程的课件与参考资料&#xff0c;用于《程序设计语言》课程的教学&#xff0c;供入门级用…

RESTful应用

AJAX技术 浏览器是多进程的&#xff0c;简单的说就是&#xff0c;浏览器每打开一个标签页&#xff0c;就相当于创建了一个独立的浏览器进程。但是js是基于单线程的&#xff0c;而这个线程就是浏览器的js引擎&#xff0c;浏览器无论在什么时候都只且只有一个线程在运行JavaScri…

sonic-ios-bridge(sib)性能监控之系统性能及应用性能

sib下载地址&#xff1a;Releases SonicCloudOrg/sonic-ios-bridge GitHub 可以看到最新版本为V1.3.7。下载到本地并解压后即可使用。 性能监控使用帮助&#xff1a;sib perfmon -h sib perfmon -f -j&#xff1a;以json格式化显示性能数据。 一、查看系统整体性能数据 查看…

10. POP3收取邮件

1. POP3协议 POP3&#xff0c;全名为“Post Office Protocol - Version 3”&#xff0c;即“邮局协议版本3”。是TCP/IP协议族中的一员&#xff0c;由RFC1939 定义。本协议主要用于支持使用客户端远程管理在服务器上的电子邮件。提供了SSL加密的POP3协议被称为POP3S。 POP 协议…

Kernel: sysctl: hung_task_panic、hung_task_check_count、hung_task_timeout_secs

文章目录 mutex 锁相关的一个例子这个和磁盘相关的一个例子hung_task_panic:hung_task_check_count:hung_task_timeout_secs:实例hung_task_warnings:相关的编译配置mutex 锁相关的一个例子 systemd-shutdown 卡在device-shutdown时调用的mutex-lock操作。 这个和磁盘相关的…

Android ANR触发机制及日志分析

1.ANR Application Not Responding&#xff0c;即应用程序未响应。Android系统要求一些事件在一定时间范围内完成&#xff0c;如果超过预定时间未得到有效响应或响应时间过长&#xff0c;就会造成ANR。 Android中有4种ANR发生场景&#xff1a; ①点击事件(按键和触摸事件&am…

PDF在线合并网页版有哪些?这几款你一定没用过

PDF在线合并网页版有哪些&#xff1f;很多人在工作中都需要给其他人发送一些重要文件&#xff0c;如果文件数量比较多的时候&#xff0c;就会出现耗时有耗力的情况&#xff0c;所以我们就需要想一个办法来解决问题&#xff0c;那就是将多个PDF文件进行合并&#xff0c;我们需要…

MATLAB算法实战应用案例精讲-【数模应用】概率生成模型(Generative Model)

前言 知识储备 表征学习 背后的核心思想representation learning ,不是试图直接对高维样本空间建模,而是使用一些低维潜在空间来描述训练集中的每个观察,然后学习一个映射函数,该函数可以在潜在空间中取一个点,将其映射到原始域中的一个点。换句话说,潜在空间中的每个…