指针篇章-(1)

news2024/12/23 12:15:48

 指针(1)学习流程

———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 

内存和地址

内存

所谓的指针就是为了管理内存空间,划分的一个内存一个单元。

内存单元是计算机内存中的最小存储单位,通常以字节(Byte)为单位。每个内存单元都有一个独特的地址,以便CPU可以准确地找到并读写数据。
指针在C语言或其他允许指针操作的编程语言中非常重要,因为它们提供了直接访问和操作内存的能力。使用指针,程序可以避免数据复制,从而提高效率,尤其是在处理大型数据结构(如数组和复杂的数据结构)时。

地址 

内存单元的编号就是地址 就是指针

首先,必须理解,计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。所谓的协同,至少相互之间要能够进行数据传递。
但是硬件与硬件之间是互相独立的,那么如何通信呢?答案很简单,用"线"连起来
而CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。

CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址(就如同宿舍很多,需要给宿舍编号样)。
计算机中的编址,并不是把每个字节的地址记录下来而是通过硬件设计完成的。
钢琴、吉他 上面没有写上“剁、来、咪、发、唆、拉、西”这样的信息,但演奏者照样能够准确找到每一个琴弦的每一个位置,这是为何?因为制造商已经在乐器硬件层面上设计好了,并且所有的演奏者都知道。本质是一种约定出来的共识!
硬件编址也是如此
我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表示0,1【电脉冲有无】,那么一根线,就能表示2种含义,2根线就能表示4种含义,依次类推。32根地址线,就能表示2^32种含义,每一种含义都代表一个地址。
地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。

cpu内部寄存器地址线和数据线的布局。具体来说,在cpu的左边有8个地址线,从0到7,用于指定内存中的地址。每个地址线对应一个数据线,用于读写内存的数据。此外,还有一个控制总线(R/W),用于控制数据的读写操作。在cpu的右边是内核(Kernel)部分,它与地址线和数据线相连,用于接收来自内存的数据或指令。

通过物理线传到某一个过程 访问那个数据的内存

这里也有物理地址和虚拟地址

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

指针变量和地址

 指针变量是什么

1.指针是内存单元中一个最小的单元编号,也就是地址

2.平时口语说的指针,通常指的是指针变量,是用来存放内存地址的变量

总结:指针就是地址,口语中说的指针通常指的是指针变量,是用来存放内存地址的变量。

 地址大小决定指针变量的大小

这里我们就明白:
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以.
一个指针变量的大小就应该是4个字节。
那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址

总结:
指针变量是用来存放地址的,地址是唯一标示一个内存单元的,指针的大小在32位平台是4个字节,在64位平台是8个字节。 

指针变量的举例

这里我们需要知道创建了变量的本质是申请一块空间

当然这里可以看见,在系统申请空间的时候,是随机给一个地址的

也就是

在C语言中,变量的内存分配通常是在程序的堆栈(stack)或堆(heap)上进行的。堆栈通常用于存储局部变量,而堆则用于存储动态分配的内存。当程序执行到声明变量的语句时,系统会为该变量分配一块内存空间,并将其地址赋给变量。
关于地址的选择,现代操作系统和编译器通常会使用一种称为“地址空间布局随机化”(Address Space Layout Randomization, ASLR)的技术来提高安全性。ASLR会确保每个进程的内存地址空间是随机的,这样就增加了攻击者利用内存漏洞的难度。因此,每次程序运行时,即使它申请的内存空间大小相同,分配的内存地址也可能不同。

这里引入

&单目操作符(取地址操作符)

#define _CRT_SECURE_NO_WARNINGS 1
int main()
{
	int a = 100;
	int arr[10];
	return 0;
}

 %p打印地址

&单目操作符(取地址操作符)

取地址 取出来的是较小的地址 当知道较小的地址后 顺藤摸瓜就找到了

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 100;
	int arr[10];
	printf("%p\n", &a);
	return 0;
}

 此时p也就是一个指针变量

当然 此时的语法还不对 但是此时取出的a的地址 给到了p 此时p也就是我们口中说的指针变量

这里平时说的p是指针 其实说的p是指针变量

取出来的地址 放到p里面 所以p是指针 也就是指针变量

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 100;
	int *p = &a;

	//int arr[10];
	//printf("%p\n", &a);
	return 0;
}

p是指向a的

因为p可以找到a

所以p是指向a的

*说明是指针变量 int 说明p指向的对象是int类型的

指针变量就是存放地址所以引用指针变量

任何东西只要放到指针里面 就呗指针认为是指针变量

这里不仅仅只是为了存放地址

此时p可以很好的找到a的地址

此时*p叫解引用操作符 或者间接访问操作符

这里需要知道

对p解引用 找到的结果找到的是a

取出a的地址 解引用 然后此时a就等于赋值的数值

关于指针变量大小

等同于找老莫我想吃鱼一样 老大不方便动手 小弟动手

地址是由地址线传入 一个地址线产生一个二进制位数 32个地址线产生2的32次方位地址线

所以在平台上面 x86的环境下面

一个指针的大小需要四个字节

所以地址长度决定指针变量的长度

所以64位就是一个指针就是8个字节

举例 32位的情况下 都是四个字节 和类型无关

这里插入两个内容 关于环境和计算 有利于指针的学习

计算机基础( 计算机里面为什么是32位指的是什么)(C语言代码举例)(画图超详细解析)_c语言中32位是什么意思-CSDN博客

