C语言-指针_02

指针-02

1. 指针的指针

概念:指针变量中存储的是指针的地址,又名 二维指针

语法:

数据类型 **p;

示例:

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int num = 10;
    int *p1 = &num;
    int **p2 = &p1;

    printf("*p1是: %d\n", *p1);         //*p1是: 10
    printf("p1的值是: %p\n", p1);       //p1的值是: 0x7ffd1a6bff0
    printf("p1的地址是: %p\n", &p1);    //p1的地址是: 0x7ffd1a6bff08
    printf("p2的值是: %p\n", p2);       //p2的值是: 0x7ffd1a6bff08
    printf("p2的地址是: %p\n", &p2);    //p2的地址是: 0x7ffd1a6bff10
    return 0;
}

在这里插入图片描述

2. 指针与const

2.1 指针常量 - (int * const p)

概念:

本质是一个常量

  • 该指针常量 不能修改指向的地址(就是不能指向其他变量)
  • 但是可以修改指向地址中的值

注意:数组就是一个指针常量。

语法:

数据类型 * const 指针名称

例:

void fun01()
{
    int num = 10;
    // int *p = &num;
    // int num02 = 20;
    // p = &num02;
    // printf("p = %p\n", p);

    //指针常量
    //p被const修饰,
    int * const p = &num;
    int num03 = 30;
    // p = &num03;  //指针指向的地址不可修改
    *p = 40;        //但是指针指向的地址的值可以修改
    printf("*p = %d\n", *p); //*p = 40
}

2.2 常量指针 - (const int *p)

概念:

指向常量(值不可修改)指针,本质是一个指针

  • 不能修改其指向地址的值 ,也就是 不能通过指针改变其指向的地址中的数据
  • 但是可以修改其指向的地址,也就是 可以改变其存储的地址

例:

void fun02()
{
    int num = 10;
    //常量指针
    //指针被const修饰,
    // int const *p = &num;
    const int *p = &num;
    printf("p修改前的地址%p\n", p);  //p修改前的地址0x7fff0b95ff58
    int num04 = 30;
    p = &num04;  //指针指向的地址可以修改
    // *p = 40;  //但是指针指向的地址的值不可以修改
    printf("p修改后的地址%p\n", p);  //p修改后的地址0x7fff0b95ff5c
}

2.3 常量指针常量 - (const int * const p)

概念:

指向的地址 和 只想地址中的 都不可以修改

语法:

数据类型 const * const 指针名称
或 
const 数据类型 * const 指针名称

例:

void fun03()
{
    int num = 10;
    //常量指针常量
    //指针和指针变量p都被const修饰,
    const int * const p = &num;
    int num02 = 40;
    // p = &num02; //指针指向的地址不可以被修改
    // *p = 50;    //指针指向的地址的值也不可以修改
}

3. 指针与数组元素

概述:

数组:是多个相同类型的变量的集合,每个变量占内存空间,都有 地址编号指针变量 当然 可以存放数组元素的地址

注意:

  1. 只有 两个相同类型 的指针指向 同一个数组的元素 的时候,比较大小才有意义;

  2. 指向前面元素 的指针 小于 指向 后面元素 的指针

  3. 数组就是数组中存储的 第一个元素的地址

  4. c语言中数组的本质是一个 指针常量

    • 不可以修改指针的地址,
    • 但是可以修改该指针指向地址的值

示例1:

void fun01()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p = &nums[3];

    printf("*p = %d\n", *p);       //*p = 7
    printf("*(p+1) = %d\n", *(p+1));       //*(p+1) = 9
    printf("*(p+2) = %d\n", *(p+2));       //*(p+2) = 2
    printf("*(p-1) = %d\n", *(p-1));       //*(p-1) = 5
    printf("*(p-2) = %d\n", *(p-2));       //*(p-2) = 3

    for (int i = 0; i < 10; i++)
    {
        printf("%p\n", &nums[i]);
    }
}

/*
0x7ffc007c1610
0x7ffc007c1614
0x7ffc007c1618
0x7ffc007c161c
0x7ffc007c1620
0x7ffc007c1624
0x7ffc007c1628
0x7ffc007c162c
0x7ffc007c1630
0x7ffc007c1634
*/

示例2:

void fun02()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p = &nums[0];
    for (int i = 0; i < 10; i++)
    {
        printf("nums[%d] = %d\n", i, *(p++));
    }
}

