C语言指针详解(1)

news2024/9/22 15:32:02

目录

一、什么是指针

1.1、定义

 1.2、取地址操作符(&)

 1.3、指针变量和解引用操作符(*)

二、指针变量类型的意义 

 三、const修饰指针

3.1、const修饰变量

3.2、const修饰指针变量

3.2.1、const放在*的左边 

3.2.2、 const放在*的右边

 四、指针运算

4.1、指针+-整数

4.2、指针-指针

4.3、指针的关系运算 

五、野指针 

5.1、定义

5.2、成因

5.2.1、指针未初始化

 5.2.2、指针越界访问

5.2.3、 指针操作超过所指向变量的生存期

六、传值调用和传址调用


一、什么是指针

1.1、定义

指针在C语言中也叫内存地址。那么什么是内存地址呢?以生活中的房间号为例,正因为生活中的这些房间号,我们才能快速找到要找的房间。而内存地址就相当于这些房间号,有了这些内存地址,CPU就能快速找到一个内存空间。

 1.2、取地址操作符(&)

在C语⾔中,我们想要取出一个变量的地址,就要使用一种操作符:&。例如:

#include <stdio.h>
int main()
{
	int a = 10;
	printf("%p\n", &a);
	return 0;
}

上述,我们先定义了一个整型变量a,并赋值为10,然后我们打印地址。 

注:打印地址时,用%p。

结果如下:

 1.3、指针变量和解引用操作符(*)

既然我们可以用&拿出一个地址,那么是否可以用什么东西来存储地址呢?答案是有的,这种东西就叫做指针变量。如下:

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	return 0;
}

既然我们将地址保存了起来,那么未来一定是要使用的。但是我们要怎样才能使用呢?这个时候就要用到另一种操作符:“*”(解引用操作符)。

#include <stdio.h>
int main()
{
	int a = 100;
	int* pa = &a;
	*pa = 10;
	printf("*pa=%d\n", *pa);
	printf("a=%d\n", a);
	return 0;
}

结果如下:

可以看到,通过解引用操作符,我们将*pa赋值为10,而a最终的结果也是10,由此我们可以知道:可以通过改变指针变量的值,间接改变指针变量所指向的变量的值。 

二、指针变量类型的意义 

请看如下代码,观察其运行结果:

#include <stdio.h>
int main()
{
	int a = 100;
	int* pa = &a;
	char* ch = &a;
	printf("&a=%p\n", &a);
	printf("pa=%p\n", pa);
	printf("pa+1=%p\n", pa+1);
	printf("ch=%p\n", ch);
	printf("ch+1=%p\n", ch+1);
	return 0;
}

 其结果如下:

我们可以看出,char*类型的指针+1后只跳过一个字节,而int*类型的指针+1后跳过了4个字节。由此,我们可以知道:不同的指针类型,加上或减去同一个数后,跳过的字节数不同。 

 三、const修饰指针

3.1、const修饰变量

变量是可以修改的,但是如果在变量前加上const,则变量便不能被修改。

3.2、const修饰指针变量

⼀般来讲const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不⼀样的。

3.2.1、const放在*的左边 
#include <stdio.h>
int main()
{
	int m = 10;
	int n = 20;
	int const* pa = &m;
	*pa = 20;//不可行,报错
	pa = &n;//可行
	return 0;
}

上述代码说明:const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。 

3.2.2、 const放在*的右边
#include <stdio.h>
int main()
{
	int m = 10;
	int n = 20;
	int* const pa = &m;
	*pa = 20;//可行
	pa = &n;//不可行,报错
	return 0;
}

上述代码说明:const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

 四、指针运算

4.1、指针+-整数

因为数组在内存中是连续存放的,所以只要知道第⼀个元素的地址,利用指针加减整数就能找到数组的所有元素。

#include <stdio.h>
int main()
{
	int arr[6] = { 1,2,3,4,5,6 };
	int* pa = &arr[0];
	int sz = sizeof(arr) / sizeof(arr[0]);//通过sizeof获取数组长度
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(pa + i));
	}
	return 0;
}

 结果如下:

4.2、指针-指针

先说结论:指针-指针的绝对值是两个指针之间元素的个数。

由此,我们可以自行写出一个计算字符串长度的函数my_strlen:

#include <stdio.h>
size_t my_strlen(char* ch) {
	char* strat = ch;
	char* end = ch;
	while (*end != '\0') {
		end++;
	}
	return end - strat;
}
int main()
{
	char arr[] = "abcdef";
	int num = my_strlen(arr);
	printf("%d\n", num);
	return 0;
}

