C语言:指针(第三天)

news2024/12/17 6:13:32

C语言:指针(第三天)

字符数组和字符指针

字符串实现

在C语言中,表示一个字符串有以下两种形式:

  1. 用字符数组存放一个字符串。
  2. 用字符指针指向一个字符串。
案例
#include <stdio.h>

/**
 * 方式1:使用字符数组实现字符串
 */
void str_test1()
{
    // 定义一个伪字符串
    char str[] = "I Love You";
    printf("%s\n",str);
}
/**
 * 方式2:使用字符指针实现字符串
 */
void str_tewt2()
{
    // 定义一个伪字符串
    char *str = "I Love You";
    printf("%s\n",str);
}

int main(int argc,char *argv[])
{
    str_test1();
    str_tewt2();
    return 0;
}

注意:字符数组和字符指针变量都能实现字符串的存储与运算。(字符指针–> 字符类型的指针变量)

字符数组和字符指针的联系

  • 字符数组由元素组成,每个元素中存放一个字符,而字符指针变量中存放的是地址,也能作为函数参数。

  • 只能对字符数组中的各个元素赋值,而不能用赋值语句对整个字符数组赋值。

    char arr[3];
    arr[2] = 'A';// 正确,对字符数组中的元素赋值
    
    arr = {'A','B','C'};// 错误,(可以理解为数组名就是一个常量,一旦创建就不能再改变)
    
  • 字符数组名虽然代表地址,但数组名的值不能改变,因为数组名是常量。

  • 对于字符串中字符的存取,可以用下标法,也可以用指针。

    案例
    #include <stdio.h>
    
    /**
     * 方式1:使用字符数组实现字符串
     */
    
    int main(int argc,char *argv[])
    {
    	// 使用两种方式定义字符串
        char str1[] = "你好,张欣!"char *str2 = "你好,张欣!"// 我们将数据类型为char的指针变量称为字符指针
        
        // 测试赋值
        // str1 = "您好,张鹏!";// 不能对数组整体赋值,如果要赋值,请使用string库下的strcpy()
        str2 = "您好,张鹏!";
        
        printf("%s,%s\n",str1,str2);
    
        return 0;
    }
    

字符串作为形式参数

  • 实参与形参都可以是字符数组

    void fun(char str[],int len){..}
    
    void main()
    {
        char str[] = "hello";
        fun(str,sizeof(str) / sizeof(str[0]));
    }
    
  • 实参用字符数组,形参用字符指针

    void fun(char *str,int len){
    	str[2] = 'A';// Gcc编译环境可通过
    }
    
    void main()
    {
        char str[] = "hello";// 常量池,此时的赋值,将常量池中的数据读取出来,存入到栈中数组对应的位置
        fun(str,sizeof(str) / sizeof(str[0]));
    }
    
  • 实参和形参都是指针变量(在函数内部不能对字符串中的字符做修改)

    void fun(char *str,int len){
    	str[2] = 'A';错误,字符串常量一旦创建,就不能改变
    }
    
    void main()
    {
        char *str = "hello";
        fun(str,sizeof(str) / sizeof(str[0]));
    }
    
  • 实参是指针类型,形参是字符数组(在函数内部不能对字符串中的字符做修改)

    void fun(char str[],int len){
    	str[2] = 'A';错误,字符串常量一旦创建,就不能改变
    }
    
    void main()
    {
        char *str = "hello";
        fun(str,sizeof(str) / sizeof(str[0]));
    }
    

注意:

  1. 字符数组在创建的时候,会在内存中开辟内存空间,内存空间可以存放字符数据;字符指针在创建的时候,需要依赖于字符数组,字符指针在内存开辟的内存空间中,存放的是数组元素的地址。字符指针依的创建依赖于字符数组,字符数组可以独立存在,而字符指针不能独立存在。
  2. 字符数组可以初始化,但是不能赋值;字符指针可以初始化,也可以赋值。
案例
#include <stdio.h>

/**
 * 字符指针作为函数参数:用函数调用实现字符串的复制以及长度计算。
 * 定义一个函数,实现字符串的拷贝,返回字符串的长度
 * @param source 拷贝的源字符串
 * @param target 需要保存拷贝数据的目标字符串
 * @return 字符串的大小
 */