计算机基础知识讲解(原码反码补码)(以及在C语言里面是如何计算和运用的)-CSDN博客 

这里需要知道x86 是32位的环境

至于是为什么

"x86"这个名称来源于Intel在1978年推出的8086处理器,它是第一个真正意义上的16位微处理器。后来,Intel和AMD等公司推出了基于8086架构的扩展和改进型号,如80286、80386和80486等。这些处理器虽然技术上仍然是16位的,但它们引入了扩展寄存器和指令,使得它们能够处理32位数据。
由于这些处理器在市场上获得了巨大成功,人们开始将它们统称为"x86"架构。这个名称并没有明确指出它是16位还是32位,但实际上,从80386开始,"x86"架构就已经支持32位操作了。
因此,"x86"这个名称并不是指32位环境,而是指一系列基于8086架构的处理器。不过,由于这些处理器在32位时代获得了广泛应用,人们往往用"x86"来指代32位环境。这种用法虽然不太准确,但在实际应用中却是普遍接受的。

回归正题

 x64 就是八个字节

这里需要知道

指针里面存方的一般是16进制

至于为什么是16进制 往往是因为16进制计算范围较大 比较方便

但是需要知道的是

存放的二进制 显示的16进制 简单说就是方便

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

指针类型

没有解引用之前 a的地址确实给到pa

但是当解引用的时候只改变了第一个字节

说明 指针类型决定指针的权限

整形指针一次只能访问四个字节

这就是指针类型的意义

如果你想访问一个字节 char

如果是四个字节 用int

再多就是float等等

因为这里你需要知道,在初始化的时候,分配内存和数值是随机分配的,你进行指针的访问,也只是根据类型来进行访问

指针类型的意义

 指针类型决定了指针在进行解引用操作符的时候访问几个字节,也就是决定指针的权限

加上数字 发现不一样 int*和char*每次跳过的也不一样

指针类型的计算

指针类型决定+1 -1的时候一次走的距离

指针类型决定向前或者向后走一步 多少距离

 简单的说就就是,此时打印出来的地址,减去之前的地址是大四个字节的

从而引出指针类型的意义

根据上述,我们反过来讲解指针类型的意义

从而引出指针类型的意义

此时是短整型char 前后差距是一个字节 

但是是int的情况下 前后差距就是四个字节

解引用不同的类型之间转化

对于指针解引用不同的类型访问不同的字节长度

取出的类型是一个整形的类型

但是放到了短整型里面

此时就会导致数据的丢失

void可以存放任何类型的地址 因为没有具体类型

在C或C++等编程语言中,
`void` 指针是一种特殊的指针类型,它不代表任何具体的内存地址类型。
`void` 指针可以用来存放任何数据类型的地址,因为它是所有类型的基类型。
但是,需要注意的是,虽然 `void` 指针可以存储任何类型的地址,
但是不能直接使用它来访问或操作这些地址所指向的数据,因为编译器不知道具体的数据类型,也就无法进行正确的类型匹配和内存布局。
在实际应用中,如果想要通过 `void` 指针访问具体类型的数据,需要进行类型转换,即将 `void` 指针强制转换为相应的类型指针。
例如:
```c
int a = 10;
void* void_ptr = &a;
int* int_ptr = (int*)void_ptr;
*int_ptr = 20; // 现在 a 的值变为 20
```
在这个例子中,`void_ptr` 存储了 `int` 类型变量 `a` 的地址,
但是不能直接通过 `void_ptr` 来修改 `a` 的值。我们通过类型转换得到了 `int` 指针 `int_ptr`,
然后就可以通过 `int_ptr` 来修改 `a` 的值了。

使用 `void` 指针时需要格外小心,
因为错误的类型转换会导致未定义行为,比如内存访问越界,这可能会导致程序崩溃或产生不可预测的结果。
因此,在使用 `void` 指针时,确保进行正确的类型转换和内存管理是非常重要的。

所以在不确定是什么类型的时候 用void类型的接受

不过需要知道的是

void具有局限性 因为没有具体类型

所以无法运算访问字节

如图

这里显示的是未知的大小

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

const

首先解释一下const

 在C语言中,'const'用于声明一个只读的变量。这意味着该变量一旦被初始化,其值就不能被改变。常量通常用于表示某些固定的、不变的值,例如物理地址、尺寸或数学常数等。

在C语言中,`const` 是一个关键字,用于修饰变量,以表明它们的值在定义之后不能被改变。`const` 关键字具有以下两个主要属性:
1. 只读属性:被 `const` 修饰的变量在初始化之后,其值不能被修改。这意味着一旦你给一个 `const` 变量赋了一个值,你就不能再次给它赋一个新值。
2. 类型安全:`const` 关键字告诉编译器该变量是一个常量,因此在程序执行期间,试图修改这样一个变量的值会导致编译错误或运行时错误(取决于如何尝试修改它)。
`const` 可以用在变量声明的任何位置,具有不同的含义:
- `const` 在变量类型之前:表示变量是一个只读的常量,不能被赋予初始值,必须在声明时初始化,并且它的值在程序的整个运行期间都不可更改。
- `const` 在变量类型之后:表示变量本身是可变的,但其指向的数据是只读的。这在指针中非常有用,可以确保指针指向的数据不被修改,但指针本身(即内存地址)可以改变。
例如:
```c
const int a = 10; // 声明一个常整型变量,初始化为10,之后不能更改
int const b = 20; // 同上,这两种声明方式等效
int arr[10];
const int n = 5; // 声明一个常整型变量,表示数组的大小
int* ptr = arr;
const int* const ptr_to_const = arr; // 声明一个指向常量的常量指针
// ptr_to_const指向的arr数组元素是常量,不能被修改,同时ptr_to_const本身的值也不能被改变
```
使用 `const` 可以提高代码的可读性和可维护性,同时也帮助编译器进行更好的优化。此外,`const` 关键字在C++中还有更多的用途,例如用来定义常量对象和成员函数,但在C语言中,它的使用主要局限于变量声明。

