【408专项篇】C语言笔记-第六章(指针)

news2024/11/26 18:32:38

文章目录

    • 第一节:指针的本质
      • 1. 指针的定义
      • 2. 取地址操作符与取值操作符
    • 第二节:指针的传递
      • 1. 指针的传递
    • 第三节:指针的偏移
      • 1. 指针的偏移
      • 2. 指针与一维数组
    • 第四节:动态指针与内存申请
      • 1. 指针与动态内存申请
      • 2. 堆空间和栈空间的差异

第一节:指针的本质

1. 指针的定义

直接访问:如果在程序中定义了一个变量,那么在对程序进行编译时,系统就会给这个变量分配内存单元,按变量地址存取变量值的方式称为“直接访问”,如:printf(“%d”,i);scanf(“%d”,&i);等。

间接访问:将变量i的地址存放到另一个变量中,在C语言中,指针变量是一种特殊的变量,它用来存放变量地址。

指针变量的定义格式:

基类型 *指针变量名;

例如:int *i_pointer;

指针与指针变量是两个概念,一个变量的地址称为该变量的“指针”。

如图:2000是地址,即变量i的指针。i_pointer存放的是2000,那么i_pointer称为指针变量。

64位应用程序指针寻址范围是8个字节。32位应用程序寻址范围是4个字节

2. 取地址操作符与取值操作符

取地址操作符为&,也称引用。通过该操作符我们可以获取一个变量的地址值;取值操作符为*,也称解引用。通过该操作符我们可以得到一个地址对应的数据。

指针变量的初始化一定是某个变量的地址

#include <stdio.h>

// &符号是取地址,指针变量的初始化一定是某个变量取地址
int main() {
    int i=5;
    int *p=&i;  // 指针变量初始化
    printf("i=%d\n",i);  // 直接访问
    printf("*p=%d\n",*p);  // 直接访问
    return 0;
}
F:\Computer\Project\practice\6\6.1-pointer\cmake-build-debug\6_1_pointer.exe
i=5
*p=5

进程已结束,退出代码为 0

注意点:

  1. 指针变量前面的* 表示该变量的类型为指针变量。指针变量名为p,而不是*p。

  2. 在定义指针变量时必须指定其类型,指针变量类型和变量类型要保持一致。例如:int i;那么指针变量应为int *p,都是int类型。

  3. &和*两个运算符的优先级别相同,但要按自右向左的方向结合。

  4. 如果要同时定义多个指针变量,需要使用:int *a,*b,*c…

第二节:指针的传递

指针的使用场景通常只有两种,传递和偏移。

1. 指针的传递

#include <stdio.h>

void change(int j){
    j=5;
}

int main() {
    int i=10;
    printf("before change i=%d\n",i);
    change(i);
    printf("after change i=%d\n",i);
    return 0;
}
F:\Computer\Project\practice\6\6.2-transmit\cmake-build-debug\6_2_transmit.exe
before change i=10
after change i=10

进程已结束,退出代码为 0

分析:i=10经过了change函数,值应当变为5才对,为何没有改变?

当main函数开始执行时,系统会为main函数开辟函数栈空间,当走到int i时,main函数的栈空间就会为变量i分配4个字节大小的空间,调用change函数时,系统会为change函数重新分配新的函数栈空间,并为形参j分配4个字节大小的空间,所以i和j对应空间地址是不同的。在调用change(i)时,实际上是将i的值赋给了j。我们把这种效果称为值传递(C语言的函数调用均为值传递)。因此,当我们在change函数的函数栈空间内修改变量j值后,change函数执行结束,其栈空间会释放,j就不存在了,i的地址和它不同,并没有被修改。

我们将以上程序做一些修改:

#include <stdio.h>

void change(int *j){
    *j=5;  // 注意这里不是j=5,而是*j=5
}

int main() {
    int i=10;
    printf("before change i=%d\n",i);
    change(&i);  // 传递变量i的地址
    printf("after change i=%d\n",i);
    return 0;
}
F:\Computer\Project\practice\6\6.2-transmit\cmake-build-debug\6_2_transmit.exe
before change i=10
after change i=5

进程已结束,退出代码为 0

很明显,当我们把i的地址传递给chang函数,那么它取到的值就是i的实际地址对应的值。当然修改的就是i的值了。

注意:*j=5才是对i的修改,如果写成j=5,那只是对j的修改。因为j和i对应的内存空间不同,只有*j才能取到i的空间地址。

第三节:指针的偏移

1. 指针的偏移