int str_copy(char *source,char *target)
{
    // 定义一个循环变量
    int i = 0;
    
    while(source[i] != '\0')
    {
        // 实现拷贝
        *(target+i) = *(source+i);// 指针法
        // target[i] = source[i];下标法
        
        i++;
    }
    // 拷贝结束后,一定要给target末尾加上\0
    target[i] = '\0';
    
    return i;
        
}

int main()
{
    // 定义两个数组,从键盘录入字符串
    char source[20],target[20];
    
    printf("请输入一个字符串:\n");
    scanf("%s",source);
    
    int len = str_copy(source,target);
    
    printf("%s,%s,%d\n",source,target,len);
    return 0;
}
案例
/**
 * 字符指针作为函数的参数,给定一个字符串,截取start到end之间的字符串,含头不含尾
 * 定义一个函数,实现字符串的截取
 * @param source 源字符串
 * @param start 开始截取的位置
 * @param end 截取结束的位置
 * @param target 截取后的字符串
 * @return 新字符串的长度
 */
#include <stdio.h>

int str_split(char *source,int start,int end,char *target)
{
    // 定义一个循环变量
    int i = 0,k = 0;
    
    // 遍历源字符串(数组)
    while(source[i] != '\0')
    {
        // 根据位置截取
        if(i >= start && i < end)
        {
            // 将截取的字符串存入target
            target[k] = source[i];
            k++;
        }
        i++;
    }
    // 新字符串需要末尾添加\0
    target[k] = '\0';
    
    return k;
}
int main()
{
    char *str = "abcdefg";
    char target[100];
    int len = str_split(str,2,5,target);
    printf("%s,%s,%s\n",str,target,len);
    
    return 0;
}

函数指针与指针函数

函数指针

定义:函数指针本质上是指针,它是函数的指针(定义了一个指针变量,变量中存储了函数的地址)。函数都有一个入口地址,所谓指向函数的指针,就是指向函数的入口地址。这里函数名就代表入口地址

函数指针存在的意义:

  1. 让函数多了一种调用方式
  2. 函数指针作为形参,可以形式调用(回调函数)

定义格式:

返回值类型 (*变量名)(形式参数列表)

举例:

int (*p)(int a,int b);

函数指针的初始化:

  1. 定义的同时赋值

    // 函数指针需要依赖于函数,先有函数,再有指针
    
    // 定义一个普通的函数
    int add(int a,int b){ return a +b; }
    
    // 定义一个函数指针,并给他赋值
    // 通过以下代码我们发现:函数指针的返回类型和依赖函数的返回类型一致,函数指针的参数个数类型和依赖函数一致
    int (*p)(int a,int b) = add;// 赋值一定要注意:函数不能带有()小括号
    
  2. 先定义后赋值

    // 定义一个普通的函数
    int add(int a,int b){ return a +b; }
    
    // 定义一个函数指针
    int (*p)(int,int);// 一般写作这种
    
    // 给函数指针赋值
    p = add;
    

    注意:

    1. 函数指针指向的函数要和函数指针定义的返回值类型,形参列表对应,否则编译报错
    2. 函数指针是指针,但不能指针运算,如p++等,没有实际意义
    3. 函数指针作为形参,可以形成回调
    4. 函数指针作为形参,函数调用时的实参只能是与之对应的函数名,不能带小括号
    5. 函数指针的形参列表中的变量名可以省略
案例
/**
 * 函数指针案例,指向函数的指针变量就是函数指针
 * 求a,b两个数的最大值
 * 定义一个函数指针
 */

#include <stdio.h>

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

int main(int argc,char *argv[])
{
    // 定义测试数据
    int a = 3,b = 2,c;
    
    // 直接函数调用
    c = max(a,b);
    printf("%d,%d两个数中的最大值是:%d\n",a,b,c);
    
    // 定义一个函数指针
    int (*p)(int,int) = max;
    // 间接函数调用
    c = p(a,b);
    printf("%d,%d两个数中的最大值是:%d\n",a,b,c);
    
    c = (*p)(a,b);
    printf("%d,%d两个数中的最大值是:%d\n",a,b,c); 
    
    return 0;
}

回调函数(了解)

概念

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

为什么要用回调函数
  1. 因为可以把调用者和被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。
  2. 简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法
实现
/**
 * 函数指针的特殊用法-回调函数
 * 回调函数1
 */
int callback_1(int a)
{
    printf("hello,this is callback_1:a=%d\n",a);
    return 0;
}