简单的说 ,可以片面的理解为,就是他存在的意义就是,防止新手修改老手的代码的时候,不小心吧东西修改错误

const 本质

下面我会介绍什么时候回进行修改 以及范围 当然在这之前先介绍完const本身的属性

const 本质 是不能修改 

此时用const修饰a之后 

这里的a具有了长属性 长属性就是不能被修改的属性

这里需要知道

虽然a不能被修改,但是本质还是变量 就是常变量

但是在c和c++里面const是不一样d

在c里面 const修饰的是变量

在c++里面则是常量

所以写C语言的话 后缀就要写c 

但是这样是不符合规则的

为什么是不合规的,前面已经说明 ,这里再详细的说一下

`const int a = 10;` 声明了一个常量 `a`,它的值不能被改变。接着,`int* p = &a;` 声明了一个指向 `a` 的指针 `p`。
然后,`*p = 0;` 这行代码试图修改通过指针 `p` 所指向的内存位置的值。由于 `a` 是一个 `const` 变量,它的值不能被改变,所以这行代码是不正确的,会导致编译错误。
在 C 语言中,`const` 关键字的作用是确保变量在其生命周期内不被修改。尝试修改一个 `const` 变量的值会导致编译错误。因此,如果您尝试执行 `*p = 0;`,编译器会报错,因为它不允许您通过指针 `p` 来修改 `a` 的值。
正确的做法是只读取 `a` 的值,而不是尝试修改它。例如:
```c
const int a = 10;
int* p = &a;
// 正确的使用方式是读取a的值,而不是修改它
printf("%d\n", *p); // 输出 10
```
如果想要修改 `a` 的值,需要首先取消 `const` 限制,或者使用另一个变量来存储 `a` 的值,然后修改那个变量的值。例如:
```c
int a = 10;
int* p = &a;
*p = 0; // 正确,修改了a的值
```
或者:
```c
const int a = 10;
int b = a; // b 是一个普通变量,可以被修改
b = 0; // 正确,修改了b的值,而不是a
```

const的使用规则

 所以此时p指向的是a的地址

所以这个时候可以理解为 p本身就有一个地址 然后p指向了一个地址 p里面存放了a的地址

所以

p指向一个地址

p本身有一个地址

p存放一个地址

数值出来是100

const修饰指针变量的时候放到*的右边 限制的是指针变量本身,指针变量不能指向其他变量 但是可以修改指针变量指向的内容

p不能修改 但是*p指向的内容可以进行修改

const放到*左边

修饰指针变量的时候

此时限制的是*p

但是可以修改指针变量本身

也就是此时 指针指向的内容是不能发生变化的 但是指针(也就是指针变量本身是可以发生变化的)

const生动举例

当const在*左边的时候、,,const* ,,这里假设有一对男女朋友,这个有一天女孩说,我想吃雪糕需要五十块 ,这个男孩说那我没钱一共十块钱,买不起,那女孩就不高兴了,说:那你的钱(本身的变量)不发生变化的话,那我只能变化,换男朋友了。

当const在*右边的时候、,,*const ,,这里假设还是这一对男女朋友,还是这个场景,这个有一天女孩说,我想吃雪糕需要五十块 ,这个男孩说那我没钱一共十块钱,但是没事我借钱40,(男孩这个指针指向的变量发生了变化),买了一个雪糕还有0元,那女孩就继续跟着你了,也就是指针变量本身没有发生变化

 

const在*左边指针变量本身的地址变化 但是指向的内容不变化

所以这里就是你不给我花钱 指针女孩换男朋友m->n 女孩本身变化 男朋友的钱m数值不变化

此时m愿意花钱 然后等于把const放到右边 等于m愿意花钱 女孩指针p本身不变化 不换男朋友,但是男朋友m的钱发生变化 也就是指针变量本身不发生变化,但是指向的变量可以发生变化

当然两边也都可以加上const数值

总结

想搞定变量*const int

想搞定指针变量本身 const * int

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

assert断言

这里先简单讲一下assert的断言,下面会有assert断言和野指针的结合使用 

测试和写代码直接转换问题 

这里链接一下 relase和debue的区别关于Visual Studio Installer总是出现(LNK1168无法打开 )(不需要关机直接处理好)_vs installer打不开-CSDN博客

测试和写代码直接转换问题

这里讲一下为什么在这个测试和写代码直接转换的时候会不一样

