拿捏指针(一)

这篇具有很好参考价值的文章主要介绍了拿捏指针(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说到指针可能很多人既熟悉又陌生。那么什么是指针呢?

目录

1.0 定义和地址

 1.1 指针定义及

1.2 &取地址操作符

1.3 指针变量

2.0 *解引用操作符

2.1 指针解引用

3.0 指针中的const

3.1 const在*号左边

3.2 const在*号右边

4.0  void*指针

5.0 指针的运算

5.1 指针+-整数

5.1.1 普通

5.1.2 进阶

5.2 指针-指针

5.3 指针的关系运算 

6.0 野指针

 6.1野指针的成因

6.2 规避野指针

7.0 assert

8.0 传值调⽤和传址调⽤

8.1 传值调用

 8.2 传址调⽤


1.0 定义和地址

 1.1 指针定义及

指针是一个变量,它存储了指向另一个变量的内存地址。它可以用来间接地访问和修改这个变量的值。每个变量在内存中都有一个唯一的地址,指针通过存储这个地址来引用其他变量。指针可以用于在程序中传递和操作内存地址,从而使程序能够更高效地访问和操作内存中的数据。

这样说你可能,还是听不懂,那我们举个例子:

这一天有八位客人在前台登记了入住,分别是a,b,c,d,e,f,g,h。他们一起住在酒店的一个楼层

拿捏指针(一),C语言,c语言,开发语言

这就类似于指针,通过地址能让我们准确的找到想找的人。

我们回归到计算机中来,每次房间相当于一个字节,char刚刚好就是一个字符,但酒店也有大房间,有四个字节的int,八个字节的long.....而这些地址在C语言当中就被叫做指针。也可以理解为,

房间编号=地址=指针。

在32位下有32根总地址线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么 ⼀根线,就能表⽰2种含义,2根线就能表⽰4种含 义,依次类推。32根地址线,就能表⽰2^32种含 义,每⼀种含义都代表⼀个地址。同理在64位下,有64根总线,就能表示2^64种含义的地址。

1.2 &取地址操作符

取地址操作符用于获取一个变量的地址。

每个字符都有属于自己的地址。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int main()
{
	int a = 2;  
	return 0;
}
//a的地址
//0x004FFDF0
//0x004FFDF1
//0x004FFDF2
//0x004FFDF3

 拿捏指针(一),C语言,c语言,开发语言

这就是整形(int)a向内存申请的四个字节,那么我们应该怎么将地址存起来 ? 

这就需要&(取地址操作符)&a将a的地址取出来。

int main()
{
	int a = 2;
    &a;
	return 0;
}

1.3 指针变量

我们将a的地址取出来,但是要将它存到哪里呢?

这是时候就需要用到指针变量将这个地址给存起来。

int main()
{
	int a = 2;
	int* p1 = &a;
	return 0;
}

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址

那么,int*和int应该怎么理解呢?

拿捏指针(一),C语言,c语言,开发语言

p的左边是int*,*是在说明p是指针变量,而前面的int在说明a是整形,而指针变量p指向的是int(整形)a的内容。

如果我们创建的变量是char  a ='we',那么储存变量的类型也要是char * 。

那么指针变量的大小是多少?占多少个内存?

拿捏指针(一),C语言,c语言,开发语言拿捏指针(一),C语言,c语言,开发语言

  • 在32位的平台下,指针的大小是4个字节。
  • 在64位的平台下,指针的大小是8个字节。

注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

2.0 *解引用操作符

我们要存放东西,拿东西就需要地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。

定义:解引用操作符(*)用于访问指针所指向的内存地址中存储的值。当使用解引用操作符对指针进行解引用时,实际上是在访问指针所指向的内存单元。

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

以上代码,*p=10;*p意思就是通过p中存放的地址,找到所指向的空间,*p其实就是a变量,所以*p = 10,就是把操作符a改成0。

但为什么我们要大费周章的改,不直接对a就行更改呢?有没有意义?

那肯定是有的,对a的修改,多了一种途径,写代码就会更加灵活。

2.1 指针解引用

//代码1
int main()
{
	int n = 0x11223344;
	int* p = &n;
	*p = 0;
	return 0;
}
//代码2
int main()
{
	int n = 0x11223344;
	char* p = (char* )&n;
	*p = 0;
	return 0;
}

我们调试时代码1时会发现,代码1会将n的四个字节全部改为0,而代码2只有一个字节会被改为0。

这我们就能得出,指针变量初始化是根据变量的类型来决定的,类型决定了访问的字节大小。

3.0 指针中的const

将变量的地址传给指针变量,通过指针变量同样可以修改变量的值。但有时候我们不希望这个值或者地址被修改,我们用到什么呢?没错就是const。

3.1 const在*号左边

 拿捏指针(一),C语言,c语言,开发语言

当const在*号左时,我们可以这样理解,*p指向指针的内容,内容锁死了,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。我们要修改它的值的时候,编译器就会报警告,无法修改。

将*p注释后则会打印5。

拿捏指针(一),C语言,c语言,开发语言

3.2 const在*号右边

拿捏指针(一),C语言,c语言,开发语言

当const在*号左时,p修饰的是指针变量本身,p里面的地址就被锁死,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变。

我们将p=&b注释掉,看看打印的值拿捏指针(一),C语言,c语言,开发语言

4.0  void*指针

void*指针是无类型的指针,可以接受任何类型的指针,但void*类型的指针不能直接进⾏指针的+-整数和解引⽤的运算。

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

vs编译器的结果

拿捏指针(一),C语言,c语言,开发语言

又以上结果可以得知,void*可以接收任意类型的指针但是不能进行运算。 

5.0 指针的运算

5.1 指针+-整数

5.1.1 普通

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

打印结果:

&n     =00EFF8A0
pc     =00EFF8A0
pc+1 =00EFF8A1
pi      =00EFF8A0
pi+1  =00EFF8A4 

我们可以看出来,pi和pc跳过的大小是不一样的,pi的类型是(int*)所以跳过 四个字节;而pc的类型是(char*)跳过一个字节。

总结:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

5.1.2 进阶

因为数组在内存中是连续存放的,所以只要知道了首元素地址,就能知道后边的元素的地址和值。

数组 1 2 3 4 5 6 7 8 9 10
下标 0 1 2   3 4 5 6 7 8 9
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(p + i));//(p+i)就是指针+整数,同理-也是可行的
	}
	return 0;
}

