目录
在学习C语言的过程中,指针绝对是一个比较难懂的知识点,但是指针又是整个C语言中的精华,作为一个C语言开发人员,C语言指针是我们必须需要掌握的,那该如何来学习呢?下面将跟随我的步伐分两步慢慢来学习指针,感受指针之美!
1. 指针是什么?
在认识指针之前,我们需要简单先认识一下内存:
内存:内存是用来存放数据的硬件,程序执行前需要先放到内存中才能被CPU处理
指针是什么呢?
指针其实就是存放在内存中一个最小单元编号,也就是地址。
我们平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
总结:指针就是地址,我们口语中常说的指针通常指的是指针变量
那指针变量又是什么呢?
顾名思义,我们知道指针其实就是一个地址,那指针变量就是一个存放地址的变量了。
代码演示:
int main()
{
int a = 10;//在内存中开辟一块空间
int* pa = &a;
int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
/*a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
中,p就是一个之指针变量*/
return 0;
}
指针的大小是多少呢?
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址指针的大小在32位平台是4个字节,在64位平台是8个字节
2. 指针和指针类型
我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
准确的说:是有的
那这里肯定就会有人疑惑了,不是说存放指针的空间在相同的平台上的空间上都是相等的吗?那不管什么类型都可以存放啊,那为什么还要有指针类型呢,它的意义是什么呢?
下面跟随我的代码演示就能明白了:
这里可以看到我们用不同的类型来存放地址都是一样的。那意义呢?
2.1 指针+-整数
通过这个代码这么一演示,大概就能明白指针类型的意义是什么了吧,原来类型决定了指针向前或向后走一步的距离(如代码演示:int*类型的指针走一步走了4个字节,char*类型是一个字节)
总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。
2.2 指针的解引用
我们通过给两个不同类型的指针变量进行赋值就会发现,同时赋值为0时,int*类型的指针四个字节全部都赋值为了0,cha*类型的指针只有一个字节赋为了0
总结:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
3. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
1. 指针未初始化
int main() { int* pa;//局部变量指针未初始化,默认为随机值 *pa = 20; return 0; }
2.指针越界访问
int main() { int arr[10] = { 0 }; int* p = arr; int i = 0; for (i = 0; i < 12; i++) { (*p) = i; p++; // 当指针指向的范围超出arr数组的范围时,p指针就是野指针 } return 0; }
3.指针指向的空间释放
int* test() { int a = 10; return &a; } int main() { int* pa = test();//返回a的地址时a的空间释放了 //pa就成野指针 printf("a=%d", pa); return 0; }
3.2 如何规避野指针
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
int main() { //当不知道指针初始化为什么地址时,初始化为NULL(空指针) int* p = NULL; return 0; }
4.指针运算
4.1 指针+-整数
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* a = arr;//arr为首元素地址
int* b = arr + 9;
while (a<=b)
{
printf("%d ", *a);
a++;
}
return 0;
}
4.2 指针-指针
我们在前面学习了用计数法,递归法来计算字符串长度,这次我们用指针-指针的方式
int my_strlen(char* str) { char* start = str; while (*str != '\0') { str++; } return str - start; } //实现stelen函数计算字符串长度 int main() { int len = my_strlen("abcd"); printf("%d\n",len); return 0; }
4.3 指针的关系运算
#define N_VALUES 5 float values[N_VALUES]; float* vp;//全局指针变量默认初始化为0 int main() { //指针+-整数;指针的关系运算 for (vp = &values[0]; vp < &values[N_VALUES];) { *vp++ = 0; //= *vp=0 // *vp++ } return 0; }
标注规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
5.指针和数组
数组名就是首元素地址
已知可以把数组名当成地址存放在数组中,那么我们就可以通过这个来访问
int main() { int arr[] = { 1,2,3,4,5 }; int* p = arr; int i = 0; for (i = 0; i <5; i++) { printf("%d ", *(p + i));// p+i 其实计算的是数组 arr 下标为i的地址 } }
6. 二级指针
二级指针:指针变量也是变量,是变量就有地址,用来存放指针变量的地址就二级指针
int main() { int a = 10; int* pa = &a;//存放a的地址(一级指针) int** ppa = &pa;//存放pa的地址(二级指针) return 0; }
7.指针数组
指针数组是指针还是数组呢?
我们可以这么来理解,好孩子—他还是一个孩子,好只是修饰的。使用指针数组其实是一个数组。
int main() { int arr[] = { 0 };//整形数组—就是存放整形的数组 char ch[] = { 0 };//字符数组—就是存放字符的数组 int* app[5]; //指针数组—存放整形指针的数组 char* cpp[5];//存放字符指针的数组 return 0; }
初始指针篇 完