目录
指针是什么?
指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储中另一个地方的值,由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元,因此,将地址形象化地称为“指针”,意思是通过它能找到以它为地址的内存单元
指针是个变量,存放内存单元的地址(编号),即是存放地址的变量
一个内存单元为一个字节
#include <stdio.h>
int main()
{
int a = 10;
int* p = &a;//指针变量(存放地址
return 0;
}
编址:
对于32位机器,假设有32根地址线,没根地址线在寻址时产生一个电信号【正点或者负电(1/0)】,产生的地址就可能是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
…………………………………………………
11111111 11111111 11111111 11111111
每个地址标识一个字节,就可以有2^32Byte==4GB的空闲空间进行编址
即在32位平台上要用4个字节存放地址,而64位平台要8个字节来存放地址
总结:
指针用来存放地址,地址是唯一标示一块地址空间的
指针的大小在32位平台是4个字节,在64位平台是8个字节
指针和指针类型
#include <stdio.h>
int main()
{
int a = 0x11223344; //存在地址中时是44 33 22 11
int* pa = &a;
*pa = 0; //此时将a变为00 00 00 00
return 0;
}
#include <stdio.h>
int main()
{
int a = 0x11223344; //存在地址中时是44 33 22 11
char* pb = &a;
*pb = 0; //只将44改为了00,即00 33 22 11
return 0;
}
指针类型发生变化,解引用操作时结果会发生变化
指针类型决定指针进行解引用操作的时候有多大的权限(能操作几个字节)(不同类型的指针可存储相同的数据)
int* p; //能访问4个字节
char* p; //能访问1个字节
double* p; //能访问8个字节
指针+-整数
#include <stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pb = &a;
printf("%p\n", pa); //00000025E17FFC64
printf("%p\n", pa+1); //00000025E17FFC68 相差4
printf("%p\n", pb); //00000025E17FFC64
printf("%p\n", pb+1); //00000025E17FFC65 相差1
return 0;
}
指针类型决定指针走一步走多远(决定指针的步长
总结:
指针类型决定指针进行解引用操作的时候有多大的权限
指针类型决定指针走一步走多远
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;//arr是数组名,首元素地址
int i = 0;
for ( i = 0; i < 10; i++)
{
*(p + i) = 1; //可将arr数组中每个元素改为1
}
return 0;
}
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
char* p = arr;//arr是数组名,首元素地址
int i = 0;
for ( i = 0; i < 10; i++)
{
*(p + i) = 1; //不可将每个元素改为1
} //循环一次只前进一个字节,而一个元素有4个字节
return 0;
}
野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
导致野指针的原因:
一·指针未初始化
#include <stdio.h>
int main()
{
int a; //局部变量不初始化,默认是随机值
int* p; //局部的指针变量不初始化,就会是随机值
*p = 20;//在内存中随机找一块空间放入20
return 0;
}
二·指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for ( i = 0; i < 15; i++)
{
*p = i; //越出arr的范围后就变为野指针
p++; //这两步可缩写为*p++ = i(后置++,先解引用再++
}
return 0;
}
三·指针指向的空间释放
#include <stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test(); //局部变量进入范围创建,出了范围就会被销毁
*p = 20; //a是存在于函数中,函数运行结束,a的空间被释放,p存放的地址就会变为随机
return 0;
}
规避出现野指针:
一·指针初始化
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a; //初始化
int* p = NULL; //NULL-用来初始化指针,给指针赋值(0)
return 0;
}
二·小心指针越界
三·指针指向空间释放即时设置为NULL(空/0)
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
*pa = 20; //不打算用pa时,设置成NULL
pa = NULL;
return 0;
}
四·指针使用之前检测有效性
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
*pa = 20;
pa = NULL;
*pa = 10; //指针为空时强行使用,程序会崩溃
return 0;
}
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
*pa = 20;
pa = NULL;
if (pa != NULL) //使用前检查有效性
{
*pa = 20;
}
return 0;
}
指针运算:
·指针+-整数
·指针-指针
·指针的关系运算
指针+-整数:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for ( i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[9];
for ( i = 0; i < sz; i++)
{
printf("%d ", *(p - i));
}
return 0;
}
指针-指针:
指针-指针得到的是地址之间的距离(一般只用于同一数组中)
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,20 };
printf("%zd\n", &arr[9] - &arr[0]); //打印9
return 0;
}
#include <stdio.h>
int main()
{
int a = 10; //地址为0x000000790CBCFC94
int b = 20; //地址为0x000000790CBCFCB4
printf("%zd\n", &a - &b); //打印-8
return 0;
}
用指针计算字符串长度:
#include <stdio.h>
int my_strlen(char* str)
{
char* start = str;
char* end = str;
while (*end != '\0')
{
end++;
}
return end - start;
}
int main()
{
//strlen - 求字符串长度
//递归 - 模拟实现了strlen(1.计数器的方式 2.递归的方式)
//1、2方法详见于 C-函数和递归-TWO
char arr[] = "bit";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
指针的关系运算:
#define MVP 3
#include <stdio.h>
int main()
{
float Player[MVP];
float* mvp;
for ( mvp=&Player[MVP]; mvp>&Player[0];)
{ //指针之间比较大小就是指针之间的关系运算
*--mvp = 0;
}
return 0;
}
#define MVP 3
#include <stdio.h>
int main()
{
float Player[MVP];
float* mvp;
for ( mvp = &Player[MVP-1]; mvp>= &Player[0]; mvp--)
{ //指针之间比较大小就是指针之间的关系运算
*mvp = 0;
}
return 0;
}
以上两种写法在绝大多数编译器上都能执行并完成任务,但避免第二种写法,C语言标准并不保证它可行
PS.C语言指针比较标准:
允许指向数组元素的指针于指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
PS.数组名代表:
绝大部分时候数组名都是首元素地址
例外:
一·&arr - &数组名 - 此时数组名不是首元素地址,数组名表示整个数组,&数组名 取出的是整个数组的地址
#include <stdio.h> int main() { int arr[10] = { 0 }; printf("%p\n", arr); //000000EE4F59FA58 printf("%p\n", arr + 1); //000000EE4F59FA5C 相差4 printf("%p\n", &arr); //000000EE4F59FA58 printf("%p\n", &arr + 1); //000000EE4F59FA80 相差28(16进制)=40(10进制) printf("%p\n", &arr[0]); //000000EE4F59FA58 printf("%p\n", &arr[0] + 1); //000000EE4F59FA5C 相差4 return 0; }
二·sizeof(arr) - sizeof(数组名) - 此时数组名也是表示整个数组,sizeof(数组名) 计算的是整个数组的大小
二级指针:
二级指针用来存储指针的地址
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa; //此时的ppa就是二级指针
//……………… 用二级指针可修改a的值
return 0;
} //ppa == &pa
//*ppa == pa == &a
//**ppa == *pa == a
//………………
指针数组:
指针数组 - 数组 - 存放指针的数组
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[3] = { &a,&b,&c }; //创建指针数组
int i = 0;
for ( i = 0; i < 3; i++)
{
printf("%d\n", *(arr[i]));
}
return 0;
}
数组指针 - 指针