目录
一、指针和指针类型
1、指针
指针是个变量,存放内存单元的地址(编号)。通过它能找到以它为地址
的
内存单元。
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//将a的地址存放在p变量中,p就是一个之指针变量。
return 0;
}
指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针在不同平台上的字节大小不同:指针的大小在
32
位平台是
4
个字节,在
64
位平台是
8
个字节
。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(double*));
return 0;
}
2、指针类型
(1)既然不同的类型的指针都是4个字节,为什么要不同的指针类型?
#include <stdio.h>
int main()
{
int n = 0x11223344;
char* pc = (char*)&n;
int* pi = &n;
*pc = 1;
*pi = 1;
printf("%p\n", pc);
printf("%p\n", pi);
return 0;
}
从程序的运行结果可以看到,char* pc,int* pi的地址是一样的,但同样的赋值操作打印的结果却不同,这是因为n的存储在内存中是16进制的44 33 22 11(大端,小端问题),pc是char类型的指针,赋值时只访问1个字节,即n被改为 16进制的01 33 22 11,即n = 11 22 33 01(10进制即287453953);,pi是int类型的指针,赋值时访问4个字节,即n被改为 16进制的01 00 00 00,即n = 00 00 00 01(10进制即1).
结论1:指针类型决定了指针进行解引用操作符(*)能够访问空间的大小
int* p //*p 能够访问4个字节
char* p //*p 能够访问1个字节
double* p //*p 能够访问8个字节
(2)指针类型的意义
#include <stdio.h>
//演示实例
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc + 1);
printf("%p\n", pi);
printf("%p\n", pi + 1);
return 0;
}
结论2:指针类型决定了指针走一步走多远
int* p //p+1 ,指针偏移4个字节
char* p //p+1 ,指针偏移1个字节
double* p //p+1 ,指针偏移8个字节
3、二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是
二级指针
。
int b = 20;
*ppa = &b;//等价于 pa = &b;
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
指针
+-
整数
#define N_VALUES 5
int main()
{
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
return 0;
}
指针
-
指针
//求字符串长度的函数
int my_strlen(char *s) {
char *p = s;
while(*p != '\0' )
p++;
return p-s;
}
指针的关系运算
//方式1
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
代码简化
,
这将代码修改如下:
//方式2
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) {
*vp = 0; }
C语言标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许
与指向第一个元素之前的那个内存位置的指针进行比较
因此使用方式1更合适。
二、指针和数组
1、指针和数组之间的关系
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;//p存放的是数组首元素的地址
由数组知识可知,数组名表示的是数组首元素的地址,指针同样指向地址,那么对与相同类型的数组或指针,是否存在着一定的关系呢?
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr; //指针存放数组首元素的地址
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<sz; i++)
{
printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p+i);
}
return 0;
}
所以
p+i
其实计算的是数组
arr
下标为
i
的地址。
那我们就可以直接通过指针来访问数组。
如下:
#include <stdio.h>
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int *p = arr; //指针存放数组首元素的地址
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i<sz; i++)
{
//printf("%d ", arr[i]);
printf("%d ", *(p + i));//与上面那行效果一致
}
return 0;
}
2、指针数组
(1)指针数组是数组还是指针呢?
指针数组是数组,存储的是指针
int main()
{
int a = 10;
int b = 20;
int c = 30;
//整形数组 ->存放整形
//字符指针 ->存放字符
//指针数组 ->存放指针
int* arr[3] = { &a,&b,&c };//指针数组
return 0;
}
三、野指针问题
1、野指针产生原因
1. 指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2. 指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3. 指针指向的空间释放
int* test()
{
int a = 10;
return &a;//函数调用结束即销毁
}
int main()
{
int*p = test();//虽接收到地址,但此时已不是a的地址
return 0;
}
2、如何规避野指针
1.
指针初始化
2.
小心指针越界
3.
指针指向空间释放即使置
NULL
4.
指针使用之前检查有效性