指针的另一种场景就是对其进行加减。在实际中,我们把对指针的加减称为指针的偏移,加就是向后偏移,减就是向前偏移。

#include <stdio.h>

#define N 5
int main() {
    int a[N]={1,2,3,4,5};
    int *p=a;  // 保证等号两边的数值类型一致。一维数组本身存放的就是首地址,所以没有必要写&取地址
    int i;
    for(i=0;i<N;i++){ // 正序输出
        printf("%3d",*(p+i));
    }
    printf("\n------------------\n");
    p=&a[N-1];  // 让P指向最后一个元素,注意有&
    for(i=0;i<N;i++){  // 逆序输出
        printf("%3d",*(p-i));
    }
    printf("\n");
    return 0;
}
F:\Computer\Project\practice\6\6.3-deviation\cmake-build-debug\6_3_deviation.exe
  1  2  3  4  5
------------------
  5  4  3  2  1

进程已结束,退出代码为 0

说明:数组本身就是存放的就是指针,不需要加&,但是里面的具体数据需要通过&取值。

同时,偏移的长度是其基类型的长度,也就是偏移sizeof(int),这样通过*(p+1)就可以得到元素a[1]的值。如下图:

2. 指针与一维数组

分析:为什么一维数组在函数调用进行传递时,它的长度子函数无法知道呢?

#include <stdio.h>

void change(char *d){ // 形参实际上接收到的是一个地址(数组的首地址)
    *d='H';  // 指针法
    d[1]='E';  // 下标法
    *(d+2)='L';  // 指针法
}

int main() {
    char c[10]="hello";
    puts(c);
    change(c);  // 数组名作为参数传递给子函数时,是弱化为指针的
    puts(c);
    return 0;
}
F:\Computer\Project\practice\6\6.3-pointer_arr\cmake-build-debug\6_3_pointer_arr.exe
hello
HELlo

进程已结束,退出代码为 0

解析:因为一维数组名中存储的是数组的首地址。所以子函数change中其实传入了一个地址。定义一个指针变量时,指针变量类型要和数组类型保持一致,通过取值操作,就可以将"h"改为"H",这种方法称为指针法。获取数组元素时,也可以通过取下标的方式来获取数组元素并修改,这种方法称为下标法。

第四节:动态指针与内存申请

1. 指针与动态内存申请

平时我们定义的整型、浮点型、字符型变量、数组变量都是在栈空间中,而栈空间的大小在编译时确定的,不会改变。如果使用的空间大小不确定,就要使用堆空间

#include <stdio.h>
#include <stdlib.h>  // malloc需要的头文件
#include <string.h>

int main() {
    int i;
    char *p;
    char q[50];   // 也可以写成char *q
    scanf("%d",&i);
    p=(char*) malloc(i); // 使用malloc动态申请堆空间,注意强制类型转换。堆空间刚申请下来是void类型,需要转换
    strcpy(p,"malloc success");
    strcpy(q,p);
    puts(p);
    free(p);   // free时必须使用malloc申请时返回的指针值,同时不能进行任何偏移
    puts(p);  // 可以看到释放完后是乱码
    puts(q);
    return 0;
}
F:\Computer\Project\practice\6\6.4-apply\cmake-build-debug\6_4_apply.exe
20
malloc success
鄅
malloc success

进程已结束,退出代码为 0

说明:在执行#include <stdlib.h> void *malloc(size_t size)时,需要给malloc传递的参数是一个整型变量。

当确定了用来存储什么类型的数据后,需要将void*强制转换为对应的类型。

同时需要注意指针本身的大小,和其指向空间的大小是两回事

如图所示,定义的整型变量i,指针变量p均在main函数的栈空间中,通过malloc申请的空间会返回一个堆空间的首地址,我们把首地址存入变量p,知道了首地址,就可以通过strcpy函数往对应的空间存储字符数据。

堆的效率要比栈低得多

栈空间由系统自动管理,而堆空间的申请和释放需要自行管理。所以需要通过free函数释放堆空间,free函数的头文件及格式为:

#include <stdlib.h>

void free(void *ptr);

其传入的参数为void类型指针,任何指针均可自动转换为void*类型指针,所以p传递给free函数时,不需要强制类型转换。p的地址值必须是malloc当时返回的地址值,不能进行偏移等操作

2. 堆空间和栈空间的差异

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