/*
nums[0] = 1
nums[1] = 3
nums[2] = 5
nums[3] = 7
nums[4] = 9
nums[5] = 2
nums[6] = 4
nums[7] = 6
nums[8] = 8
nums[9] = 10
*/

示例3:

void fun03()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p1 = &nums[0];
    int *p2 = &nums[1];
    if (p1 < p2)
    {
        printf("p1在p2的前面\n");
    }
    else{
        printf("p1在p2的后面\n");
    }
}
//p1在p2的前面

示例4:

void fun04()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    //数组名就是数组中存储的第一个元素的地址
    printf("nums = %p\n",nums);         //nums = 0x7ffe6b558c30
    printf("nums[0]= %p\n",&nums[0]);   //nums[0]= 0x7ffe6b558c30
}

示例5:

void fun05()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p = nums;

    for (int i = 0; i < 10; i++)
    {
        // printf("nums[%d] = %d\n", i, *(p++));
        // printf("nums[%d] = %d\n", i, *(p+i));
        // printf("nums[%d] = %d\n", i, nums[i]);
        // printf("nums[%d] = %d\n", i, p[i]);
        printf("nums[%d] = %d\n", i, *(nums+i));

        //c语言中数组的本质是一个 指针常量
        //不可以修改指针的地址,但是可以修改该指针指向地址的值
        // printf("nums[%d] = %d\n", i, *(nums++));  //报错
    }
    
}

4. 指针与数组

4.1 数组名

数组名 其实是数组中 首元素的地址 。

例:

void fun04()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    //数组名就是数组中存储的第一个元素的地址
    printf("nums = %p\n",nums);         //nums = 0x7ffe6b558c30
    printf("nums[0]= %p\n",&nums[0]);   //nums[0]= 0x7ffe6b558c30
}

4.2 数组指针

数组名可以赋值给一个指针变量,此时该指针指向一个数组,被称为 数组指针,其本质是一个 指针

语法:

  • 指向一维数组语法
    • 数据类型 * 指针名;
  • 指向二维数组指针的语法
    • 数组类型 (*指针名)[二维数组中一维数组的长度];
  • 指向三维数组指针的语法
    • 数组类型 (*指针名)[三维数组中二维数组的长度][二维数组中一维数组的长度];

示例:

void fun06()
{
    //数组指针
    //概念:指向数组的指针,本质是一个指针
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p1 = nums;

    int nums02[2][4] = {1,2,3,4,5,6,7,8};
    int (*p2)[4] = nums02;

    int nums03[2][3][4] = {0};
    int (*p3)[3][4] = nums03;
}

4.3 指针数组

指针变量是一个变量,数组是可以存储多个类型形容的变量的勇容器,故 数组可以存储多个指针变量,此时该数组 被称为 指针数组,其本质是一个 数组

语法:

数据类型 * 数组名[长度];

例:

void fun07()
{
    int a = 10,b = 20,c = 30;
    int *p1 = &a;
    int *p2 = &b;
    int *p3 = &c;
    //指针数组
    //存储指针的数组,本质是一个数组
    int *nums[3] = {p1, p2, p3};
}

4.4 字符数组与字符串指针的区别

字符数组:内存(栈、静态全局区)中开辟了一段空间存放字符串,将其首元素地址赋值给 数组名,是个 指针常量

字符串指针:

  • 如果指向 在 文字常量区 中存放 字符串,会将 字符串首地址赋值给 指针变量,此时该指针是个 常量指针

  • 如果指向 栈、静态全局区中存放的字符数组,那么则是一个 普通的指针变量,值和地址都可以修改。

    示例1:

void fun08()
{
    //str01的内存地址在栈区
    //str01是一个数组,所以是一个指针常量
    //指针常量可以修改其地址中的值
    //指针常量不可以修改其指向的地址
    char str01[] = "hello";
    str01[0] = 'H';
    printf("str01=%s\n",str01);     //str01=Hello
    //str01 = "world";			//报错,不能指向另外的地址
    
    //str02的内存地址在文字常量区
    //常量指针
    //指针指向的地址可以修改
    //指针指向的地址中的元素不可修改
    char * str02 = "hello";
    printf("str02修改前的地址%p\n",str02);      //str02修改前的地址0x400d28
    str02 = "world";
    printf("str02修改后的地址%p\n",str02);      //str02修改后的地址0x400d4
    //str02[0] = 'W';     //段错误 (核心已转储)
}

示例2:

