指针
指针重点
- 指针是什么
- 指针和指针类型
- 野指针
- 指针和数组
- 二级指针
- 指针数组
指针是什么
在计算机科学中,指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为"指针"。意思是通过它能找到它为地址的内存单元。
可以理解为:编号 = 地址 = 指针
int mian()
{
int a =10;
int * pa = &a;
return 0;
}
指针变量
int main()
{
int a =10;
printf("%p\n",&a);
int * pa = &a;
return 0;
}
解引用操作
int main()
{
int a =10;
int * pa =&a;
*pa = 20;
printf("%d\n",a);
return 0;
}
可以理解为: 指针 就是 地址
指针变量的大小
int mian()
{
printf("%d\n",sizeof(short*));
printf("%d\n",sizeof(int*));
printf("%d\n",sizeof(long*));
printf("%d\n",sizeof(long long*));
printf("%d\n",sizeof(char*));
printf("%d\n",sizeof(float*));
printf("%d\n",sizeof(double*));
return 0;
}
Console:
4
4
4
4
4
4
4
从上总结下来:
-
指针大小是相同的 -
指针是用来存地址的 -
所以指针需要多大的空间,取决于地址的存储需要多大空间 -
指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理) -
指针是用来存放地址的,地址是唯一标示一块地址空间的。 -
指针的大小在32位平台是4个字节,在64位平台是8个字节。
指针类型
- 指针类型决定了:指针解引用的权限有多大
- 指针类型决定了,指针走一步,能走多远(步长)
int mian()
{
int arr[10] = {0};
int * p = arr;
char * pc = arr;
printf("%p\n",p);
printf("%p\n",p+1);
printf("%p\n",pc);
printf("%p\n",pc+1);
return 0;
}
Console:
004FFC40
004FFC44
004FFC40
004FFC41
总结:
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。比如:char * 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针形成原因
- 指针未初始化
#include <stdio.h>
int mian()
{
int *p;
*p = 20;
return 0;
}
Console:
使用了未初始化的局部变量"p"
- 指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for( i = 0; i <= 11; i++)
{
*(p++) = i;
}
return 0;
}
- 指针指向的空间释放
如果指针指向的内存空间被释放了,那么指针指向的空间就为空了,这时候指针就成为了野指针。
如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放及时置NULL
- 指针使用之前检查有效性
C语言本身是不会检查数据的越界行为的,所以小心指针越界。
int mian()
{
int * p = NULL;
int a = 10;
int * p2 = &a;
return 0;
}
指针运算
指针 - 指针
int mian()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n", &arr[9] - &arr[0] );
return 0;
}
Console:
9
从上面的例子可以看出来:
指针 - 指针得到的两个指针之间的元素个数
int mian()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
char c [5];
printf("%d\n", &arr[9] - &c[0]);
return 0;
}
这里会运行报错,出现一个提示:
从"char*" 到 “int *” 打的类型不兼容
所以可以总结:
指针和指针相减的前提:两个指针指向同一块空间
用计数器求字符串长度
int my_strlen(char* str)
{
int count = 0;
while(*str != '\0')
{
count++;
str++;
}
}
int main()
{
int len = my_strlen("abc");
printf("%d\n",len);
}
用递归求字符串长度
int strlen(char* str)
{
if(*str != '\0')
return 1 + strlen(str + 1);
else
return 0;
}
int mian()
{
char arr[] = "abc";
printf("%d\n",strlen(arr));
return 0;
}
Console:
3
用指针 - 指针计算字符串长度
int my_strlen(char* str)
{
char* start = str;
while(*str != '\0')
{
str++;
}
return str - start;
}
int main()
{
int len = my_strlen("abc");
printf("%d\n",len);
return 0;
}
Console:
3
指针的关系运算
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
指针和数组
数组名是什么?
int mian()
{
int arr[10] - {0};
printf("%p\n",arr);
printf("%p\n",&arr[0]);
return 0;
}
Console:
0059FD48
0059FD48
从上面例子可以得出一个结论: 数组名是数组首元素的地址
int mian()
{
int arr[10] = {0};
int* p = arr;
int i = 0;
for( i = 0; i < 10; i++)
{
printf("%p <==> %p\n",&arr[i],p + i);
}
return 0;
}
Console:
00EFFA64 <==> 00EFFA64
00EFFA68 <==> 00EFFA68
00EFFA6C <==> 00EFFA6C
00EFFA70 <==> 00EFFA70
00EFFA74 <==> 00EFFA74
00EFFA78 <==> 00EFFA78
00EFFA7C <==> 00EFFA7C
00EFFA80 <==> 00EFFA80
00EFFA84 <==> 00EFFA84
00EFFA88 <==> 00EFFA88
由上的例子可以得到下面一段关系:
int mian()
{
int arr[10] = {0};
int* p = arr;
int i = 0;
for( i = 0; i < 10; i++)
{
*(p + i) = i;
}
for( i = 0; i < 10 ; i++)
{
printf("%d",*(p + i));
}
return 0;
}
Console:
0 1 2 3 4 5 6 7 8 9 10
[ ] 是一个操作符,2 和 arr 是两个操作数,所以有:arr[2] = 2[arr] = p[2]
int mian()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int *p = arr;
printf("%d\n",2[arr]);
printf("%d\n",arr[2]);
printf("%d\n",p[2]);
return 0;
}
Console:
3
3
3
二级指针
指针变量也是变量,是变量就有地址,那指针的变量存放在哪?这就是二级指针。
int main()
{
int a = 10;
int* pa = &a;
int **ppa = &pa;
return 0;
}
二级指针的内存实现:
*ppa == pa ppa是二级指针
*pa == a pa是一级指针
**ppa == a
_________________
| | a
| 10 | (0x0012ff40)
—————————————————
| | pa
| (0x0012ff40) | (0x0012ff48)
—————————————————
| | ppa
| (0x0012ff48) |
—————————————————
所以,总结下来:
二级指针存放的是一级指针的内存
指针数组
指针数组: 存放指针的数组
int mian()
{
int arr[10];
char ch[10];
int* parr[5];
char* pch[5];
}
|