5.2 指针-指针

 //指针-指针
#include <stdio.h>
int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}
int main()
{
	printf("%d\n", my_strlen("abc"));
	return 0;
}

或许有人会感觉指针-指针很奇怪,难于解决。那么我们画图来看看就明白了

拿捏指针(一),C语言,c语言,开发语言

5.3 指针的关系运算 

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

6.0 野指针

野指针概念:野指针(或称为悬空指针)是指一个指针变量指向了无效的内存地址或者已被释放的内存,而访问该指针所指向的内存会导致不可预测的行为或程序崩溃。

 6.1野指针的成因

(1)指针未初始化

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

(2) 指针越界访问

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		//当指针指向的范围超出数组arr的范围时,p就是野指针
		printf("%d\n", arr[i]);
		* (p++) = i;
	}
	return 0;
}

拿捏指针(一),C语言,c语言,开发语言

最后圈起来的这两个值就是随机值,也就是野指针。

(3) 指针指向的空间释放

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

6.2 规避野指针

(1)指针初始化

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

#ifdef __cplusplus
 #define NULL 0
 #else
 #define NULL ((void *)0)
 #endif

初始化如下:

#include <stdio.h>
int main()
{
	int num = 10;
	int* p1 = &num;
	int* p2 = NULL;
	return 0;
}

(2)⼩⼼指针越界

⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是 越界访问。

(3)指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性

(4)避免返回局部变量的地址

7.0 assert

assert头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”。

#include <stdio.h>
#include <assert.h>
int main()
{
	int a = 0;
	assert(a != 0);
	return 0;
}

宏接受⼀个表达式作为参数。上面该表达式为真(返回值⾮零), 任何作⽤,程序继续运⾏。如果该表达式为假(返回值为零), 流 st assert() 不会产⽣ assert() 就会报错,在标准错误 derr 中写⼊⼀条错误信息,显⽰没有通过的表达式,以及包含这个表达式的⽂件名和⾏号。

8.0 传值调⽤和传址调⽤

8.1 传值调用

int Add(int x ,int b)
{
	return x + b;
}
int main()
{
	int a = 2;
	int b = 3;
	int ret = Add(a, b);
	printf("%d", ret);
	return 0;
}

上面代码就是将a和b的值,传到Add函数里,这就是简单的传值。

 8.2 传址调⽤

写⼀个函数,交换两个整型变量的

打印

 交换前:a=2 b=3
交换前:a=2 b=3

我们发现传值只是将数值拷贝了一份,但a和x,b和y的地址是不一样的。在函数中x和y进行了交换,返回的值还是Swap函数调⽤结束后回到main函数,a和b的没法交换。Swap1函数在使⽤ 的时候,是把变量本⾝直接传递给了函数,这种调⽤函数的⽅式我们之前在函数的时候就知道了,这 种叫传值调⽤。

既然传值解决不了问题那只能用传地解决。

void Swap(int* px, int* py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int a = 2;
	int b = 3;
	printf("交换前:a = % d b = % d\n", a, b);
	Swap(&a, &b);
	printf("交换后:a = % d b = % d\n", a, b);
	return 0;
}

打印结果

交换前:a = 2 b = 3
交换后:a = 3 b = 2 

这⾥调⽤Swap函数的时候是将变量的地址传 递给了函数,这种函数调⽤⽅式叫:传址调⽤


谢谢观众老爷们观看。 文章来源地址https://www.toymoban.com/news/detail-843948.html

