指针是C语言中的一大难题,这篇文章将带大家简单了解运用指针。
int main()
{
int a = 10;//在内存中开辟一块空间
int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//这里的int表示a的类型是整型、这里的*表示p是指针。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是
//一个之指针变量。
return 0;
}
总结一下:指针变量,用来存放地址的变量(存放在指针中的值都被当成地址处理)。
那下面又来了两个问题:1.一个小的单元到底是多大?2.如何编址?
首先我们回答第一个问题,一个小的单元是1个字节。
下面我们看二个问题,先要了解原理:
char* pc = NULL;//存放char类型变量的字节,存放的数据大小为1字节
int* pi = NULL;//存放int类型变量的字节,存放的数据大小为4字节
float* pf = NULL;//存放float类型变量的字节,存放的数据大小为4字节
我们发现pa和pa+1的地址相差4个字节,pc和pc+1的地址却只相差1个字节,也就是说整型;指针+1跳过4个字节,而字符指针+1只跳过1个字节,我们得到结论:指针的类型决定了指针向前或者向后走一步有多大(距离),跳过的是n(+n)* sizeof(type)个字节。
3. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
野指针的成因有两个:1.指针未初始化 2.指针越界访问
1. 指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
这就是一个典型的指针未初始化,它未初始化局部变量“p”,我们定义指针的时候: type * p;这里*说明p是指针变量,type说明p指向的对象的类型,同时也代表p解引用的时候访问的大小是sizeof(type)。但是我们必须在定义的时候给他右边一个确切的要取出的地址,否则就会导致编译器报错。
2.指针越界访问
指针的越界访问是很常见的错误,也有几种错误类型,我们举例子来说明一下:
这个代码运行的时候,就会出现程序报错问题,这就是因为:arr的下标只从0—9,而我们在访问下标的时候,给到范围全达到了i<=11,这就导致i=10和i=11的时候,是找不到数组的,所以就会出现指针越界访问,从而报错。
下一个例子:
这个代码我们看的第一眼像是对的,其实它是有问题的代码,a的空间是进入函数创建的,那么它出函数返还给操作系统的时候,它的空间就已经没有了,但是它的地址传出来了,所以主函数其实运行的时候就已经越界调用访问了test() . 所以虽然运行成功了,但是这个代码其实是有问题的,p还是一个野指针。
那我们如何避免野指针呢?我们需要注意以下几点:
1. 指针初始化
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
这里也可以写成:arr[i] == *(arr+i) == *(i+arr) == i[arr]
2.指针-指针
int my_strlen(char *s)
{
char *p = s;
while(*p != '\0' )
p++;
return p-s;
}
这也是有前提条件的:指针和指针(两个指针)指向同一块空间。
指针-指针得到的数值的绝对值是指针之前的元素个数。
3.指针的关系运算
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
地址是有大小的,指针的关系运算就是比较指针的大小。
有的人会简化以上代码:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
其实这样子写是不符合标准规定的,标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。所以如果这样子写,会导致出现指针指向第一个元素之前的那个元素位置的指针进行对比,会出现指针越界。
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证 它可行。
5. 指针和数组
很多人可能会混淆数组和指针。
指针变量就是指针变量,不是数组,指针变量的大小是4/8个字节,专门是用来存放地址的。
数组就是数组,不是指针,数组是一块连续的空间,可以存放1个或多个类型相同的数据。
但是指针和数组也是有很多联系的:数组中,数组名其实是数组首元素的地址,数组名==地址==指针,当我们知道数组首元素的地址的时候,因为数组又是连续存放的,所以通过指针就可以遍历访问数组,数组是可以通过指针来访问的。
我们看一个例子:
可见数组名和数组首元素的地址是一样的。
所以我们也可以直接通过指针来访问数组:
6. 二级指针
指针变量也有变量,是变量就有地址,那指针变量的地址存放在哪里?
int a = 10;
int* p = &a;//p是一级指针,指针变量也是变量,变量是在内存中开辟空间的,是变量就有地址
int** pp = &p;//pp是二级指针,二级指针变量就是用来存放一级指针变量的地址
二级指针了解即可,一般用途少。
7. 指针数组
这就是指针数组的一个例子,其中parr[i][j],其实就是下标为i的那个数组,下标为j的数字,这也是模拟二级数组。
8.总结
综上所述,我们已经基本学习完了关于指针的基础知识,希望大家能学习透彻,进而学习更深层次的指针教学。