1.内存和地址
1.1 内存:
在生活中,每个人都有自己的居住地,为我们遮风挡雨。同样数据也会有自己的”居住地“,即内存,计算机CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数 据也会放回内存中。同时内存也会划分成一个个内存单元,并且每个内存单元大小为一个字节,一个字节里面有八个比特位。
1.2 地址:
顾名思义,地址在生活中即表示位置,就比如房间号,如果需要找一个人,有了房间号就能立刻定位一个人具体住在哪里,而不需要挨个房间找,大大提高了查找效率。同样,给内存编上序号,也能大大提高查找某一内存,进而读取使用,这个序号称之为“地址”,又名“指针”。
#include <stdio.h>
int main()
{
int a = 6;
int* p = &a;
printf("%p\n", p);
return 0;
}
代码中创建了变量a,在C语言中创建变量其实就是向内存申请一片空间,由于int类型占4个字节,所以a向内存申请了4个字节来存放整数10,并且这4个字节的地址是连续的(打印显示第一个地址)
#include <stdio.h>
int main()
{
int a = 1;
printf("%p\n", &a);
return 0;
}
多次执行就会发现,每次执行打印的地址都会不一样,这是因为每次执行都会为a重新开辟空间,所以会有所不同。
2.2指针变量与解引用操作符(*)
2.2.1指针变量
从上面可以看到,把a的地址值赋值给p,并存在变量p中这一操作,p就称之为指针变量。
int a = 1;
int* p = &a;
指针变量也是一种变量,这种变量是用来存放地址的,存放在指针变量中的值都会理解为地址,而指针变量也会被叫做指针。这就有人会有疑惑,不是说地址是指针吗?现在怎么又说指针变量也是地址?欸其实地址是指针这是没有错的,指针变量本质还是变量,被叫成指针是人们对其的简称,所以有时候要辨别一下。
接下来就对上面的类型进行拆解,以便更好理解。
还是一上面那段代码为例,p的左边是int*,*在说明p是一个指针变量,int在说明这个指针指向的对象是int类型(即a)(这两句话要牢牢记住),int*即为p的指针类型。
同理:
char c = 's';
char* p2 = &c;
2.2.3解引用操作符
既然我们把地址存在指针变量中,也不能只存着啊,总不能占着茅坑不拉*吧,总要发挥指针变量的用处,所以就有了解引用操作符(*),我们就可以拿着房间号(地址)去找某人了(指向对象)。先上代码:
#include <stdio.h>
int main()
{
int a = 1;
int* p = &a;
printf("%d\n", a);
printf("%d\n", *p);
printf("%d\n", *(&a));
return 0;
}
结果为
那么a == *p == *(&a)。
2.3指针变量的大小
64位机器(x64环境),假设有64根地址总线,一个地址就是64个二进制组成的二进制序列,存储就需要8个字节的空间,那么指针变量的大小就是8个字节。
同理,32位机器(x86环境),指针变量的大小就是4个字节。
所以,一个指针变量的大小是4或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;
}
3.指针变量类型的意义
3.1指针的解引用
既然指针变量的大小与指向对象的类型无关,指针变量在同一环境大小都一样,那有没有必要分那么多的指针类型呢?--其实是有必要,设计者这么设计一定是有他的深意吧......看代码:
3.2指针 +-整数、
先上代码:
#include <stdio.h>
int main()
{
int a = 66;
int* pi = &a;
char* pc = (char*)&a;
printf("%x\n", &a);
printf("%x\n", pi);
printf("%x\n", pi + 1);
printf("%x\n", pc);
printf("%x\n", pc + 1);
return 0;
}
#include <stdio.h>
//代码1
void test1()
{
int n = 10;
int m = 20;
int* p = &n;
*p = 20;//ok?
p = &m; //ok?
}
void test2()
{
//代码2
int n = 10;
int m = 20;
const int* p = &n;
*p = 20;//ok?
p = &m; //ok?
}
void test3()
{
int n = 10;
int m = 20;
int* const p = &n;
*p = 20; //ok?
p = &m; //ok?
}
void test4()
{
int n = 10;
int m = 20;
int const* const p = &n;
*p = 20; //ok?
p = &m; //ok?
}
int main()
{
//测试⽆const修饰的情况
test1();
//测试const放在*的左边情况
test2();
//测试const放在*的右边情况
test3();
//测试*的左右两边都有const
test4();
return 0;
}
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = arr;
size_t num = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < num; i++)
{
printf("%d ", *(pa + i));
}
return 0;
}
5.2指针-指针
指针-指针得到的是这两指针之间的元素个数。
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa1 = arr + 1;//指向的是2
int* pa2 = arr + 5;//指向的是6
printf("%d\n", pa1 - pa2);
printf("%d\n", pa2 - pa1);
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 sz = sizeof(arr)/sizeof(arr[0]);
while(p<arr+sz) //指针的⼤⼩⽐较
{
printf("%d ", *p);
p++;
}
return 0;
}
6.野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
6.1野指针成因
1.指针未初始化
2.指针越界访问
3.指针指向的空间已经释放
6.2如何规避野指针
6.2.1初始化指针
如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL。NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错。
6.2.2小心指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。
6.2.3当指针变量不再使用时,及时置为NULL,在指针使用前检查有效性。
assert(p != NULL)
6.2.4避免返回局部变量的地址
因为出了某个区域局部变量空间就会释放,所以不应返回该地址。
指针部分暂时讲这么多,如果内容存在错误或者需要补充的,欢迎评论区留言,感谢观看。