C语言在测试和写代码时可能会出现不一样的结果,这主要是由于以下几个原因:
1. **浮点数精度问题**:C语言中的浮点数计算通常不会得到精确的结果,因为计算机内部用二进制表示浮点数时,无法精确表示所有的小数。所以在不同平台或者不同编译器上运行时,浮点数的运算结果可能会有细微的差异。
2. **Endianness(字节序)**:字节序是指计算机中字节存储的顺序。C语言中没有明确指定字节序,这可能导致在不同硬件平台上,多字节数据(如整数、浮点数)的存储顺序不同,从而影响数据的解析和运算。
3. **编译器和运行时的优化**:编译器在将C代码编译成机器码时,可能会进行各种优化。这些优化可能会改变代码的执行逻辑或者运算结果。另外,运行时的库函数也可能对输入数据进行不同的处理。
4. **测试环境和写代码的环境差异**:如果在不同的环境(如不同的操作系统、硬件平台、编译器版本等)中进行测试和编写代码,可能会因为环境的差异导致结果不同。
5. **代码的实现细节**:在编写代码时,可能会因为实现的细节不同(例如,算法效率、边界条件处理等)而在测试时得到不同的结果。
为了减少这些差异,通常需要在代码中加入适当的注释,说明可能存在的差异,并在文档中详细描述测试环境。此外,也可以使用标准库函数,如`<stdint.h>`中定义的固定宽度整数类型,来避免因为浮点数精度和字节序引起的问题。在编写和测试代码时,应尽可能在相同的环境中进行,以确保结果的一致性。

assert 存在的意义

 assert可以确保传过来的指针不是空指针

`assert`是一个在C语言中广泛使用的宏,它的存在主要有以下意义:
1. **调试辅助**:在开发过程中,`assert`用于在运行时检查程序的假设是否成立。如果`assert`语句的条件评估为假,则程序会抛出一个断言失败异常,通常会导致程序终止并输出错误信息,这样可以帮助开发者快速定位问题所在。
2. **静态代码分析**:在编译时,`assert`可以作为静态代码分析的工具。通过检查`assert`语句,编译器可以确保程序在运行之前满足一定的条件。这有助于在编译阶段发现潜在的错误,而不是在运行时才暴露出来。
3. **文档和代码的同步**:`assert`的使用可以在一定程度上作为代码注释,它表明了程序员对程序某一部分的预期。这样,其他阅读代码的人可以更清楚地理解代码的假设和约束。
4. **性能分析**:在某些情况下,`assert`可以用来测试代码路径是否应该被执行。如果`assert`失败,这意味着程序处于一个不应该到达的状态,这可能有助于发现性能瓶颈或逻辑错误。
5. **代码简洁性**:使用`assert`可以避免在代码中充斥着大量的错误检查和异常处理代码,使得正常逻辑更加清晰。
6. **沙盒环境的创建**:在某些安全敏感的应用中,`assert`可以用来创建沙盒环境,确保程序在特定的安全边界内运行。
总之,`assert`是C语言中一个非常有用的工具,它有助于提高代码的质量和开发效率,同时也使得代码更加健壮。然而,需要注意的是,`assert`不应该用于修复错误,而应该仅用于检测错误。在生产环境中,应该确保`assert`语句在编译时被正确处理,或者在运行时不会对程序的稳定性产生影响。

assert具体举例 

需要包含头文件assert.h 

这个代码的健壮性更好

限制了所改变的内容

 ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strlen

strlen存在的意义

`strlen`是C语言标准库函数之一,它的作用是计算字符串的长度,不包括终止符`\0`(空字符)。`strlen`函数在`string.h`或`stdlib.h`头文件中定义。
`strlen`的意义和用途包括:
1. **获取字符串长度**:`strlen`函数可以方便地获取一个字符串的长度,这对于许多字符串操作(如字符串比较、复制、连接等)是非常必要的。
2. **内存分配**:在动态分配内存时,通常需要知道字符串的长度,以便为字符串加上结束符`\0`并分配足够的空间。`strlen`可以提供这个长度信息。
3. **字符串处理**:在进行字符串处理时,如搜索、替换、截断等操作,需要知道字符串的实际长度。`strlen`函数提供了这个信息。
4. **安全性**:在使用字符串时,`strlen`可以帮助避免缓冲区溢出等安全问题。例如,在复制字符串时,知道目标缓冲区的大小和源字符串的长度可以确保不会超出缓冲区的边界。
5. **跨平台兼容性**:`strlen`是C语言标准库的一部分,因此它在不同的操作系统和编译器之间具有很好的兼容性。
使用`strlen`时需要注意的是,由于`strlen`不考虑字符串中的宽字符或多字节字符,它只计算单字节字符的长度,因此在处理非ASCII字符串时可能不够准确。对于宽字符或多字节字符串的长度计算,应使用相应的库函数,如`wcslen`(宽字符串长度)或`strlen`的变体,这些函数能够正确处理多字节字符。 

具体的举例 

这里是和assert进行了一个结合 

利用assert代码加入到代码 里面会减少出错性(这里涉及野指针的,下面会讲到) 

包含头文件

string.h或stdlib.h头文件中定义

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

指针的基本算法 

指针的作用就是访问内存

指针的加法运算 

指针的运算和访问内存息息相关下面会讲到 

 

这里是整形,一个整形是四个字节,在指针的类型里面已经讲到 

当然不能搞混

所以逻辑就是

代码举例

指针版本1

下标方式访问

指针方式访问

指针加减 整数

解引用

每次指针+1

每次加一之后则变化指向的数值

如图 

指针版本2

这个运行的前提条件是数组在内存里面是连续存放的

指针的减法运算(指针-指针)

指针的运算

指针-指针

指针-指针绝对值

得到是两个指针之间的元素个数

没有意义

不是所有的计算都是有意义的

而指针的计算应该是有意义的

指针-指针的运算

