内存是计算机中用于存储数据和程序的设备。它是一个由许多存储单元组成的集合,每个存储单元都可以存储一个固定大小的数据。这些存储单元可以被计算机读取和写入,以便在程序运行时存储和检索数据。
内存就好比一家旅馆,里面的房间就好比存储空间,往内存里面存放数据就好比旅客入住,取出数据就好比旅客退房,当所有房间都住满的时候,内存也就存储满了。
在计算机操作中所做的每一步操作都是会产生大小不一的数据的,这些数据都会存储到内存中。
当有客人来住旅馆的时候,如何准确的告诉该旅客应该去哪个房间?或者要寻找以为旅客时,如何知道他在哪一间房间中?答案是找房间号,只要找到了房间号,也就能精准找到需要找到的旅客。
计算机定位数据也是如此。
在计算机中,每个内存单元都有一个唯一的地址,用于标识该单元,也相当于每个房间有一个独无二的房间号。地址是一个数字,用于访问内存中的数据。计算机使用这些地址来读取和写入内存中的数据。也就是通过房间号来找到里面的人。
地址通常是一个整数,它表示内存中存储单元的位置。例如,在一个32位计算机上,每个地址可以表示2^ 32个不同的内存单元。相当于房间号由4位数字组成,如 0000 代表第一间房间,0001代表第二间房间,那么一共就可以代表 10000 (10 ^ 4)间房间,也就是代表有这么多空间可以用来存储数据。(计算机使用的是二进制表示法,因而 32 位只能表示 2 ^ 32个地址,生活中用的是十进制,所以4 位数字可以表示 10000个房间,即10 ^4个房间)。
当程序需要读取或写入内存中的数据时,它会使用地址来指示要读取或写入的内存单元。程序员可以使用编程语言中的指针来访问内存地址,以便直接读取或写入内存中的数据。指针是一个变量,它存储一个地址,可以用于访问内存中的数据。
在C语⾔中,创建变量其实就是向内存申请空间,⽐如下面这段代码:
#include
int main()
{
int a = 10;
return 0;
}
创建变量会消耗内存,它消耗的方式在于向内存中申请4个字节,⽤于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:
1. 0x006FFD702
2. 0x006FFD713
3. 0x006FFD724
4. 0x006FFD73
那我们如何能得到a的地址呢?
这⾥就得学习⼀个操作符(&) - - 取地址操作符,操作如下:
#include
int main()
{ int a = 10;
&a;//取出a的地址
printf("%p\n", &a);
return 0;
}
按照我画图的例⼦,会打印处理:006FFD70,&a取出的是a所占4个字节中地址较⼩的字节的地址。
虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可⾏的。
那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如0x006FFD70,这个数值有时候也是需要存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中。
比如:
#include
int main()
{
int a = 10;
int* pa = &a;//取出a的地址并存储到指针变量pa中
return 0;
}
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
我们看到pa的类型是 int* ,我们该如何理解指针的类型呢?
int a = 10;
int * pa = &a;
这⾥pa左边写的是 int* , * 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int)类型的对象。
那如果有⼀个char类型的变量ch,ch的地址,要放在什么类型的指针变量中呢?答案如下:
char ch = 'w';
char* pc = &ch;
指针解引用:
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。
C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
#include
{
int main()
{
int a = 100;
int* pa = &a;
*pa = 0;//此时a 变成了0;
return 0;
}
上⾯代码中第7⾏就使⽤了解引⽤操作符, *pa 的意思就是通过pa中存放的地址,找到指向的空间,pa其实就是a变量了;所以pa = 0,这个操作符是把 a 改成了0.
这⾥如果⽬的就是把a改成0的话,写成 a = 0; 不就完了,为啥⾮要使⽤指针呢?
其实这⾥是把a的修改交给了pa来操作,这样对a的修改,就多了⼀种的途径,写代码就会更加灵活.
• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
指针变量的大小可以用 sizeof 关键字来计算。如:
#include
int main()
{
int a = 1;
int* p = &a;
printf("%zd",sizeof(p));
return 0;
}
代码会在屏幕上打印出指针变量的大小。
在解引用时,指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。可以在编译器中进行调试查看。
先看⼀段代码,调试观察地址的变化。
#include
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;
}
代码运⾏的结果如下:
我们可以看出, char类型的指针变量+1跳过1个字节,int类型的指针变量+1跳过了4个字节。
**结论:**指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
const 修饰的是其后面的内容,保证其后面的内容不能够被修改。
例如 const int n = 0
,此时的 n 是不可修改的。
当const 修饰指针变量的时候,分为两种情况:
例如:
int a=10; int b=1; const int* p = &a
或者int a=10; int b=1; int const* p=&a
此时p=&b
是被允许的,但是*p = 2
,这种通过对指针来改变指针指向的内容是不被允许的。
例如:
int a=10; int b=1; int* const p = &a
或者int a=10; int b=1; int* const p=&a
此时p=&b
是不被允许的,但是*p = 2
,这种通过对指针来改变指针指向的内容是被允许的。
指针的基本运算有三种,分别是:
•指针 + - 整数
•指针 - 指针
•指针的关系运算
指针 + - 整数的结果是指针往后或往前跳过指针类型个数据。
例如:char* p; p + 1 表示往后跳过一个字节的地址,p - 1 表示往前跳一个字节地址
int* p;p + 1 表示往后跳过四个字节的地址,p - 1 表示往前跳过四个字节的地址
short* p; p + 1 表示往后跳过2个字节的地址, p + 1 表示往前跳过2个字节的地址
指针减指针的结果为两个指针间相差的地址位置个数的绝对值。
例如:0x0011fdf2 - 0x0011fdf1 = 1;
0x0011fdf1 - 0x0011fdf2 = -1;
#include
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;
}
上面代码的结果为 3.
1. 指针未初始化
#include
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2. 指针越界访问
#include
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;
}
3. 指针指向的空间释放
#include
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
1. 指针初始化
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL.
NULL是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。
2. 当心指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。
3. 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,同时使⽤指针之前可以判断指针是否为NULL。
4. 避免返回局部变量的地址
指针是C语言当中最重要的部分之一,熟练掌握指针之后,能加深对于C语言的理解。