C语言基础(三)——指针

  五、指针

5.1 指针的定义

内存区域中的每字节都对应一个编号,这个编号就是“地址”.
在程序中定义一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元.
按变量地址存取变量值的方式称为“直接访问”,如printf(""%d",i);、 scanf("%d",&i);等,
另一种存取变量值的方式称为“间接访问”,即将变量i的地址存放到另一个变量中.
在C语言中,指针变量是一种特殊的变量,它用来存放变量地址。
指针变量的定义格式如下:

基类型  *指针变量名;

例如

int *i_pointer; 

指针与指针变量是两个概念,一个变量的“地址”成为该变量的“指针”
用来存放一个变量的地址(即指针)的变量,称为“指针变量”
如下图的i_pointer,在64位应用程序中,sizeof(i_pointer)=8,即占8个字节
如果考研强调程序是32位的,则寻址范围为4字节 

5.2 取地址操作符与取值操作符,指针本质

取地址操作符为&,也称引用,通过&可以获取一个变量的地址值;
取值操作符为*,也称解引用,通过*可以得到一个地址对应的数据。
如下例,通过&i获取整型变量i的地址值,然后对整型指针变量p进行初始化, p中存储的是整型变量i的地址值,所以通过*p(printf 函数中的*p)就可以获取整型变量i的值. p中存储的是一个绝对地址值,

#include <stdio.h>

int main() {
    int i=5;
    //定义了一个指针变量,i_pointer就是指针变量名
    //指针变量的初始化一定是某个变量取地址来赋值,不能随机写个数
    int *i_pointer;
    i_pointer=&i;
    printf("i=%d\n",i); //直接访问
    printf("*i_pointor=%d",*i_pointer);
    return 0;
}

注意:

(1)指针变量前面的“*”表示该变量为指针型变量。
例如,float *pointer_1;
指针变量名是pointer_1 ,而不是*pointer_1.
(2)在定义指针变量时必须指定其类型。只有整型变量的地址才能放到指向整型变量的指针变量中。例如,下面的赋值是错误的:
float a;
int * pointer_1;
pointer_1=&a;//毫无意义而且会出错
(3)如果已执行了语句
pointer_1=&a; 那&*pointer_1与&a相同,都表示变量a的地址,即pointer_1
即虽然”&“和”*“两个运算符的优先级相同,但是要按自右向左的方向结合

*&a表示,先进行&a的运算,得到a的地址,在进行*运算,*&a和*pointer_1的作用一样,都等价于变量a,即*&a与a等价

int *a,b,c;表示声明了一个指针变量,两个int变量
要声明三个指针变量需要写成

int *a,*b,*c;

指针的使用场景只有两个,即传递和偏移

5.3 指针的传递 

下例的主函数通过子函数改变量i的值,但是执行程序后发现i的值并未发生改变
因为i的地址和j的地址实际并不相同,执行change函数改变的并不是之前定义的i的地址

#include <stdio.h>
void change(int j){ //j是形参
    j=5;
}
int main() {
    int i=10;
    printf("before value i=%d\n",i);
    //在子函数内去改变主函数的某个变量值
    change(i); //C语言的函数调用是值传递,实参赋值给形参,j=i
    printf("after value i=%d\n",i);
    return 0;
}

//输出结果都是10

原理如下图所示,程序的执行过程其实就是内存的变化过程,当main函数执行时,系统会为其开辟函数栈空间,当程序走到int i时,main函数的栈空间就会为变量i分配4字节大小的空间,调用change函数时,系统会为change函数重新分配新的函数栈空间,并为形参变量j分配4字节大小的空间,调用change(i)实际是将i的值赋值给j,这就是值传递,当change()中修改变量j的值后,函数执行结束,其栈空间就会释放,j就不再存在,i的值不会改变。

要在子函数中修改main函数的值,如下所示,通过指针进行操作 

#include <stdio.h>
void change(int *j){ //j是形参
    *j=5;    //间接访问得到变量i  *j等价于变量i
}

//指针的传递
int main() {
    int i=10;
    printf("before value i=%d\n",i);
    change(&i);     //传递变量i的地址  j=&i
    printf("after value i=%d\n",i);
    return 0;
}

//执行change()后,打印的i的值为5

变量i的地址传递给change函数时,实际效果是j=&i,依然是值传递,只是这时候的j是一个指针变量,内部存储的是变量i的地址,所以通过*j就间接访问到了与变量i相同的区域,通过*j=5就实现了对变量i的值的改变。

5.4 指针的偏移使用场景

5.4.1 指针的偏移

拿到指针变量后可以对他进行加减,如找到一栋楼是2栋,那往前就是1栋,往后就是3栋

#include <stdio.h>
//指针的偏移使用场景,也就是对指针进行加减
#define N 5  //符号常量N
int main() {
    int a[N]={1,2,3,4,5}; //数组名内存中存储了数组的起始地址,a中存储的就是一个地址值
    int *p;//定义指针变量p
    p=a;//不是取地址a 因为数组名中本来就有地址
    int i;
    for(i=0;i<N;i++){
        printf("%3d",*(p+i)); //这里*(p+i)和a[i]的结果是等价的
    }
    printf("\n-------------------------------\n");
    p=&a[4];    //让p指向最后一个元素
    for(i=0;i<N;i++){   //逆序输出
        printf("%3d",*(p-i));
    }
    printf("\n");
    return 0;
}

  1  2  3  4  5
-------------------------------
  5  4  3  2  1

如下图所示,数组名中存储着数组的起始地址Ox61fdf0,其类型为整型指针,所以可以将其赋值给整型指针变量p,可以从监视窗口中看到p+1的值为Ox61fdf4.指针变量加1后,偏移的长度是其基类型的长度,也就是偏移sizeof(int),这样通过*p+1就可以得到元素a[1]。编译器在编译时,数组取下标的操作正是转换为指针偏移来完成 

float *p; p的加减也是偏移4个字节  

 5.4.2 指针与一维数组

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

#include <stdio.h>
//指针与一维数组的的传递
//数组名作为实参传递给子函数时,是弱化为指针的 (指针默认就是8个字节)
//练习传递与偏移

void change(char *d){
    *d='H';
    d[1]='E';   //*(d+1)='E'与其等价
    *(d+2)='L';
    *(d+3)='L';
    *(d+4)='O';
}
int main(){
    char c[10]="hello";
    change(c);
    puts(c);
    return 0;
}

5.6指针与malloc动态内存申请,栈与堆的差异

5.6.1 指针与动态内存申请

C语言的数组长度固定,因为其定义的整型、浮点型、字符型变量、数组变量都在栈空间中,而栈空间的大小在编译时是确定的,如果使用的空间大小不确定,就要使用堆空间。

#include <stdio.h>
#include <stdlib.h> //malloc使用的头文件
#include <string.h> //strcpy使用的头文件
int main() {
    int size;//代表要申请多大字节的空间
    char *p;//void*类型的指针是不能偏移的,不能进行加减 ,因此不会定义无类型指针
    scanf("%d",&size);  //输入要申请的空间大小
    //malloc返回的void*代表无类型指针
    p= (char*)malloc(size); //malloc返回的是对应空间起始地址 要强转malloc的类型与p一致
    strcpy(p,"malloc success");//strcpy传进去的也是指针类型的 所以可以直接copy
    puts(p);
    free(p);//释放申请的空间时,必须是最初malloc返回给我们的地址  即free的时候p不能变 不能填p+1这种 会异常
    printf("free success\n");
    return 0;
}

注意:指针本身的大小和其指向空间的大小是两码事,指针本身大小只跟操作系统的位数有关,指向空间的大小是字节定义的,需要定义变量单独存储

5.6.2 栈空间与堆空间的差异

栈是计算机系统提供的数据结构,计算机会在底层对栈提供支持,分配专门的寄存器存放栈的地址,操作都有专门的指令执行,所以栈的效率比较高。

堆则是C/C++函数库提供的数据结构,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间就可能调用系统功能去增加程序数据段的内存空间,堆的效率要比栈低得多。

栈不能动态,所以要动态还是要使用堆,注意堆使用完要用free释放

#include <stdio.h>
#include <stdlib.h> //malloc使用的头文件
#include <string.h> //strcpy使用的头文件
//堆和栈的差异
char* print_stack(){
    char c[100]="I am print_stack func"; //c中存了数组的起始地址
    char *p;
    p=c;
    puts(p);
    return p;
}
char* print_malloc(){
    char *p=(char*) malloc(100); //堆空间在整个进程中一直有效,不因为函数结束消亡
    strcpy(p,"I am print_stack func");
    puts(p);
    return p;
}
int main(){
    char *p;
    p=print_stack(); //调用函数执行完之后,操作系统就会直接释放掉这个栈空间
    puts(p);//p接到了print_stack()的指针 打印乱码或null
    p=print_malloc();
    puts(p);
    free(p);//只有free的时候,堆空间才会释放
    return 0;
}


/*
int main() {
    int size;//代表要申请多大字节的空间
    char *p;//void*类型的指针是不能偏移的,不能进行加减 ,因此不会定义无类型指针
    scanf("%d",&size);  //输入要申请的空间大小
    //malloc返回的void*代表无类型指针
    p= (char*)malloc(size); //malloc返回的是对应空间起始地址 要强转malloc的类型与p一致
    strcpy(p,"malloc success");//strcpy传进去的也是指针类型的 所以可以直接copy
    puts(p);
    free(p);//释放申请的空间时,必须是最初malloc返回给我们的地址  即free的时候p不能变 不能填p+1这种 会异常
    printf("free success\n");
    return 0;
}
*/

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

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

相关文章

C++ 入门(八)— 常量和字符串

常量和字符串 常量变量常量表达式编译时优化 Constexpr 变量std::string字符串输出 std::coutstd::string可以处理不同长度的字符串字符串输入 std::cin用于输入文本std::getline()不要按值传递Constexpr 字符串 std::string_view可以使用许多不同类型的字符串进行初始化可以接…

基于springboot+html实现的衣物捐赠平台

一、系统架构 前端&#xff1a;html | layui | jquery | css 后端&#xff1a;springboot | thymeleaf | mybatis 环境&#xff1a;jdk1.8 | mysql | maven 二、代码及数据库 三、功能介绍 01. 登录页 02. 注册 03. web页-首页 04. web页-捐赠衣服 05. web页-论坛交流…

Doris实战——金融壹账通指标中台的应用实践

目录 前言 一、业务痛点 二、早期架构挑战 三、架构升级 四、一体化指标数据平台 4.1 构建指标体系 4.2 构建指标平台功能 五、Doris指标应用实践 六、未来规划 原文大佬的这篇指标中台的应用实践有借鉴意义&#xff0c;这里摘抄下来用作学习和知识沉淀。 前言 在搭建…

开源项目_代码生成项目介绍

1 CodeGeeX 系列 1.1 CodeGeeX 项目地址&#xff1a;https://github.com/THUDM/CodeGeeX 7.6k Star主要由 Python 编写深度学习框架是 Mindspore代码约 2.5W 行有 Dockerfile&#xff0c;可在本地搭建环境模型大小为 150 亿参数相对早期的代码生成模型&#xff0c;开放全部代…

BAT等大厂必问技术面试题,2024Android开发面试解答之设计模式

IT行业薪水高&#xff0c;这是众所周知的&#xff0c;所以很多人大学都选择IT相关专业&#xff0c;即使非该专业的人&#xff0c;毕业了也想去一个培训机构镀镀金&#xff0c;进入这一行业。 但是有关这个行业35岁就退休的说法&#xff0c;也一直盛传。 加上这几年不断有各大…

基于java Springboot实现课程评分系统设计和实现

基于java Springboot实现课程评分系统设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获取源…

【白嫖8k买的机构vip教程】Appium自动化(3):Appium-Desktop界面介绍

Appium-Desktop主界面包含三个菜单Simple、Advanced、Presets Simple界面&#xff1a; Host设置Appium server的ip地址&#xff0c;本地调试可以将ip地址修改为127.0.0.1&#xff1b;Port设置端口号&#xff0c;默认是4723不用修改Start Server 启动 Appium serverEdit Confi…

网络安全课程VIP介绍(比同行便宜)

免责声明 本文发布的工具和脚本&#xff0c;仅用作测试和学习研究&#xff0c;禁止用于商业用途&#xff0c;不能保证其合法性&#xff0c;准确性&#xff0c;完整性和有效性&#xff0c;请根据情况自行判断。如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利&#xff0c…

(学习日记)2024.03.01:UCOSIII第三节

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

Java毕业设计-基于springboot开发的私人健身与教练预约系统-毕业论文+答辩PPT(有源代码)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1.开发说明2.需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、后台功能模块2.1管理员功能2.2用户功能2.3教练功能 四、毕设内容和源代码获取总结 [Java毕业设计-基于springboot…

零拷贝技术深入分析

一、零拷贝 在前面的文章“深浅拷贝、COW及零拷贝”中对零拷贝进行过分析&#xff0c;但没有举例子&#xff0c;也没有深入进行展开分析。本文将结合实际的例程对零拷贝进行更深入的分析和说明。 在传统的IO操作中&#xff0c;以文件通过网络传输为例 &#xff0c;一般会经历以…

【前端素材】推荐优质在线花卉商城电商网页Flowery平台模板(附源码)

一、需求分析 1、系统定义 在线花卉商城是一个通过互联网提供花卉销售服务的电子商务平台&#xff0c;用户可以在该平台上浏览、选择和购买各种花卉产品。 2、功能需求 在线花卉商城是一个通过互联网提供花卉销售服务的电子商务平台&#xff0c;用户可以在该平台上浏览、选…

内存取证 Volatility

文章目录 安装工具volatility和插件mimikatz[陇剑杯 2021]内存分析 内存分析工具 volatility&#xff0c;有Volatility2和Volatility3两种&#xff0c;分别基于Python2和Python3环境运行。说是一般Volatility2比Volatility3好用&#xff0c;所以我也选择的Volatility2版本。 一…

kubectl 陈述式资源管理方法

目录 陈述式资源管理方法 项目的生命周期 1.创建kubectl create命令 2.发布kubectl expose命令 service的4的基本类型 查看pod网络状态详细信息和 Service暴露的端口 查看关联后端的节点 ​编辑 查看 service 的描述信息 ​编辑在 node01 节点上操作&#xff0c;查看…

LeetCode 2120.执行所有后缀指令

现有一个 n x n 大小的网格&#xff0c;左上角单元格坐标 (0, 0) &#xff0c;右下角单元格坐标 (n - 1, n - 1) 。给你整数 n 和一个整数数组 startPos &#xff0c;其中 startPos [startrow, startcol] 表示机器人最开始在坐标为 (startrow, startcol) 的单元格上。 另给你…

前端的文字的字体应该如何设置

要设置文字的字体&#xff0c;在CSS中使用font-family属性。这个属性可以接受一个或多个字体名称作为其值&#xff0c;浏览器会按照列表中的顺序尝试使用这些字体渲染文本。如果第一个字体不可用&#xff0c;浏览器会尝试使用列表中的下一个字体&#xff0c;依此类推。 字体设…

SpringCloud gateway限流无效,redis版本低的问题

在使用springCloud gateway的限流功能的时候&#xff0c;配置RedisRateLimiter限流无效&#xff0c;后来发现是Redis版本过低导致的问题&#xff0c;实测 Redis版本为3.0.504时限流无效&#xff0c;改用7.0.x版本的Redis后限流生效。查了资料发现很多人都遇见过这个问题&#x…

让面试官眼前一黑,手把手带你打造个性化的 GitHub 首页

前期回顾 手机打开 第三方 “微信、快手、QQ、电话、信息” 等-CSDN博客https://blog.csdn.net/m0_57904695/article/details/136304084?spm1001.2014.3001.5501 &#x1f6a9;Github访问 Huo-zai-feng-lang-li (彩色之外) (github.com) &…

uniapp实现-审批流程效果

一、实现思路 需要要定义一个变量, 记录当前激活的步骤。通过数组的长度来循环数据&#xff0c;如果有就采用3元一次进行选择。 把循环里面的变量【name、status、time】, 全部替换为取出的那一项的值。然后继续下一次循环。 虚拟的数据都是请求来的, 组装为好渲染的格式。 二…

【python基础学习04课_python的字典】

字典 一、字典的定义 1、定义 字典&#xff1a;具有键值对 映射关系的一组无序的数据组合key: value key不变(不能够重复的&#xff0c;通常用str) value可变(可以用很多类型)通过key来找到对应的value标识符&#xff1a;{}关键字: dict无序&#xff1a;没有下标 2、打印…