低地址和高地址的区别

指针-指针限制条件必须指向同一空间

所以计算出的结果是空间的长度 

 

但是像这个计算出的结果 ,因为不是一个空间所以计算出的结果不是中间的长度,而是没有意义的运算 

指针里面strlen的运算

统计的是\0之前出现的个数

包含头文件

strlen头文件string

数组名相当于首元素地址

字符的地址用字符指针接收

strlen 指针的运算方式1

这里是数组名每次进行++ 然后最后求出总的字符的长度 

strlen指针的运算方式2

这个采取的是指针-指针的运算方式

也就是同一空间 start=str 这里的str是首元素地址 也就是指向的是首元素

然后每次进行str++再减去首元素地址 也就是指针-指针的运算

因为

这里str最后指向的是str这个空间的最后一个元素

start指向的是身str的首元素地址 也就是这个空间的第一个元素

指针的关系大小的运算

区分 

运算的代码 

这里是首元素的地址 

 

 sz是长度,p<arr+sz意思就是循环条件就是小于首元素地址加上整体长度

此时是不存在越界访问的情况的因为是小于而不是是小于等于

然后打印*p

每次P++

从而实现代码

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

野指针以及越界访问的问题

野指针的定义 

野指针(Wild Pointer)是指向未知或者不可访问内存地址的指针。在C或C++编程语言中,野指针通常是由于指针没有被正确初始化而产生的。这意味着指针不指向任何有效的内存地址,而是可能指向堆、栈或者代码区的任意位置。
定义:
野指针是一种不安全指针,它的值没有指向程序中已知地址的任何有效内存。这种指针的值可能是任意地址,也可能是一个随机的旧值。在野指针上进行解引用操作(即通过指针访问它所指向的内存)会导致未定义行为(Undefined Behavior),这通常会导致程序崩溃或者产生错误的结果。
野指针的常见来源包括:
1. 指针声明后没有立即初始化。
2. 指针释放后没有重新初始化。
3. 指针通过局部变量返回函数外,而该局部变量已经超出作用域。
4. 在动态内存分配失败时,忽略错误并将指针置为`NULL`以外的值。
为了避免野指针,程序员应该遵循以下最佳实践:
- 总是初始化指针,使其指向一个有效的内存地址,或者明确地将其设置为`NULL`。
- 在使用指针之前,检查它是否为`NULL`。
- 避免指针的悬挂(dangling pointer)现象,即指针指向的内存已经被释放。
- 使用现代C++特性,如智能指针,可以自动管理内存,减少野指针的风险。

指针正确代码和错误代码的对比

正确代码 

错误代码

也就是烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫

这里为什么会产生烫这个字和函数栈有关,因为函数栈溢出的时候就会产生烫这个字

烫烫烫的问题

这里为什么会产生烫这个字和函数栈有关,因为函数栈溢出的时候就会产生烫这个字

在C语言编程中,栈溢出(stack overflow)是一个常见的问题,它发生在程序的栈空间被耗尽的时候。栈是用来存储局部变量和函数调用的上下文信息(如返回地址、函数参数等)的区域。当一个函数调用自身或其他函数时,这些信息会被推入栈中。如果函数调用层次太深或者递归调用的深度超过了栈的大小限制,就会导致栈溢出。
栈溢出时,程序会出现未定义行为(undefined behavior),这可能导致程序崩溃、数据损坏或其他不可预测的结果。在中文里,并没有“烫”这个字与栈溢出直接相关的说法。如果您在某个特定的上下文或方言中遇到了“烫”这个词与编程错误或异常相关联,那可能是一个特殊的用法或者是某个特定社群的俚语,而不是一个标准的计算机科学术语。
为了避免栈溢出,程序员应该:
1. 限制递归的深度,确保递归调用的层级不会超过栈的最大容量。
2. 使用尾递归优化(tail recursion optimization),如果编译器或语言支持,这可以避免栈空间的增加。
3. 避免在栈上分配大量内存,特别是在递归函数中。
4. 使用堆(heap)来存储较大的数据结构,而不是栈,因为堆的容量远大于栈。
如果您在某个特定的文档、教程或者社区中看到了“烫”这个词与栈溢出相关的解释,那么它可能是一个特定的术语或者是一个翻译错误。在这种情况下,建议您查找更多的信息或者向该社区的成员澄清这个问题。

野指针造成的问题

野指针容易造成的问题

 野指针(Wild Pointer)是指向未知或者不可访问内存地址的指针。在C或C++编程中,野指针通常是由于指针没有被正确初始化而产生的。使用野指针进行解引用(dereferencing)会导致未定义行为(Undefined Behavior),这可能导致程序崩溃、数据损坏或其他不可预测的结果。
野指针造成的问题主要包括以下几点:
1. **程序崩溃**:野指针可能指向内存中的任何位置,包括操作系统或其他程序的内存区域。解引用野指针可能会读取或修改这些位置的内容,导致程序崩溃。
2. **数据损坏**:野指针可能导致对程序其他部分数据的意外修改,这种修改可能是不可逆的,从而导致程序行为变得不可预测。
3. **安全问题**:野指针可能被用于发起缓冲区溢出攻击(Buffer Overflow Attack)。攻击者可以利用野指针向程序中写入恶意数据,从而覆盖返回地址或其他关键数据,导致程序执行意外的代码。
4. **性能下降**:野指针可能导致程序在运行时产生意外的分支,从而影响程序的性能。
5. **调试困难**:由于野指针导致的问题通常是未定义行为,因此在出现问题时,调试和定位错误可能会非常困难。
为了避免野指针造成的问题,程序员应该遵循以下最佳实践:
- 总是初始化指针,使其指向一个有效的内存地址,或者明确地将其设置为`NULL`。
- 在使用指针之前,检查它是否为`NULL`。
- 避免指针的悬挂(dangling pointer)现象,即指针指向的内存已经被释放。
- 使用现代C++特性,如智能指针,可以自动管理内存,减少野指针的风险。
正确管理指针是确保软件质量和稳定性的重要方面,也是避免野指针问题的关键。