到了这里,关于拿捏指针(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言:动态内存(一篇拿捏动态内存!)

    目录 学习目标:  为什么存在动态内存分配  动态内存函数: 1. malloc 和 free 2. calloc 3. realloc 常见的动态内存错误: 1. 对NULL指针的解引用操作 2. 对动态开辟空间的越界访问 3. 对非动态开辟内存使用free释放 4. 使用free释放一块动态开辟内存的一部分 5. 对同一块动态内存多次

    2024年02月10日
    浏览(48)
  • ChatGPT微信开发,轻松拿捏

    在人工智能领域, Ai 已经是一个屡见不鲜的东西了,为什么这次 openAi 推出的 chatGPT 却异常的受人欢迎?其实这还得益于 GPT 模型。 那么什么是 GPT 模型?我们可以看一下 chatGPT 自己的回答: GPT(Generative Pre-trained Transformer) 是一种基于变换器的语言模型,用于自然语言处理

    2024年02月08日
    浏览(40)
  • [开发语言][python][c++]:C++中的this指针和Python中的Self -- 26岁生日

    以朋友的新岁祝福开篇,祝笔者也祝大家☺️: 之前一直对 C++ 中的 this 和 python 中的 self 到底是什么关系,为什么 C++ 要显式的写出来,python 则不需要? 模糊不清,趁着周末整理一下相关结论,希望本篇文章可以解答这些问题,同时对C++和Python中的类加深些理解。 python 当

    2024年01月24日
    浏览(71)
  • C/C++|物联网开发入门+项目实战|指针|嵌入式C语言高级|C语言内存空间的使用-学习笔记(9)

    参考: 麦子学院-嵌入式C语言高级-内存空间 内存类型资源地址、门牌号的代名词 指针:地址的代名词 指针变量:存放指针这个概念的盒子 *P char *p *p; C语言娟译器对指针这个特殊的概念,有2个疑问? 1、分配一个盒子,盒子要多大? 在32bit系统中,指针就4个字节 2、盘子里存放

    2023年04月22日
    浏览(70)
  • C语言:字符函数和字符串函数(一篇拿捏字符串函数!)

    目录 求字符串长度: 1. strlen(字符串长度) 长度不受限制函数: 2. strcpy(字符串拷贝) 3. strcat(字符串追加) 4. strcmp(字符串比较) 长度受限制函数: 5. strncpy(字符串拷贝) 6. strncat(字符串追加) 7. strncmp(字符串比较) 字符串查找: 8. strstr(查找字符串子串) 9. strtok(字符串分割) 错误信

    2024年02月10日
    浏览(105)
  • 【开发工具】gitee还不用会?我直接拿捏 >_>

    🌈键盘敲烂,年薪30万🌈 目录 📕揭开git面纱 📕git的一些前置操作 📕如何获取本地仓库 📕本地仓库的操作 📕远程仓库操作 📕分支操作 📕标签操作 📕常见问题 注意:在使用git命令的时候一定要注意路径,在不同路径下执行相同命令结果也是不一样的例如创建和克隆仓

    2024年02月05日
    浏览(50)
  • [数据结构 -- C语言] 堆(Heap),你小子就是堆,看我如何透彻的将你拿捏

    目录 1、堆的概念及结构 1.1 概念(概念总是重要的) 1.2 结构,分为两种 1.2.1 小堆/小根堆示例 1.2.2 大堆/大根堆示例 2、堆的接口 3、接口实现 3.1 堆的初始化 3.2 堆的销毁 3.3 堆的插入 功能分析: 功能实现: 3.4 堆的删除 功能分析: 功能实现: 3.5 取堆顶的数据 3.6 堆的数据

    2024年02月07日
    浏览(50)
  • 【05】STM32·HAL库开发-C语言基础知识 | stdint.h介绍 | 位操作 | 宏定义的使用 | 条件编译 | extern声明 | typdef使用 | 结构体、指针、代码规范介绍。

      stdint.h 是从 C99 中引进的一个标准 C 库的文件,可以在MDK5的安装路径:D:MDK5.34ARMARMCCinclude中找到。   stdint.h 定义了很多类型别名,将有符号的char类型定义别名为int8_t等,使用此套别名有易于移植。   在MDK中需要配置才能支持使用S99标准, 默认是勾选的 。   只

    2024年02月08日
    浏览(44)
  • 【C语言】指针进阶:字符指针&&数组指针&&函数指针

    ✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 🌹 感谢您的点赞与关注,同时欢迎各位有空来访我的 🍁平凡舍 回想之前,我们学了 指针 的一些基础👉 指针与结构体 我们知道了指针的概念: 指针就是

    2023年04月08日
    浏览(43)
  • c语言指针(深入了解指针)

    前沿:       有人曾说过不会指针等于没有学习c语言,而我也是非常认同这个观点的,要想学习好c语言,指针是比不可缺少的,如果指针学不会c语言也就没办法学好,而向如此越重要的东西越比较难学,但难学并不代表学不会,这片文章将由简单到复杂让你深刻的了解指针

    2023年04月08日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包