/**
 * 回调函数2
 */
int callback_2(int b)
{
    printf("hello,this is callback_2:b=%d\n",)b;
}

/**
 * 实现回调函数
 */

int handle(int x,int (*callback)(int))
{
    printf("开始执行!\n");
    
    callback(x);
    
    printf("执行结束!\n");
}

int main(int argc,char *argv[])
{
    handle(100,callback_1);
    handle(200,callback_2);
    return 0;
}

指针函数

定义:本质上是函数,这个函数的返回值类型是指针,这个函数称为指针函数。(应用场景:需要返回数组的时候)

语法:

指针类型 函数名(形参列表)
{
    函数体;
    return 指针变量;
}

举例:

// int *get(int a)
int* get(int a)
{
    int *b = &a;
    return b;
}

注意:

在函数中不要直接返回一个局部变量的地址,因为函数调用完毕后,局部变量会被回收,使得返回的地址就不明确。

解决方案:

如果非要访问,可以给这个局部变量添加static,可以延长它的生命周期,从而避免野指针(尽量少用,因为存在内存泄漏)

/**
 * 指针函数-有若干个学生的成绩(每个学生有4门成绩),要求在用户输入学生序号后,能输出该学生的全部成绩,用指针函数来实现
 */

#include <stdio.h>

/**
 * 定义一个函数,传入学生的序号,返回这个学生的所有课程成绩
 * @param p 二维数组
 * @param n 学生索引(二维数组中的行号)
 * @return 学生成绩(行号对应的列数组)
 */

float *search(float (*p)[4],int n)
{
    // 定义一个指针,用来接收查询到的某个学生的所有课程
    float *pt;
    pt = *(p+n) // *(p+n),*p[n],p[n]
    return pt;
}

int main()
{
    // 准备一个二维数组
    float score[][4] = {60,70,80,89},{55,66,77,88},{90,89,90,91};
    
    int m;
    float *p;
    printf("请输入学生序号(0~2):\n");
    scanf("%d",&m);
    printf("第%d个学生的成绩:\n",m);
    
    // 用来接收某个学生的所有成绩
    p = search(score,m);
    
    // 遍历成绩
    for(int i = 0,i < 4,i++)
    {
        printf("%5.2f\t",*(p+i));
    }
    printf("\n");
    
    return 0;
}

二级指针

说明:指针除了一级指针,还有多级指针,但是我们一般开发中最多用到二级指针。三级指针本质上用法和二级指针差不多。

定义:二级指针,又被称之为多重指针,引用一级指针的地址,此时这个指针变量就得定义成二级指针。

int a = 10;   // 普通变量
int *p = &a;  // 一级指针
int **w = &p; // 二级指针
int ***x = &w;// 三级指针

定义格式:

数据类型 **变量名 = 指针数组的数组名或者一级指针的地址

举例:

// 指针数组
int arr = {11,22,33};
int *arr_ = {&arr[0],&arr[1],&arr[2]};

// 一级指针
int a = 10;
int *p = &a;

// 二级指针和指针数组
// 字符型指针数组,本质上是一个二维的char数组
char *str[3] = {"abc","aaa034","12a12"};
// 如果要用一个指针变量来接收,就需要一个二级指针
char **p_ = str;// str指向的是首元素的地址
// 二级指针和二维数组
int arr[2][3] = {{1,2,3},{11,22,33}};
int **k = arr;// 编译报错,数据类型不相符(二维数组不等于二级指针)
int a = 90;// 变量
int *p = &a;// p存放a地址
int **k = &p;// k存放p的地址

printf("%p,%p,%d\n",k,*k,**k);// p->p的地址,*k->p的值->a的地址,**k->a的地址-a的值

结论:

  1. 二级指针和指针数组是等效,和二维数组不等效。
  2. 二维数组和数组指针是等效,和二级指针不等效。

二级指针的用法:

  1. 如果是字符的二级指针,可以像遍历字符串数组一样遍历它
  2. 如果是其他的二级指针,就需要解引用两次访问它所指向的数据
案例
/**
 * 二级指针案例:使用指向指针数组的指针变量。
 */