野指针的非法访问问题

形成非法访问

可以看到每次的数值是随机的

野指针的越界访问问题

这里是10个元素循环11次

循环第十一次的时候就已经造成越界访问

所以此时的指针就变成了野指针

指针指向的空间释放

越界和传址问题图解 

 

野指针的寻址的问题

但是遗憾的是a的空间运行结束进行释放 a的空间还给操作系统了 但是下面的空间还是进行寻址 也就是此时找的是空地址 此时这个指针就是野指针 此时野指针再访问空间 就是非法访问

这里不能指向这个空间 ,因为这里创建的空间最后会进行销毁

也就是说如果指向这个空间的haul,最后指向的是一个空的地址

最后导致野指针的发生

简单的举例就是,小敏今天开了一个房间是901,准备进行诈骗。然后让小帅第二天来,小帅第一天吧钱交出去之后,第二天来到这个酒店,说我要进去901,但是酒店说了,这个人退房了,已经没有这个房间了。

这个就是指针的寻址问题,

因为在另外一个函数里面进行运算的事情,只能返回一个数值,而不是整个地址,这个函数需要进行运算的时候,或者需要进行诈骗的时候会开辟一块空间,不需要进行诈骗会吧这个空间关闭。但是如果你还是需要回去那个空间,自然就找不到这个空间了。

越界和传址问题图解 

如何避免野指针

本来就是拴着的狗 你还去里面 肯定咬你

避免指针越界访问

更加安全

assert断言和野指针的使用

包含头文件 assert.h

也就是assert进行断言 如果p不等于空指针 则没事

如果等于空指针 则 断言失败 报错

直接报错了 如果不满足条件

这里是满足条件才继续往下走

断言成功 继续运行

断言只要是表达式就可以 不一定非得说是指针

禁用很简单

assert对程序员很友好

所以在写代码的时候适当assert一下 对你是刮目相看的

release是发布版本 发布出去 还断言干什么

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

传值调用 传址调用

两者区别

首先就是顾名思义

传值调用传过去的是数值

传址调用传过去的是地址

在C语言中,函数对参数的传递有两种方式:传值调用(Pass by Value)和传址调用(Pass by Reference)。
传值调用:
在传值调用中,函数接收的是实际参数值的一个副本。
这意味着在函数内部对参数的任何操作都不会影响原始的变量。
传值调用适用于基本数据类型(如int、float等),因为这些数据类型可以轻松地通过值传递来复制。
传址调用:
在传址调用中,函数接收的是实际参数地址(即变量的内存地址)。
这意味着在函数内部对参数的任何操作都会直接影响原始的变量。
传址调用通常用于指针类型或数组,因为这些数据类型无法简单地通过值传递来复制,而是需要传递它们的地址。
简单来说,传值调用是传递数据的副本,而传址调用是传递数据所在内存的地址。下面是一个示例,展示两种调用方式的差异:

#include <stdio.h>

// 传值调用示例
void modifyValue(int value) {
    value = 50;
}

// 传址调用示例
void modifyReference(int *ptr) {
    *ptr = 50;
}

int main() {
    int a = 25;

    // 使用传值调用
    modifyValue(a);
    printf("After modifyValue: a = %d\n", a); // a 仍为 25

    // 使用传址调用
    modifyReference(&a);
    printf("After modifyReference: a = %d\n", a); // a 变为 50

    return 0;
}

在上述代码中,通过传值调用修改的变量 a 的值没有被改变,因为传递的是 a 的副本。而通过传址调用修改的是变量 a 的值,因为传递的是 a 的地址。

两者的情况分析

不是所有情况都适合

xy都有自己的空间

所以实际是xy进行交换半天 但是ab没有进行交换

虽然形参得到了实参的数值 但是实参有自己的空间 对形参的修改不会影响实参

所以没有达到最终的效果

怎么解决

所以此时就是间接找到了ab的地址

因为此时指针指向的是地址 

此时是直接找到地址

画图解释‘

如果不指向地址的情况下 形参是开辟自己的空间 但是指向的是ab的地址从而对ab的地址进行操作

所以最终还是三个变量之间的交换

相当于就是直接对a b 操作

传址调用

总结

也就是主要是函数内部可以操作外面 就需要传址调用

如果只是接收数值 需要传值调用

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

补充

指针类型和C语言编程语言环境的区别