void fun09()
{
    //字符数组
    char str01[] = "hello";
    //指向 栈、静态全局 区中存放的字符数组
    char *str03 = str01;
    printf("str03修改内容前%s\n",str03);  //tr03修改内容前hello
    str03[0] = 'H';
    printf("str03修改内容后%s\n",str03);  //str03修改内容后Hello

    str03 = "world";  
    printf("str03修改地址后%s\n",str03);  //str03修改地址后world
}

示例3:

void fun10()
{
    char names[][50] = {"boboy","tom","jerry","zhangsan","lisi"};
    //指针数组:存储的是这些值的首地址
    char *ns[50] = {"boboy","tom","jerry","zhangsan","lisi"};
    //数组指针
    // char (*nss)[50] = names;
    char (*nss)[50] = {"boboy","tom","jerry","zhangsan","lisi"};
}

5. 函数与指针

5.1 函数名就是函数地址

函数名本身就是函数在代码区存储该函数的地址,故 可以赋值给一个指针变量,但是因为其在代码区的地址不可修改,所以该指针是一个指针常量

语法:

  • 函数指针的定义初始化

    返回值类型 (*指针名称)(指向的函数的形参列表的数据类型) = 函数名;
    

    注意:

    • 指向的函数的形参列表的数据类型可以忽略不写
  • 调用其指向的函数

    指针名(实参列表);
    
    变量名 = 指针名(实参列表);
    

    注意:

    • 实参列表可以忽略不写

示例1:函数名本身就是函数在代码区存储 该函数的地址

#include <stdio.h>
void test01()
{
    printf("test01被调用了!");
}
void fun01()
{
    printf("%p\n", test01);  //0x400526
}

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

示例2:调用其指向的函数

#include <stdio.h>
void test01()
{
    printf("test01被调用了!\n");
}

void fun02()
{
    /*
        函数指针的书写方式
        返回值类型 (*指针变量名)(形参列表)
    */
    //p1 == test01
    //调用函数:test01(实参列表);
    void (*p1)() = test01; 
    p1();       //test01被调用了!
}

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

示例3:指向的函数的形参列表的数据类型可以忽略不写

void add(int a, int b)
{
    printf("sum = %d\n", a+b);
}

void fun03()
{
    void (*p1)(int, int) = add;
    p1(10, 20);     //sum = 30
}

示例4:

int sub(int a, int b)
{
    return a-b;
}

void fun04()
{
    int (*p1)(int, int) = sub;
    int num = p1(10,3);
    printf("num = %d\n", num);  //num = 7
}

5.2 指针变量作为函数的参数 (传递指针变量的地址 )

指针变量是一个变量,故可以作为 函数的形参,此时传递的是指针变量的地址

示例:

  • 指针变量作为形参在函数中定
  • 指针变量作为实参,在调用函数时传递
//指针变量作为形参在函数中定义
void setNum(int *num, int i)
{
    num[i] = 10;
}

void fun05()
{
    int ns[5] = {0};
    // int *p = ns;
    //指针变量作为实参,在调用函数时传递
    //本次传递的是指针变量的地址
    setNum(ns, 0);
    printf("ns[0] = %d\n", ns[0]);      //ns[0] = 10
}

5.3 字符串指针作为实参

字符串指针作为实参,将指针指向常量区的内容传递函数。函数内部修改指针内容时,不会影响函数外部的值

示例1:函数内部修改指针内容时,不会影响函数外部的值

void setStr(char *str)
{
    str = "world";
}

void fun06()
{
    char *s = "hello";
    setStr(s);
    printf("s=%s\n", s);     //s=hello
}

在这里插入图片描述

示例2:

void setchar(char *str, int i)
{
    str[0] = 'H';
}

void fun07()
{
    //字符数组是指针常量,可改值不可该地址
    char str[] = "hello";
    char *p = str;
    //setchar函数修改的是地址中的值,所以传入该指针会出现段错误
    // char *p = "hello";  //常量指针,可改地址不可改值
    setchar(p, 0);
    printf("str=%s\n", str);    //str=Hello
}

5.4 字符串指针的指针作为函数参数

字符指针的指针作为函数的实参,函数的形参使用 **q(指针的指针),函数 内部修改字符指针地址,就可以 修改 函数外部的结果

示例:函数内部修改字符指针地址,就可以 修改 函数外部的结果

void setName(char **name)
{
    *name = "李四";
}
void fun09()
{
    char *n = "张三";
    char **p = &n;
    setName(p);
    printf("n=%s\n",n);
}

