目录
6.2.3 指针变量不再使⽤时,及时置NULL,指针使用之前检查有效性
点赞后看养成习惯
一.地址与内存
每个内存单元,相当于⼀个学⽣宿舍,⼀个人字节空间⾥⾯能放8个⽐特位,就好⽐同学们住的⼋⼈间,每个⼈是⼀个⽐特位。
每个内存单元也都有⼀个编号(这个编号就相当于宿舍房间的⻔牌号),有了这个内存单元的号,CPU就可以快速找到⼀个内存空间。
⽣活中我们把⻔牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起
了新的名字叫:指针。
总结: 内存单元的编号==地址==指针(重点)
如图:每个内存(字节)都有个 地址(指针)
二. 指针变量和地址
2.1 取地址操作符(&)
在C语言中创建变量其实是向内存申请空间来存放数据
int a = 10;表面上看,是创建了一个整型变量a,并且对a赋值10;
实际上,是向内存申请了4Byte的空间,命名为a,用来存放10。
比如:0a就是10(16进制)
2.2 指针变量
2.2.1 指针变量
那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如:0x006FFD70,这个数值有时候也是
存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在指针变量中。
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//取出a的地址并存储到指针变量pa中
return 0
}
2.2.2 如何理解指针
我们看到pa的类型是 int* ,我们该如何理解指针呢?
一副图就行
2.3 指针变量的大小
1.指针变量的⼤⼩取决于地址的⼤⼩
2.32位平台下地址是32个bit位(即4个字节)
3.64位平台下地址是64个bit位(即8个字节)
#include <stdio.h>
int main()
{
printf("%zd\n", sizeof(char *));
printf("%zd\n", sizeof(short *));
printf("%zd\n", sizeof(int *));
printf("%zd\n", sizeof(double *));
return 0;
}
一 . 32位平台下地址是32个bit位(即4个字节)
二.64位平台下地址是64个bit位(即8个字节)
注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的
指针大小 和 指针(⼀次能操作几个字节)不一样!
三. 指针变量类型的意义(如何理解指针类型)
3.1指针的意义
指针变量的大小和类型无关,只要是指针变量,在同⼀个平台下,大小都是⼀样的,为什么还要有各种各样的指针类型呢?
其实指针类型是有特殊意义的! ! ! ! ! ! !
结论(重要):指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作几个字节)
比如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。
3.2 指针+-整数
![](https://i-blog.csdnimg.cn/blog_migrate/932a6749ca4be94fbce8a75af3bcc204.png)
char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
3.3 void* 指针
在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针(或者叫泛型指针),这种类型的指针可以⽤来接受任意类型地址。
但是也有局限性, void* 类型的指针不能直接进行指针的+ - 整数和解引⽤的运算。
四. const修饰指针
void test2()
{
//const在*的左边
int n = 10;
int m = 20;
const int* p = &n;//或者int const*p = &n
*p = 20;//指针指向的内容不可以被修改
p = &m; //指针变量本⾝可以修改
}
void test3()
{
//const在*的右边
int n = 10;
int m = 20;
int *const p = &n;
*p = 20; //指针指向的内容可以被修改
p = &m;//指针本身不可以修改
}
结论:const修饰指针变量的时候
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
• const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改
五. 指针运算
指针的基本运算有三种,分别是:
5.1 指针+-整数
因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸瓜就能找到后面的所有元素。
#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]);
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i)); //p+i 这⾥就是指针+整数
}
return 0;
}
5.2指针-指针
总结:指针-指针是元素之间的个数(记住)
比如:模拟实现strlen函数,就可以使用
#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;
}
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++;
}
return 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 i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3. 指针指向的空间释放
n和 函数 已经被释放,返回的n的地址,已经不是 n 了,继续访问就回越界。
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
6.2 如何规避野指针
6.2.1 指针初始化
可以给指针赋值NULL. NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错
include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
6.2.2 ⼩⼼指针越界
不能超出范围访问,超出了就是越界访问。
6.2.3 指针变量不再使⽤时,及时置NULL,指针使用之前检查有效性
6.2.4 避免返回局部变量的地址
如
七. assert断言
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报
错终⽌运⾏。这个宏常常被称为“断⾔”。
assert() 的缺点是,因为引⼊了额外的检查,增加了程序的运⾏时间。
但是,在 VS 这样的集成开发环境中,在 Release 版本中,assert()直接就是优化掉了。这样在debug版本写有利于程序员排查问题,在 Release 版本不影响用户使⽤时程序的效率。
八. 指针的使⽤和传址调⽤
我们看这样一段代码:
我们发现其实没产⽣交换的效果,这是为什么呢?
结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实
参。所以Swap是失败的了。
那怎么样才能修改呢?其实用传址调用就可以解决。
记得点赞噢