指针类型是C语言编程中的一个核心概念,而C语言编程语言环境则是指C语言本身及其相关的编译器、运行时库、操作系统等软件和硬件环境的总和。下面分别解释这两者的区别:
### 指针类型
指针类型是指在C语言中用于存储和管理内存地址的数据类型。指针本身是一个变量,其值是另一个变量的内存地址。通过指针,可以间接访问和操作内存中的数据。
指针类型包括以下几个方面:
1. **基本指针类型**:如`int *p;`中的`p`是一个指向整型的指针。
2. **空指针**:在C中,`NULL`是一个定义在`stddef.h`中的空指针常量,表示没有指向任何有效内存地址的指针。
3. **指向指针的指针**:如`int **pp;`中的`pp`是一个指向整型指针的指针。
4. **函数指针**:指向函数的指针,可以用来调用函数,如`void (*fp)(int);`。
5. **数组指针**:指针可以指向数组,如`int (*arr)[10];`中的`arr`是一个指向含有10个整数的数组的指针。
指针类型在C语言中的使用非常广泛,因为它们提供了内存操作的高效途径,并且是实现数据结构(如链表、树、图等)的基础。
### C语言编程语言环境
C语言编程语言环境包括了C语言本身以及使用C语言进行编程时所依赖的所有外部资源。这些资源可以包括:
1. **编译器**:将C源代码转换为机器代码的工具,如GCC、Clang等。
2. **运行时库**:在程序运行时提供额外功能的库,如标准库`libc`,它包含了像`printf`、`malloc`这样的函数。
3. **操作系统**:提供程序执行和管理硬件资源的平台,如Linux、Windows等。
4. **开发工具**:包括编辑器、调试器、性能分析工具等,如Vim、Emacs、GDB等。
5. **硬件平台**:指针类型和C语言的其他特性在不同的硬件平台上可能有所不同,因为硬件架构会影响内存管理方式和指针的实现。
总结来说,指针类型是C语言中的一个语言特性,而C语言编程语言环境则是一个更为广泛的概念,包括了C语言本身以及用于编写和执行C程序的所有工具和平台。

指针类型+1增加四个字节和编程环境+1增加四个字节的区别

在C语言中,"指针类型+1增加四个字节"和"编程环境+1增加四个字节"这两者之间的区别在于它们所指的具体内容和上下文。
### 指针类型+1增加四个字节
1. **指针自增操作**:当你对一个指针进行`++`操作时,例如`int *p; p++;`,如果`int`类型占用四个字节,那么指针`p`的内容将增加四个字节,指向下一个`int`类型的位置。这是指针操作的一种,是指针本身值的改变。
2. **指针的移动**:这里的"增加四个字节"是指指针的数值增加了`sizeof(int)`的大小。`sizeof(int)`在大多数现代平台上的确是四个字节,但这并不是固定的,它取决于编译器和操作系统。
### 编程环境+1增加四个字节
1. **数据大小的变化**:在编程环境的上下文中,"+1增加四个字节"可能指的是在某个上下文中,数据类型的大小增加了。例如,如果编程环境从32位升级到64位,那么通常整数类型的大小会从四个字节增加到八个字节。
2. **性能优化或需求**:在某些情况下,编程环境的更新可能不会改变数据类型的大小,但可能会引入新的数据类型或者优化,以满足更高的性能需求或者更好的兼容性。
3. **内存管理的变化**:在更广泛的上下文中,"增加四个字节"也可能指的是内存管理策略的变化,比如操作系统或编译器可能会在指针周围增加额外的元数据,以支持更高级的内存分配或优化技术。
总的来说,"指针类型+1增加四个字节"是指针操作的直接结果,而"编程环境+1增加四个字节"是指整个编程语言和运行时环境的改变,这些改变可能影响数据类型的大小、内存布局、性能特性等多个方面。这两者是不同层次的概念,一个是语言层面的操作,另一个是整个环境层面的变化。

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

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

相关文章

帝国cms7.5仿非小号区块链门户资讯网站源码 带手机版

帝国cms7.5仿非小号区块链门户资讯网站源码 带手机版 带自动采集 开发环境&#xff1a;帝国cms 7.5 安装环境&#xff1a;phpmysql 包含火车头采集规则和模块&#xff0c;采集目标站非小号官网。 专业的数字货币大数据平台模板&#xff0c;采用帝国cms7.5内核仿制&#xff0…

爱普生的SG2016系列高频,低相位抖动spxo样品