#include <stdio.h>
void fun1()
{
    // 字符指针
    char *name[] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"};
    // 定义一个二级指针(字符的二级指针)
    char **p;
    // 定义循环变量
    int i = 0;
    // 遍历指针数组
    do
    {
        p = name + i;
        printf("%s\n", *p);
        i++;
    } while (i < 5);
    printf("\n");
}
void fun2()
{
    int arr1[5] = {11, 12, 13, 14, 15};
    // 创建一个指针数组
    int *arr[] = {&arr1[0], &arr1[1], &arr1[2], &arr1[3], &arr1[4]};
    int **p = arr, i = 0;
    // 遍历
    for (; i < 5; i++)
    {
        // printf("%5d",**(p+i));
        printf("%5d", **p);
        p++;
    }
    printf("\n");
}
int main()
{
    fun1();
    fun2();
}

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

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

相关文章

RK3576 Android14,内存大于4G时UVC应用无法申请内存

最近有个项目需要将Linux虚拟成UVC摄像头&#xff0c;开发过程中遇到一个奇怪的事情&#xff0c;通过V4l2框架接口申请内存时&#xff0c;相同的板子&#xff0c;只是内存一个4G一个8G。4G的内存可以申请成功&#xff0c;8G就不行。提示“内存不足” 内存更大反而内存不足&…

TimesFM(Time Series Foundation Model)时间序列预测股市价格的数据研究(4)

TimesFM&#xff08;Time Series Foundation Model&#xff09;时间序列预测的数据研究(3)-CSDN博客文章浏览阅读846次&#xff0c;点赞19次&#xff0c;收藏12次。1. **表示预测区间**&#xff1a;在很多预测任务中&#xff0c;模型给出的不只是一个单一的预测值&#xff08;比…

opencv所有常见函数

一、opencv图像操作 二、opencv图像的数值运算 三、opencv图像的放射变换 四、opencv空间域图像滤波 五、图像灰度化与直方图 六、形态学图像处理 七、阈值处理与边缘检测 八、轮廓和模式匹配

常见漏洞—SSRF_FastCGI

FastCGI协议 简介 Fast CGI源自旧版本的CGI 路由/结构图 # 访问url --> 浏览器生成HTTP请求报文 --> web server解析请求&#xff08;例如nginx&#xff09; web server 是内容的分发者 当访问静态页面时&#xff0c;web server 会直接返回资源&#xff0c;例如index.htm…

【游戏设计原理】10 - 科斯特的游戏理论

科斯特的游戏理论强调了游戏与学习之间的关系&#xff0c;认为“玩得开心”与“学习”是紧密相连的。换句话说&#xff0c;游戏的核心魅力在于通过适当的挑战和不断的学习进程激发玩家的内啡肽循环&#xff0c;这让玩家在不断的探索和进步中找到乐趣。 科斯特的理论通过游戏是…

ES-IndexTemplate和DynamicTemplate

IndexTemplate 什么是IndexTemplate 索引模板&#xff0c;帮助你设定Mappings和Settings&#xff0c;并按照一定的规则&#xff0c;自动匹配到新创建的索引之上 模板仅在一个索引被新建的时候&#xff0c;才会产生应用&#xff0c;索引被修改不会影响已创建的索引可以设定多…

【大语言模型】ACL2024论文-27 Mementos:一个全面的多模态大型语言模型在图像序列推理上的基准测试

【大语言模型】ACL2024论文-27 Mementos&#xff1a;一个全面的多模态大型语言模型在图像序列推理上的基准测试 目录 文章目录 【大语言模型】ACL2024论文-27 Mementos&#xff1a;一个全面的多模态大型语言模型在图像序列推理上的基准测试目录文章摘要研究背景问题与挑战如何…

CSS基础与应用详解

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Css篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Css篇专栏内容:CSS基础与应用详解 前言 CSS&#xff08;层叠样式表&#xff09;是网页设计中不可或缺的一部分&am…

C/S软件授权注册系统(Winform+WebApi+.NET8+EFCore版)

适用软件&#xff1a;C/S系统、Winform桌面应用软件。 运行平台&#xff1a;Windows .NETCore&#xff0c;.NET8 开发工具&#xff1a;Visual Studio 2022&#xff0c;C#语言 数据库&#xff1a;Microsoft SQLServer 2012&#xff0c;Oracle 21c&#xff0c;MySQL8&#xf…

国标GB28181网页直播平台EasyGBS国标EasyGBD对讲音频demo