打印出对应的地址:

void setName(char **name)
{
    printf("name---->%p\n", name);      //name---->0x7ffd94285208
    *name = "李四";
    printf("*name改变后---->%p\n", *name);    //*name改变后---->0x400a53
}
void fun09()
{
    char *n = "张三"; 
    printf("n---->%p\n", n); //n---->0x400a71
    printf("*n---->%p\n", &n); //*n---->0x7ffd94285208
    char **p = &n;
    printf("p---->%p\n", p); //p---->0x7ffd94285208
    printf("*p---->%p\n", *p); //*p---->0x400a71
    setName(p); 
    printf("n修改后---->%p\n", n); //n修改后---->0x400a53
    printf("*p修改后---->%p\n", *p); //*p修改后---->0x400a53
    
    printf("n=%s\n",n);		//n=李四
}

图示:

在这里插入图片描述

5.5 指针作为返回值

函数中局部指针变量做为函数的返回值函数执行完毕后,其局部指针变量指向的地址也将被释放,外部无法使用,导致段错误

如需外部使用可以将 局部指针变量修改为 静态局部指针变量

示例:

int* my01()
{
    /*
        静态局部变量生命周期:
        随着所在函数第一次调用而生成,随着所在进程的执行完毕而销毁
     */ 
    static int num[5] = {1,2,3,4,5};
    return num;
}