精工爱普生公司(TSE: 6724&#xff0c;“爱普生”)已经开始发货样品的新系列简单封装晶体振荡器(SPXO)与差分输出1。该系列包括SG2016EGN、SG2016EHN、SG2016VGN和SG2016VHN。它们在基本模式下都具有低相位抖动&#xff0c;并且采用尺寸为2.0 x 1.6 mm的小封装&#xff0c;高度…

【go从入门到精通】什么是go?为什么要选择go?

go的出生&#xff1a; go语言&#xff08;或Golang&#xff09;是Google开发的开源编程语言&#xff0c;诞生于2006年1月2日下午15点4分5秒&#xff0c;于2009年11月开源&#xff0c;2012年发布go稳定版。Go语言在多核并发上拥有原生的设计优势&#xff0c;Go语言从底层原生支持…

深度学习 精选笔记(10)简单案例:房价预测

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

Maven【4】(继承)(命令行操作)

文章目录 【1】基础概念【2】继承的作用【3】创建父工程和子工程【4】在父工程中统一管理依赖 【1】基础概念 说到继承&#xff0c;我们很容易想到Java中的继承&#xff0c;有子类和父类&#xff0c;子类继承父类&#xff0c;那么我们maven中的继承是什么呢&#xff1f; Maven…

二分查找常用解题模板(带一道leetcode题目)

1.为了较为清晰的写出各种情况&#xff0c;接下来的代码中不会出现else&#xff0c;而是将每一个else if均给写出来!!! 2.为了防止每次的mid溢出&#xff0c;我们均写为mid left (right - left) 基本的二分查找模板&#xff08;寻找一个数&#xff09; 基本问题描述&#xff…

计算机网络物理层知识点总结

本篇博客是基于谢希仁编写的《计算机网络》和王道考研视频总结出来的知识点&#xff0c;本篇总结的主要知识点是第二章的物理层。上一章的传送门&#xff1a;计算机网络体系结构-CSDN博客 通信基础 物理层概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&am…

服务器上部署WEb服务方法

部署Web服务在服务器上是一个比较复杂的过程。这不仅仅涉及到配置环境、选择软件和设置端口&#xff0c;更有众多其它因素需要考虑。以下是在服务器上部署WEb服务的步骤&#xff1a; 1. 选择服务器&#xff1a;根据项目规模和预期访问量&#xff0c;选择合适的服务器类型和配置…

MySQL:函数

提醒&#xff1a; 设定下面的语句是在数据库名为 db_book里执行的。 创建user_info表 注意&#xff1a;pwd为密码字段&#xff0c;这里使用了VARCHAR(128)类型&#xff0c;为了后面方便对比&#xff0c;开发项目里一般使用char(32)&#xff0c;SQL语句里只有MD5加密函数 USE db…

iOS卡顿原因与优化

iOS卡顿原因与优化 1. 卡顿简介 卡顿&#xff1a; 指用户在使用过程中出现了一段时间的阻塞&#xff0c;使得用户在这一段时间内无法进行操作&#xff0c;屏幕上的内容也没有任何的变化。 卡顿作为App的重要性能指标&#xff0c;不仅影响着用户体验&#xff0c;更关系到用户留…

XUbuntu22.04之解决:仓库xxx没有数字签名问题(二百一十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

buuctf_misc_荷兰宽带数据泄露+被偷走的文件

荷兰宽带数据泄露 题目&#xff1a; 没啥&#xff0c;工具给大家放这了&#xff0c;这个&#xff08;相对来说&#xff09;比较安全 https://routerpassview.en.lo4d.com/windows 打开后&#xff0c;.bin文件直接托进去 只是我想不到的是&#xff0c;flag这算是username&…

H.266参考软件VTM各版本的性能差异

VTM&#xff08;VVC Test Model&#xff09;&#xff0c;是H.266视频编码标准的参考软件&#xff0c;即是VVC spec.的一种参考实现&#xff0c;代码里包括了H.266的软件编码器和软件解码器实现&#xff0c;代码地址如下&#xff1a; https://vcgit.hhi.fraunhofer.de/jvet/VVCS…

基于单片机的节能窗控制系统设计

摘 要:本文以单片机为基础,对节能窗控制系统进行了科学设计,在满足日常生活需求的同时更好地实现节能减排目标。此设计中的节能窗控制系统,实际操作要灵活,具备可靠且稳定的性能,同时具备节能功效。 关键词:单片机;节能窗控制系统;系统设计 在节能窗等概念推广的背景…

前端学习第二天-html提升

达标要求 了解列表的分类 熟练掌握列表的用法 熟练掌握表格的结构构成 合并单元格 表单的组成 熟练掌握表单控件分类的使用 1.列表 1.1 无序列表 <ul>&#xff1a;定义无序列表&#xff0c;并且只能包含<li>子元素。 <li>&#xff1a;定义列表项&a…

hippy 调试demo运行联调-mac环境准备篇

适用对于终端编译环境不熟悉的人看&#xff0c;仅mac端 hippy 调试文档官网地址 前提&#xff1a;请使用node16 联调预览效果图&#xff1a; 编译iOS Demo环境准备 未跑通&#xff0c;待补充 编译Android Demo环境准备 1、正常安装Android Studio 2、下载Android NDK&a…

10-Java装饰器模式 ( Decorator Pattern )

Java装饰器模式 摘要实现范例 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构 装饰器模式创建了一个装饰类&#xff0c;用来包装原有的类&#xff0c;并在保持类方法签名完整性的前提下&#xff0c;提供…

JProfiler 14 for Mac 14.0激活版:Java性能分析的终极工具

JProfiler是一款专业的Java应用程序性能分析工具&#xff0c;可帮助开发人员识别和解决Java应用程序中的性能问题。JProfiler支持Java SE、Java EE和Android平台&#xff0c;提供了多种分析选项&#xff0c;包括CPU分析、内存分析和线程分析等。 软件下载&#xff1a;JProfiler…

本地快速部署谷歌开放模型Gemma教程(基于WasmEdge)

本地快速部署谷歌开放模型Gemma教程&#xff08;基于WasmEdge&#xff09; 一、介绍 Gemma二、部署 Gemma2.1 部署工具2.1 部署步骤 三、构建超轻量级 AI 代理四、总结 一、介绍 Gemma Gemma是一系列轻量级、最先进的开放式模型&#xff0c;采用与创建Gemini模型相同的研究和技…

利用IP地址识别风险用户:保护网络安全的重要手段

随着互联网的发展和普及&#xff0c;网络安全问题日益突出&#xff0c;各种网络诈骗、恶意攻击等风险不断涌现&#xff0c;给个人和企业的财产安全和信息安全带来了严重威胁。在这样的背景下&#xff0c;利用IP地址识别风险用户成为了保护网络安全的重要手段之一。IP数据云探讨…