近年来&#xff0c;随着信息技术的飞速发展&#xff0c;视频监控领域正经历从传统安防向智能化、网络化安防的深刻转变。在此过程中&#xff0c;GB28181标准凭借其强大的功能和灵活性&#xff0c;成为了推动视频监控系统互联互通和高效管理的重要一环。通过支持GB28181协议&…

session 共享服务器

1.安装 kryo-3.0.3.jar asm-5.2.jar objenesis-2.6.jar reflectasm-1.11.9.jar minlog-1.3.1.jar kryo-serializers-0.45.jar msm-kryo-serializer-2.3.2.jar memcached-session-manager-tc9-2.3.2.jar spymemcached-2.12.3.jar memcached-session-manager-2.3.2.jar …

【蓝桥杯国赛真题15】python质因数个数 蓝桥杯青少年组python编程国赛真题详细解析

目录 python质因数个数 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python质因数个数 第十二届蓝桥杯青少年组python比赛国赛真题详细解析 …

智能硬件「百团大战」:AI驱动的周期来了吗?

要想在竞争激烈的市场中打造出真正的AI硬件“爆款”&#xff0c;并非简单地在现有硬件上堆砌AI功能就能实现&#xff0c;而是需要深刻理解AI的本质&#xff0c;用AI技术从底层逻辑出发&#xff0c;彻底重塑硬件产品的设计、功能与用户体验。 作者|斗斗 编辑|皮爷 出品|产…

Linux核心概念与常用命令

文章目录 一、Linux概述1、常见的操作系统2、Linux发展史3、Linux目录结构 二、文件和目录操作1、pwd - 显示当前目录2、cd - 切换目录3、ls - 列出目录内容4、mkdir - 创建目录5、touch - 创建空文件6、cp - 复制文件或目录7、mv - 移动或重命名文件8、rm - 删除文件或目录9、…

uniappp配置导航栏自定义按钮(解决首次加载图标失败问题)

1.引入iconfont的图标&#xff0c;只保留这两个文件 2.App.vue引入到全局中 import "./static/fonts/iconfont.css"3.pages.json中配置text为图标对应的unicode {"path": "pages/invite/invite","style": {"h5": {"…

vue组件开发:构建响应式快捷导航

前言 快捷导航不仅能够显著提升系统的灵活性和用户交互性&#xff0c;还极大地增强了用户的操作体验。本文将展示如何在 vue 中实现一个既可自定义又具备响应式特性的快捷导航菜单。 一、实现思路 列表页 结构设计 定义页面结构&#xff0c;包含一个导航卡片和一个对话框组件&a…

基于 Spring Boot 实现图片的服务器本地存储及前端回显

??导读&#xff1a;本文探讨了在网站开发中图片存储的各种方法&#xff0c;包括本地文件系统存储、对象存储服务&#xff08;如阿里云OSS&#xff09;、数据库存储、分布式文件系统及内容分发网络&#xff08;CDN&#xff09;。文中详细对比了这些方法的优缺点&#xff0c;并…

深入了解IPv6——光猫相关设定:DNS来源、DHCPv6服务、前缀来源等

光猫IPv6设置后的效果对比图&#xff1a; 修改前&#xff1a; 修改后&#xff1a; 一、DNS来源 1. 网络连接 来源&#xff1a; 从上游网络&#xff08;如运营商&#xff09;获取 IPv6 DNS 信息&#xff0c;通过 PPPoE 或 DHCPv6 下发。 特点&#xff1a; DNS 服务器地址直…

欧科云链研究院:AI时代,如何证明“我是我”?

OKG Research&#xff5c;编辑 近日&#xff0c;OpenAI 发布了新模型 Sora。这是一款高性能的文本到多模态生成工具&#xff0c;支持从文本生成精细的图像和动态视频。 相较早先发布的视频样例&#xff0c;该功能目前已经可以由用户真实上手体验&#xff0c;目前由于服务过载…

Cesium进阶教程——自定义图形、外观、绘图基础、现有着色器移植至Cesium、ShadowMapping、视频GIS、模型压平、卷帘

基础必看 WEBGL基础&#xff08;从渲染管线角度解读&#xff09; 参考路线 http://www.xt3d.online/tutorial/further/article.html 自定义图形 https://blog.csdn.net/m0_55049655/article/details/138908327 https://blog.csdn.net/m0_55049655/article/details/140306837 …