结果如下:

4.3、指针的关系运算 

//指针的关系运算 
#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int sz = sizeof(arr)/sizeof(arr[0]);
 while(p<arr+sz) //指针的⼤⼩⽐较 
 {
 printf("%d ", *p);
 p++;
 }
 return 0;
}

 结果如下:

五、野指针 

5.1、定义

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。 

5.2、成因

5.2.1、指针未初始化
#include <stdio.h>
int main()
{
	int* p;//指针未初始化,默认为随机值
	*p = 10;
	return 0;
}

 规避方法:初始化指针:如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL。 NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。

 5.2.2、指针越界访问
#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i <= 11; i++){//指针越界访问
		*(p++) = i;
	}
	return 0;
}

当指针指向的范围超出数组arr的范围时,p就是野指针。

5.2.3、 指针操作超过所指向变量的生存期

当指针执行的变量的声明周期已经结束时,如果指针仍然指向这块空间,就会使该指针成为野指针。

六、传值调用和传址调用

假如叫你写⼀个函数,交换两个整型变量的值,你可能会写出如下代码:

#include <stdio.h>
void swap(int x, int y) {
	int c = x;
	x = y;
	y = c;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(a, b);
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

结果如下:

然后你震惊地发现,a和b的值并没有互换,这是因为a、b和x、y的地址不同,所以虽然x和y的值互换了,但是并不影响a和b的值。这就是传值调用。而传址调用则不同:

#include <stdio.h>
void swap(int* x, int* y) {
	int c = *x;
	*x = *y;
	*y = c;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(&a, &b);
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

 结果如下:

我们发现,这次a和b的值成功互换了,这是因为这次将a和b的地址传入了函数,函数通过地址间接操作main中的a和b,这就是传址调用。

未完待续...... 

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

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

相关文章

docker的安装+docker镜像的基本操作

一&#xff0e;docker的介绍 1、Docker 是什么&#xff1f; Docker 是⼀个开源的应⽤容器引擎&#xff0c;可以实现虚拟化&#xff0c;完全采⽤“沙 盒”机制&#xff0c;容器之间不会存在任何接⼝。 Docker 通过 Linux Container&#xff08;容器&#xff09;技术将任意…

中秋节送礼推荐,数码好物精选推荐

中秋节将至&#xff0c;想要为家人或朋友准备一份特别的礼物吗&#xff1f;不妨考虑南卡Runner Pro5骨传导耳机。这款耳机不仅在功能上表现出色&#xff0c;而且设计独特&#xff0c;非常适合作为节日赠品。 卓越的性能&#xff0c;完美的体验 南卡Runner Pro5凭借其卓越的性…

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——7.list(模拟实现)

1.前言 1.1list与vector的不同 区别&#xff1a;list的迭代器底层和其他两个迭代器底层有很大区别&#xff0c;因为list的链式结构决定了与它们两个的不一样 相同&#xff1a;迭代器用法大致一样&#xff0c;其他成员函数的使用也大致一样。 vector与list都是STL中非常重要的序…

关于安装hbase的问题(操作系统-windows)

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

快速学习“堆“排序(C语言数据结构)

前言&#xff1a; 堆的实现其实并不难&#xff0c;难的是要用堆实现排序&#xff0c;也就是堆的运用。 下面需要探究一下堆的排序是怎样的。 如何利用堆进行升序或者降序的排序。 "堆排序"&#xff1a; 原理&#xff1a; 例如&#xff1a;此时要将数组里的数组int a…

干货实用帖 | PARASOFT与JENKINS 插件集成

&#x1f4d6; 介绍&#xff1a; 本篇介绍如何使用Jenkins上的插件Parasoft Findings&#xff0c;应用到C/Ctest项目中。 ✅ 准备工作&#xff1a; Jenkins项目C/Ctest 10.4以上版本及有效的许可证 视频教学&#xff1a; Parasoft与Jenkins插件集成 安装插件&#xff1a; 首先…

Vue3 获取农历(阴历)日期,并封装日历展示组件

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是码喽的自我修养&#xff01;今天给大家分享vue3项目中使用 chinese-lunar-calendar 插件获取农历(阴历)日期&#xff0c;并封装了日历展示组件&#xff01;提供了具体的代码帮助大家深入理解&#xff0c;彻底掌握&#…

【舞动生命,营养护航】亨廷顿舞蹈症患者的维生素补给站

Hey小伙伴们~&#x1f44b; 在这个充满色彩的世界里&#xff0c;每个人都在以自己的方式绽放光彩。但你知道吗&#xff1f;有一群特别的朋友&#xff0c;他们面对着亨廷顿舞蹈症的挑战&#xff0c;却依然以不屈不挠的精神舞动着生命的旋律。&#x1f483;✨ 今天&#xff0c;就…

游戏如何对抗 IL2cppDumper逆向分析

众所周知&#xff0c;Unity引擎中有两种脚本编译器&#xff0c;分别是 Mono 和 IL2CPP 。相较于Mono&#xff0c;IL2CPP 具备执行效率高、跨平台支持等优势&#xff0c;已被大多数游戏采用。 IL2CPP 模式下&#xff0c;可以将游戏 C# 代码转换为 C 代码&#xff0c;然后编译为…

STM32学习记录-06-ADC模数转换器

1 ADC简介 ADC(Analog-Digital Converter)模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁 12位逐次逼近型ADC,1us转换时间 输入电压范围:0~3.3V,转换结果范围:0~4095 18个输入通道,可测量16个外部和…

FPGA在医疗方面的应用

可编程逻辑支持以灵活、低风险的方式成功实施系统设计&#xff0c;同时提供了最佳的成本效率和增值的差异化功能&#xff0c;延长了医疗保健应用的生命周期&#xff0c;包括诊断成像、电子医疗、治疗和生命科学与医院设备。 在医疗方面的应用非常广泛&#xff0c;以下是几个主…

Langchain Memory组件深度剖析:从对话基础到高级链式应用

文章目录 前言一、Langchain memory 记忆1.Memory 组件基本介绍2.Memory 组件的类型1.ChatMessageHistory2.ConversationBufferMemory3.ConversationBufferWindowMemory4.ConversationEntityMemory5.ConversationKGMemory6.ConversationSummaryMemory 二、长时记忆1.简单介绍2.…

Error: Can not import paddle core while this file exists

背景 因为工作需要&#xff0c;原来的项目部署的电脑被征用&#xff0c;重新换了一个新电脑&#xff0c;重装了系统&#xff0c;今天在给一个使用ocr的项目进行环境配置的时候发现&#xff0c;无论安装哪个版本的paddlepaddle&#xff0c;总是可以安装成功&#xff0c;但是导入…

Android CCodec Codec2 (四)C2Param - Ⅱ

这一篇内容我们来解答复杂参数定义过程中提出的疑问&#xff0c;本文有大量的模板和宏展开&#xff0c;请耐心阅读。 1、不含灵活数组的复杂结构体定义 DEFINE_AND_DESCRIBE_C2STRUCT和C2FIELD是不能分开的&#xff0c;使用时必须要按顺序依次调用这两个宏定义。宏定义比较复杂…

【机器学习-监督学习】神经网络与多层感知机

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

java-Mybaits框架01

1.框架概念 在基础语言之上&#xff0c;对各种基础功能进行封装&#xff0c;方便开发者&#xff0c;提高开发效率&#xff1b; java后端框架 mybaits&#xff1a;对jdbc进行封装 Spring&#xff1a;对整个java后端架构进行管理。 SpringWeb&#xff1a;对web&#xff08;S…

vxe-grid 利用dayjs提供的方法来格式化, 计算二个日期之间的年数/年龄

1、安装dayjs pnpm add dayjs yarn add dayjs npm install dayjs 2、导入 import dayjs from dayjs; 3、vxe-grid列&#xff1a; export const UserColumns: VxeGridPropTypes.Columns [ ... {title: 年龄,width: 70,field: old,showOverflow: tooltip,align: center,sortabl…

android studio 设置gradle jdk

1. 左上角点击file 2. 按照如下点击&#xff1a; 3. 即可修改gradle jdk

EasyExcel文件导出简洁版

1. EasyExcel简介 EasyExcel是一个基于Java的简单、快速、lightweight的Excel处理库。它的主要特点包括: 轻量级设计: EasyExcel的jar包大小仅约1MB,相比较其他Excel处理库如Apache POI来说更加轻量。 采用内存友好的流式读写模式,无需一次性加载整个Excel文件到内存,大大减少…

Linux的CPU调度优化详解

一、引言 随着计算机硬件技术的不断发展和进步&#xff0c;现代服务器和工作站通常都配备了多核CPU&#xff0c;为了充分发挥多核处理器的性能优势&#xff0c;Linux系统提供了多种CPU调度器以及相关的参数设置&#xff0c;以便进行CPU调度优化&#xff0c;提高系统的整体性能…