char* print_stack(){
    char c[17]="I am print_stack";
    puts(c);
    return c;
}
char* print_malloc(){
    char *p;
    p=(char*)malloc(20);
    strcpy(p,"I am print_malloc");
    puts(p);
    return p;
}
int main() {
    char *p;
    p=print_stack();
    printf("p=%s\n",p);  // 打印出来了null,因为栈空间被释放了
    p=print_malloc();
    print_malloc("p=%s\n",p);   // 打印出了实际的数据,因为堆空间不会被释放,除非进程结束
    return 0;
}
F:\Computer\Project\practice\6\6.4-heap_stack\cmake-build-debug\6_4_heap_stack.exe
I am print_stack
p=(null)
I am print_malloc
I am print_malloc

进程已结束,退出代码为 

解析:函数执行结束后,栈空间会被释放,而堆空间不会被释放,除非进行free操作。

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

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

相关文章

电脑无线网卡连接的无线信号不好的,经常丢包,掉线断网如何优化解决

环境&#xff1a; 电脑&#xff1a;HP480G7 系统:Win10 专业版 无线网卡&#xff1a;水星ud6s 、TP-LINK等 AP&#xff1a;锐捷RAP2200(E) 组网模式&#xff1a;AP代管AP模式 问题描述&#xff1a; 电脑无线网卡连接的无线信号不好的&#xff0c;经常断网&#xff0c;因…

docker logs命令详解

docker 命令官档如下&#xff1a; https://docs.docker.com/engine/reference/commandline/docker/ docker logs官档如下 https://docs.docker.com/engine/reference/commandline/logs/#retrieve-logs-until-a-specific-point-in-time 一、常用命令 1. 可以查看命令用法 doc…

英国访问学者|签证申请详细步骤简述

英国访问学者签证流程是怎么样的&#xff1f;下面就随知识人网老师一起来看一看英国访问学者签证申请详细步骤简述。 第1步&#xff1a;英国签证申请中心只接受在线填写的申请表&#xff0c;不接受手写表格。请访问官网完整填写申请表。(必须使用英文大写填写) 第2步&#xff…

数据库系统 整体结构化 的理解

数据库系统实现整体数据的结构化&#xff0c;这是数据库的主要特征之一&#xff0c;也是数据库系统与文件系统的本质区别。 在文件系统中&#xff0c;文件中的记录内部具有结构&#xff0c;但是记录的结构和记录之间的联系被固化在程序中&#xff0c;需要由程序员加以维…

jenkins配置maven+git自动构建jar包

进入页面 安装maven插件 需要使用maven插件构建项目,所以安装maven插件 步骤如下图&#xff1a; 点击安装后跳转至安装页面&#xff0c;等待在线安装完成即可 安装完成配置当前服务器中安装的maven 下滑到最下面点击新增maven 配置完成点击应用完成 配置当前服务器中安装的gi…

ADB 命令结合 Monkey 的使用

1、什么是monkey Monkey 是Android SDK提供的一个命令行工具&#xff0c;可以简单方便的发送伪随机的用户事件流&#xff0c;对Android APP做压力&#xff08;稳定性&#xff09;测试。主要是为了测试app是否存在无响应和崩溃的情况。 2、adb结合 monkey 的常用命令 adb shel…

如何选择跨平台桌面应用开发框架

受益于开源技术的发展&#xff0c;以及响应快速开发的实际业务需求&#xff0c;跨平台开发不仅限于移动端跨平台&#xff0c;桌面端虽然在市场应用方面场景不像移动端那么丰富&#xff0c;但也有市场的需求。 相对于个人开发者而言&#xff0c;跨平台框架的使用&#xff0c;主…

干货!一次伪静态页面的SQL注入!白帽黑客实战 。

涉及技能点 SQL注入基础原理 盲注常用函数及思路 burpsuite基础知识 过程记录 1.发现 在翻阅一EDU站点时&#xff0c;发现路径中带有明显的数字参数 好像与我们平时习惯的xxx.php?id不同呃 2.尝试注入 按照以往习惯&#xff0c;先用order by探探底&#xff0c;结果翻车了…

SPARKSQL3.0-PhysicalPlan物理阶段源码剖析

一、前言 阅读本节需要先掌握【SPARKSQL3.0-Optimizer阶段源码剖析】 本质&#xff1a;物理计划阶段将optimizer阶段优化后的逻辑算子树【LogicalPlan】进行进一步转换&#xff0c;生成物理算子树【SparkPlan】&#xff0c;物理算子树的节点可以直接生成 RDD 或对 RDD 进行 t…

javaweb 使用element + vue 完善项目 servlet 优化

