目录
地址
地址也就是我们常说的指针,在讲解指针前先讲个生活中的小例子,方便理解地址的概念。
学生小明是个住校生,他的朋友来他的学校找他出去玩,通过同学得知小明正在宿舍休息,要是没有宿舍的楼号以及宿舍的房间号,那想找到他就需要在宿舍里一间一间找,会花费大量时间,但是有了楼号跟宿舍编号,那就可以节省很多很多时间。
在编程里这个楼号跟宿舍编号就是我们说的指针了,在电脑里,内存被分为了一个一个小单元,一个单元的大小为一个字节。
内存大小单位如下
bit - ⽐特位
Byte - 字节
KB
MB
GB
TB
PB
1Byte = 8bit
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
这每一个字节就相当于宿舍编号,我们在编程时把数据放在单元块里,我们通过单元的地址就可以快速准确的找到我们需要的变量了。
有关地址的操作符
简单了解了地址之后,先介绍一些与指针有关的操作符,这些是用指针进行编程时常用的操作符。
取地址操作符 &
在编程时创建的一些变量就是向内存申请了一些空间来存储这些变量。那我们怎么能知道变量的地址呢,就需要“&”这个操作符。
使用案例:
#include<stdio.h>
int main()
{
int a = 20;
int* n = &a;//int* 代表n是一个整形类型的指针变量
return 0;
}
如图,经过调试可以看出我们将a的地址存放到了n里面。
注:
取地址操作符取出来的是变量的首地址也就是第一个字节的地址,但整形有4个字节,我们用首地址来找寻变量时,电脑会顺着这个首地址找寻n个字节,这个n与指针的类型有关,整形变量的指针就找四个字节,字符类型的就是首地址一个字节。
解引用操作符 *
前面说了取地址操作符,将地址存储到一个变量,那我们直接使用这个变量才操作的话就是使用里面的地址来进行操作。
比如:a+1
#include<stdio.h>
int main()
{
int a = 20;
int* n = &a;
printf("%d", n+1);
return 0;
}
如图结果是一段乱码,原因是直接使用n变量本质上是让n存放的地址加1,里面存放的其实还是一个地址,然后用“%d”打印,就会使变量类型不符,出现乱码,那么想要正确打印可以用“%p”打印,不过打印出来的也是地址。
修正后:
#include<stdio.h>
int main()
{
int a = 20;
int* n = &a;
printf("%d", *n+1);//这里在n前面加了个*,对n解引用
return 0;
}
运行结果正确
也就是说我们将一个变量a的地址传给另一个变量n时,我们想通过n找到a就需要在前面加一个“*”,它的作用是通过n里面的地址来找到地址里存放的数据。
指针变量
整形指针变量:int* p
字符指针变量:char* p
"*"代表变量p是一个指针变量。
int、char表示指针指向的数据类型是整形或者字符。
指针变量大小:
在32位的环境下指针变量大小是4个字节。
在64位的环境下指针变量大小是8个字节。
指针的传参调用
当指针变量作为参数进行传参调用时,定义的函数()内部也应该是类型相同指针变量。
void init(int* x)//定义了一个整形指针来接收整形变量b
{
printf("%d",*x);
}
int main()
{
int n=0;
int* b =&n;
init(b);
return 0;
}
指针±整数
#include <stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;//将地址强制转换成char* 类型
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;
}
运行结果如下:(地址是以16进制显示的)
第一、二、四行都是打印的n的地址,主要观察第三行与第五行。
第三行是pc+1,可以看出它有“012FFB7C”变成了“012FFB7D”,也就是加了个1,但是第五行加了个四,但程序上都是+1,这是为什么?
主要是因为pc是char* 类型的指针变量,让它+1是根据指向变量的数据类型大小来计算的,char类型的变量大小是1,它的地址在加1时也就只是加了一个字节,而int类型的则是加4个字节。
结论: 指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
void*指针
在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针(或者叫泛型指针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进⾏指针的±整数和解引⽤的运算。
当我们用一个int* 的指针变量存放char* 类型的指针时,系统会报错,因为类型不兼容,但void* 可以不会有这个问题,不管是字符还是整形甚至是数组函数类型的都可以用它来存放。
指针-指针
有一个函数叫做strlen,它的作用是求字符串长度。其实本质上也可以理解为用末地址(\0的地址)减首地址求中间有几个字节,也就是指针-指针。
#include <stdio.h>
#include<string.h>
int my_strlen(char* s)
{
char* p = s;//首地址暂存
while (*p != '\0')//当遇到\0时跳出循环,这个时候p里面存的就是\0的地址
p++;
return p - s;
}
int main()
{
char arr[] = { "abc" };
printf("%d\n", strlen(arr));
printf("%d\n", my_strlen(arr));
return 0;
}
如图用指针-指针定义的函数与库函数strlen效果一样。
野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
野指针成因
指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针 *(p++) = i;
}
return 0;
}
指针指向的空间被释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
前两个编译时都会报错,但这段代码编译时不会报错,但生成解放方案时会显示一些问题。
正常情况时这样的:
而它的时这样:可能后面在进行一些操作后会出现报错。
防止出现野指针
1.对指针进行初始化,也就是给指针赋值一个空指针NULL。
2.小心指针在存放数组地址时越界。
3.不要放置局部变量的地址。
4.指针变量不再使用时及时赋值NULL。