void fun11()
{
    int *ns = my01();
    printf("%d\n",ns[0]);   //1
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

win11 无法登录微软账户 终极解决方案

背景&#xff1a;win11突然无法登录微软账户&#xff0c;office无法激活&#xff0c;Edge里的微软账户也无法登录&#xff0c;反馈中心也无法打开等&#xff0c;有网络&#xff0c;浏览器可以访问微软并进行登录。 试过网上的网络配置&#xff08;SSL及TLS协议勾选&#xff09…

论文编写软件latex安装教程

目录 1.下载安装包2.安装texlive 本人系统为windows&#xff0c;本教程基于windows系统&#xff0c;如果是其它系统请参考对应教程&#xff0c;注意选择对应系统的安装包&#xff01; 1.下载安装包 有三种集成环境安装包 texlive 是主流的环境&#xff0c;集成了较多的包&…

k8s(三): 基本概念-ReplicaSet与Deployment

PeplicaSet ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合&#xff0c;通常用来保证给定数量的、完全相同的 Pod 的可用性。 最佳实践 Deployment 是一个可以拥有 ReplicaSet 并使用声明式方式在服务器端完成对 Pod 滚动更新的对象。 尽管 Rep…

华为杯研究生数学建模优秀参考论文(优秀论文参考2004-2022年)

一、背景介绍 中国研究生数学建模竞赛是一项面向在校研究生进行数学建模应用研究的学术竞赛活动&#xff0c;是广大在校研究生提高建立数学模型和运用互联网信息技术解决实际问题能力&#xff0c;培养科研创新精神和团队合作意识的大平台&#xff0c;大赛赞助单位为华为技术有限…

<JavaEE> synchronized关键字和锁机制 -- 锁的特点、锁的使用、锁竞争和死锁、死锁的解决方法

目录 一、synchronized 关键字简介 二、synchronized 的特点 -- 互斥 三、synchronized 的特点 -- 可重入 四、synchronized 的使用示例 4.1 修饰代码块 - 锁任意实例 4.2 修饰代码块 - 锁当前实例 4.3 修饰普通方法 - 锁方法所在实例 4.4 修饰代码块 - 锁指定类对象 …

【开源】基于JAVA语言的校园疫情防控管理系统

项目编号&#xff1a; S 037 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S037&#xff0c;文末获取源码。} 项目编号&#xff1a;S037&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生2.2 老师2.3 学校管理部门 三、…

【数据结构】二叉树---C语言版

二叉树 一、树的概念及结构1.树的概念2.树的相关概念3.树的表示4.树在实际中的应用 二、二叉树的概念及结构1.二叉树的概念2.满二叉树3.完全二叉树4.二叉树的性质5.二叉树的储存结构 三、二叉树的遍历1.前序遍历2.中序遍历3.后序遍历4.层序遍历 四、手撕二叉树&#xff08;务必…

基于JSP的网络考试系统/在线考试系统的设计与实现

摘 要 网络考试系统是由高校的一个网络考试&#xff0c;按照章程自主开展网络考试系统。网络考试是实施素质教育的重要途径和有效方式&#xff0c;在加强校园文化建设、提高学生综合素质、引导学生适应社会、促进学生成才就业等方面发挥着重要作用&#xff0c;是新形势下有效凝…

YOLOv7+姿态估计Pose+tensort部署加速

YOLOv7-Pose 实现YOLOv7&#xff1a;可训练的免费套件为实时目标检测设置了最新技术标准 YOLOv7-Pose的姿态估计是基于YOLO-Pose的。关键点标签采用MS COCO 2017数据集。 训练 使用预训练模型yolov7-w6-person.pt进行训练。训练命令如下&#xff1a; python -m torch.distr…

leetcode 3. 无重复字符的最长子串

代码&#xff1a; //采用滑动窗口来进行解决 class Solution {public int lengthOfLongestSubstring(String s) {//字符串由英文字母、数字、符号和空格组成&#xff0c;通过对应的 ASCLL 码作为下标在数组中记录出现的次数//判断字符在字串中是否重复出现int[] ascllnew int[…

前端对浏览器的理解

浏览器的主要构成 用户界面 &#xff0d; 包括地址栏、后退/前进按钮、书签目录等&#xff0c;也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分。 浏览器引擎 &#xff0d; 用来查询及操作渲染引擎的接口。 渲染引擎 &#xff0d; 用来显示请求的内容&#…

深入理解同源限制:网络安全的守护者(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

我爱上这38个酷炫的数据大屏(附 Python 源码)

随着大数据的发展&#xff0c;可视化大屏在各行各业得到越来越广泛的应用。 可视化大屏不再只是电影里奇幻的画面&#xff0c;而是被实实在在地应用在政府、商业、金融、制造等各个行业的业务场景中&#xff0c;切切实实地实现着大数据的价值。 所以本着学习的态度&#xff0…

四、设置主机名和域名映射

目录 1、配置每台虚拟机主机名 2、配置每台虚拟机域名映射 1、配置每台虚拟机主机名

MATLAB实战 | S函数的设计与应用

S函数用于开发新的Simulink通用功能模块&#xff0c;是一种对模块库进行扩展的工具。S函数可以采用MATLAB语言、C、C、FORTRAN、Ada等语言编写。在S函数中使用文本方式输入公式、方程&#xff0c;非常适合复杂动态系统的数学描述&#xff0c;并且在仿真过程中可以对仿真进行更精…

ASP.NET版本WOL服务的使用

本文以WOL为例&#xff0c;演示如何通过 GPT-4 让其为 WebAPI 项目设计一个网页。其中介绍了如何让GPT4生成相关功能&#xff0c;添加动画效果&#xff0c;接口鉴权等。 1. 背景 前面我们已经完成了一个WOL服务的开发&#xff0c;并将其迁移改造为了 ASP.NET 服务并完成了部署…

02数仓平台Zookeeper

概述 ZooKeeper是一种分布式协调服务&#xff0c;用于管理大型主机集。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper通过其简单的架构和API解决了这个问题。ZooKeeper允许开发人员专注于核心应用程序逻辑&#xff0c;而不必担心应用程序的分布式性质。 Zookeepe…

DBeaver 社区版(免费版)下载、安装、解决驱动更新出错问题

DBeaver 社区版&#xff08;免费版&#xff09; DBeaver有简洁版&#xff0c;企业版&#xff0c;旗舰版&#xff0c;社区版&#xff08;免费版&#xff09;。除了社区版&#xff0c;其他几个版本都是需要付费的&#xff0c;当然相对来说&#xff0c;功能也要更完善些&#xff…

uniApp打包的手机app如果用户没开启通知权限、引导用户开启

封装一个setPermissions.js文件 /*** 如果用户没开启通知权限、引导用户开启 */ export function setPermissions() {// #ifdef APP-PLUS if (plus.os.name Android) {var main plus.android.runtimeMainActivity();var pkName main.getPackageName();var uid main.getApp…

某公司前端笔试题(12.30)

1、对象数组去重&#xff1a; 数组去重&#xff1a; const a[{a:1,b:2},{a:2},{a:2},{a:1,c:3},{b:2,a:1}] 结果&#xff1a;[{a:1,b:2},{a:2},{a:1,c:3}] // 判断两个对象的属性值是否一致 const a [{ a: 1, b: 2 }, { a: 2 }, { a: 2 }, { a: 1, c: 3 }, { b: 2, a: 1 }] co…