我们先定义一个BaseServlet,继承HttpServlet 重写Service方法 &#xff08;因为HttpServlet就是在Service方法里做的通过请求方式进行方法分发&#xff0c;我们就重写改成通过请求路径分发&#xff09; 根据资源路径进行方法分发&#xff0c;利用反射得到调用者的class字节码文…

C++-指针:void*(不确定类型指针)简介【void *可以接受任何类型的赋值】【void *可以赋值给任何类型的变量】【void *不可以解引用】

void* 是一种特殊的指针类型&#xff0c;可用于存放任意对象的地址。一个 void* 指针存放着一个地址&#xff0c;这一点和其他指针类似。 void *可以接受任何类型的赋值&#xff0c;任何类型的指针都可以直接赋值给void *型指针&#xff0c;无需进行强制类型转换&#xff0c;相…

超大规模研究发现,缺乏维生素D,增加早逝风险

晒太阳是日常生活中最常见的一件事情了&#xff0c;但是很多人为了避免晒黑不喜欢晒太阳&#xff0c;更是把自己在夏天裹得严严实实&#xff0c;恨不得只露两只眼睛。其实每天晒太阳对身体有很多益处&#xff0c;杀菌消毒、促进维生素D的生成、促进血液循环和加速脂肪燃烧等。维…

IBM MQ 通道数量查看,以及最大通道数的修改

一&#xff0c;说明 在实践生产环境中&#xff0c;我们的运维人员很多时候都想关注通道使用了多少&#xff0c;离限定的对大通道数还有多少&#xff1f;下面我们就查看通道数量提供相应的办法。 二&#xff0c;示例 我有两个队列管理器&#xff0c;TEST_QM和 TEST_RQM&#…

C. Random Events(思维+概率)

Problem - 1461C - Codeforces 罗恩是一个长度为n的排列组合的快乐主人。 一个长度为n的排列组合是一个由1到n的n个不同的整数按任意顺序组成的阵列。例如&#xff0c;[2,3,1,5,4]是一个排列组合&#xff0c;但是[1,2,2]不是一个排列组合&#xff08;2在数组中出现了两次&…

动手学习深度学习

动手学习深度学习内容安排深度学习介绍内容安排 深度学习基础&#xff1a;线性神经网络、多层感知机卷积神经网络&#xff1a;LeNet、AlexNet、VGG、Inception、ResNet循环神经网络&#xff1a;RNN、GRU、LSTM、seq2seq注意力机制&#xff1a;Attention、Transformer优化算法&…

GPU是什么?GPU有多重要?

前段时间&#xff0c;MD和英伟达相继接到通知要对我国断供高端GPU芯片&#xff0c;很多人不知道GPU到底有什么用&#xff1f;下面IC修真院就带大家来一起了解一下GPU。 首先来了解一下GPU是什么&#xff1f; GPU–图形处理器&#xff08;Graphics Processing Unit&#xff09…

Assignment写作需要做好哪些练习?

有些澳洲留学小伙伴在被Assignment难住后往往会选择多练习来完成&#xff0c;那么如何顺利完成一篇Assignment的呢&#xff1f;小编就来为大家详解一番。 Some students studying in Australia often choose to practice more to complete assignments when they are baffled b…

【pwn】2022 极客大挑战

【pwn】2022 极客大挑战 前言 又是一年的极客大挑战&#xff0c;又老了一岁&#xff0c;也只有打打新生赛才能有第一次接触ctf快乐了&#xff0c;现在各种比赛的pwn都是纯纯的坐牢~ 本次题解的所有脚本使用的类库都是本人自己整合的一个库&#xff0c;github地址&#xff1a…

2022京东双十一全品类销售额变化情况一览:50%增长,50%下滑

面对外界的风风雨雨&#xff0c;京东一直稳如泰山。有人认为京东全线都出现销售额大幅下滑&#xff0c;有人则认为京东今年的销售额整体可观。 那么相较于去年同期&#xff0c;究竟有哪些品类在如此大环境下依然处于上升期&#xff0c;又有哪些品类遭遇滑铁卢&#xff0c;面临短…

Redis数据类型之list

文章目录listⅠ. 增删查改Ⅱ. 业务场景Ⅲ. 注意事项提示&#xff1a;以下是本篇文章正文内容&#xff0c;Redis系列学习将会持续更新 list ● 数据存储需求&#xff1a;存储多个数据&#xff0c;并对数据进入存储空间的顺序进行区分 ● 需要的存储结构&#xff1a;一个存储空间…