指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
指针所指向的内存区:
在函数调用时就会创建一个栈桢,在栈桢中内存被划分为一个一个的单元,
其中每个内存单元能存放8个比特位(一个字节),每个单元也有一个编号。
*:解引用操作符
&:取地址操作符
内存单元的编号 = 地址 = 指针
int main()
{
int a = 10;//创建一个变量a,并赋值为10
//在内存上申请4个字节的空间,存放10
printf("%p\n", &a);//000000B633B8FBE4 以16进制的形式输出
//只输出起始(低)位置,后面的地址依次加1;
//&取出地址操作符
int* pa = &a;//pa指针变量 - 存放地址 - 地址又被存放指针
//int* pa,变量的类型,变量的名字 (变量 即 存放的地址)
*pa;//*解引用操作符,*pa等价于a
//& --- *
//取地址 解引用
return 0;
}
指针:地址 指针变量:变量-存放地址
指针变量用来存放地址的,指针变量并不完全等同指针, 但口头上 指针 一般是 指针变量
1.指针变量是专门用来存放地址的,指针变量的大小取决于一个地址存放需要多大空间 32位机器上:地址线32根,地址的二进制序列就是32bit位 - 要把这个地址存起来, 需要4个字节的空间,也就是32bit位的空间 所以:32位机器上指针变量的大小是4个字节! 2.32位平台下地址是32个bit,指针变量的大小是4个字节 64位平台下地址是64个bit,指针变量的大小是8个字节


计算机中硬件单元要互相协同工作(协同:互相进行数据传递), 但是硬件和硬件之间相互独立,故用"线"连接起来(物理上的), 而CPU和内存之间也有大量的数据交互,所以两者也用线连接起来。 CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置, 而因为内存中的字节很多,所以需要给内存编号。 计算机中的编址,并不是把每个字节的地址记录下来, 而是通过硬件设计完成的。 可以简单理解为:32位的机器就有32根地址总线,每根线只有两态, 表示0,1[电脉冲有无],那么一根线就能表示2中含义,2根线就能有4种含义。 依此类推,32根地址线,就能表示2^32种含义,每一种含义都代表一个地址。 地址信息被下达给内存,在内存上,就可以找到该地址对应的数据, 将数据在通过数据总线传入CPU内寄存器。
1.指针解引用的时候有多大权限 (如果一个指针代替所有的话,解引用时的字节与变量定义类型不同) 2.指针类型决定了指针向前或向后走一步有多大(距离)
/*指针解引用的权限*/
int main()
{
int a = 0x11223344;
char* pa = &a;//pa可以存放下a的地址
*pa = 0;//解引用时这样只能改第一个字节,但是int型有四个字节
printf("%p", *pa);
return 0;
}/*指针解引用的时候有多大权限*/
int main()
{
int a = 10;
int* pa = &a;
char* pc = (char*)&a;//int*
printf("pa = %d", pa); //9697748
printf("pc = %d", pc); //9697748
printf("pa + 1 = %d", pa + 1); //9697752
printf("pc + 1 = %d", pc + 1); //9697749
}这种类型的指针可以用来接受任意类型的地址,但也有局限性, void*类型的指针,局限性在于他不能直接进行指针的+-整数和解引用的运算
有什么用: 一般void*类型的指针是使用在函数参数的部分,用来接收不同数据类型的地址, 这样可以实现泛型编程的效果,使得一个函数来处理多种类型的数据
注意: void*类型的指针不能直接进行解引用的操作 void* 类型的指针也不能进行指针计算的操作
int main()
{
int* a = 10;
char* ch = 'w';
void* pv = &a;//int*
void* pv2 = &a;//char*
//*pv = 20;//err void*类型的指针不能直接进行解引用的操作
//pv++;//err void* 类型的指针也不能加减一的操作
return 0;
}指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
使用完指针后未将其置为NULL,导致该指针指向的内存地址仍然被占用,但该指针却不受控制。野指针是C语言中一个常见的内存泄漏问题。如果程序中存在野指针,当程序再次申请内存时,可能会将之前已经释放的内存分配给新的变量,导致程序出现不可预测的行为,甚至崩溃。
野指针成因: 1. 指针未初始化 2.指针越界访问造成野指针 3.指针指向的空间释放
1. 指针未初始化
int main()
{
int* ptr;//野指针,没有初始化
*ptr = 20;//非法访问内存了
printf("%p\n", ptr);
return 0;
}2.指针越界访问造成野指针
int main()
{
int arr[5] = { 0 };
//1 1 1 1 1 1 1 1 1 1
int i = 0;
int* p = arr;
for (i = 0; i < 10; i++)
{
*p = 1;
p++;//数组越界访问
}
return 0;
}3.指针指向的空间释放
int* test()
{
int a = 10;
//...
return &a;
}
int main()
{
int* p = test();
printf("hehe\n");//为什么这里加了一个代码,这里的值就变了
printf("%d\n", *p);//
return 0;
}1.指针初始化 如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里, 可以给指针赋值NULL。 NULL 是C语言中定义的一个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错。 2.小心指针越界 一个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。 3.指针变量不再使用时,及时置NULL,指针使用之前检查有效性? 当指针变量指向一块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的一个规则就是:只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL。 4.避免返回局部变量的地址? 如造成野指针的第3个例子,不要返回局部变量的地址。
空指针是一个特殊的数据类型,它的值定义为NULL。空指针不同于NULL的整数表示,它是一个指针变量的特殊值,表示该指针变量不指向任何有效的内存地址。 使用空指针进行解引用操作会导致程序崩溃,因为没有任何有效的内存地址可供访问。在C语言中,空指针主要用于表示指针变量没有指向任何有效的内存地址,例如未初始化的指针变量或已释放的内存块。
int *ptr = NULL;
if (ptr != NULL) {
// 执行解引用操作
printf("%d", *ptr);
} else {
printf("空指针");
}