一、指针是什么?
在计算机科学中,指针(pointer)是编程语言中的 一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的成为“指针”。意思是通过它能找到以它为地址的内存单元。
总结:1、指针就是变量,用阿狸存放地址的变量。(存放在指针中的值都被当成地址处理)
2、指针是用来存放地址的,地址是唯一标识一块地址空间的。
3、指针大小在32位平台是4个字节,在64位平台上是8字节。
二、指针类型:
1、*指针类型决定了指针进行解引用操作的时候,能够访问空间的大小。
int* p; *p能够访问4个字节;
char* p;*p能够访问1个字节;
double* p;*p能够访问8个字节。
2、*指针类型决定了:指针走一步多远(指针的步长)
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);
printf("%p\n", pa+1);
printf("%p\n", pc);
printf("%p\n", pc+1);
}
代码执行:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
//int* p = arr;//数组名-首元素的地址
char* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i)=1;
}
return 0;
}
打开调试->内存,会发现char*与int*解引用操作在内存中的区别。
int* p;p+1-->4
char* p;p+1-->1
double* p;p+1-->8
三、野指针
类型一:指针未初始化
#include<stdio.h>
int main()
{
int a;//局部变量不初始化,默认是随机值
int* p;//局部的指针变量,就会被初始化
*p = 20;
return 0;
}
类型二:指针越界访问
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 12; i++)
{
//当指针指向的范围超出了数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
类型三:指针指向的空间释放
如何避免野指针?
1、指针初始化;
2、小心指针越界;
3、指针指向空间释放及时置NUL;
4、指针使用之前检查有效性。
#include<stdio.h>
int main()
{
int* p = NULL;
int a = 10;
p = &a;
if (p != NULL)
{
*p = 20;
}
return 0;
}
四、指针运算
1、指针+-整数
#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);
p++;
}
return 0;
}
2、指针-指针
eg1、计算之间的距离
#include<stdio.h>
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("%d\n",&arr[0] - &arr[9]);
return 0;
}
eg2、计算字符串长度
#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
//
char ch[] = "bit";
int len=my_strlen(ch);
printf("%d\n",len);
return 0;
}
代码实现:
3、指针的关系运算、
#include<stdio.h>
#define N_VALUES 5
int main()
{
float values[N_VALUES];
float* vp;
//指针的+-整数,指针的关系运算
for (vp = &values[N_VALUES]; vp >&values[0];)
{
*--vp = 0;
}
printf("%lf ", values);
return 0;
}
#include<stdio.h>
#define N_VALUES 5
int main()
{
float values[N_VALUES];
float* vp;
for (vp = &values[N_VALUES - 1]; vp >= &values[0]; vp--)
{
*vp = 0;
}
return 0;
}
标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的哪个内存位置的指针进行比较。
五、指针和数组
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n",arr);//地址-首元素的地址
printf("%p\n", arr + 1);
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0] + 1);
printf("%p\n", &arr);//
printf("%p\n", &arr + 1);
//1、&arr-&数组名不是首元素的地址-数组名表示整个数组-&数组名 取出的是整个数组的地址
//2、sizeof(arr)-sizeof(数组名)-数组名表示的是整个数组-sizeof(数组名)计算的是整个数组的大小
return 0;
}
&arr[0]+1、arr+1都是一个元素+1,地址加4个字节;&arr+1是一个数组+1,相当于+10个元素为40个字节。
代码实现:
1、&arr-&数组名不是首元素的地址-数组名表示整个数组-&数组名 取出的是整个数组的地址
2、sizeof(arr)-sizeof(数组名)-数组名表示的是整个数组-sizeof(数组名)计算的是整个数组的大小
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
//printf("%d ",arr[i]);
printf("%d ", *(p+i));
}
return 0;
}
代码实现:
六、二级指针
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;//ppa就是二级指针
**ppa = 2;
printf("%d\n",**ppa );
printf("%d\n",a );
return 0;
}
代码执行:
七、指针数组
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* pa = &a;
int* pb = &b;
int* pc = &c;
//整形数组-存放整形
//字符数组-存放字符
//指针数组-存放指针
int* arr[3]={&a,&b,&c};//指针数组
int i = 0;
for (i -= 0; i < 3; i++)
{
printf("%d ", *(arr[i]));
}
return 0;
}
代码实现: