开始:
1.指针的含义:
广义上,指针可以被定义为地址,而地址则是指针的值。当指针获得某个地址值时,它就指向了某个地址单元。
在计算机编程中,指针是C语言中的一个重要概念,它指的是内存地址,指针变量是用来存放内存地址的变量。在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。
例如:在x86(32位操作系统)中,指针的字节大小都是4个字节,但是x64操作系统中,指针的大小就为8个字节
2.指针为什么有各种类型?有什么用?
指针的大小与类型无关,在同一平台下,指针的大小是一样的。
指针的解引用可以提现出指针类型的意义
不同类型的指针可以访问的权限都不一样,例如:
int类型的指针可以访问4个字节,而char类型的指针只能访问一个字节。
结论:指针的类型决定了对指针解引用的时候有多大的访问权限!
3.特殊的void*指针!!!
在指针中也有一个特殊的类型是void*指针类型,可以理解为无具体类型的指针(泛型指针),这种指针可以被拿来接收任何类型的地址,但局限性是void*指针不能直接进行指针的+-整数和解引用的运算!
4.const修饰指针和指针变量
const所在的位置不同会导致有两种不同的效果。
如果修饰指针变量时,const放在*右边,这时候指针变量就不能指向其他变量了,但可以通过指针变量来修改它现在所指向的内容。
如果修饰指针变量时,const放在*左边,这时候指针变量可以指向其他变量,但不可以通过指针变量来修改它现在所指向的内容。
看下面代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int a = 50;
int b = 20;
int * const p = &a;
//p = &b; 错误,不能指向其他变量
*p = 5;
printf("%d\n", a); //5
return 0;
}
当放在右边时,可以修改指针变量所指向的内容,但不能指向其他变量,左边则相反!
5.野指针
野指针指向的位置是未知的不明确的随机的。
一般野指针的产生都是因为未初始化或者指针指向的地址已经被释放。
下面给大家一个野指针的例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int test()
{
int a = 50;
return &a;
}
int main()
{
int *p=test();
printf("%d", *p); //无结果
return 0;
}
图中的p指针就是野指针,当函数返回a的地址时由于a是局部变量,所以出函数会销毁,这时p还指向a的地址的话是不行的,因为那块地址已经释放了。
那么如何规避野指针呢?
1.初始化指针,一般如果暂时不知道指针该用于哪里,建议先初始化为NULL。
2.避免返回局部变量的地址。
6.传值调用和传址调用
有时候一些问题非指针不可,例如:
用函数交换两个变量的值
看代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Swap(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10;
int b = 20;
Swap(a,b);
printf("%d %d\n", a, b);//10 20
return 0;
}
我们可以看到打印出来a和b的值并没有交换,这是因为我们只是传值调用了
应该做个小修改:
传a和b的地址进入函数,就可以真正的交换a和b的值了
那么这两种方法有什么区别呢?
传值调用:这时传给函数的实参会被形参临时拷贝一份空间来接收,对形参的修改不影响实参。
传址调用:可以让主函数和函数真正建立起联系,在函数内部可以修改主函数里面的变量。
结论:需要主函数中的变量值去完成计算就用传值调用,如果在函数内部要修改主函数里面的变量就用传址调用!
7.二级指针
指针可以有二级指针,三级指针,四级指针......等等,但我们一般用到的也就到三级指针了!
上代码
int main()
{
int a = 5;
int* p = &a;
int * * pa = &p;
printf("%d", **pa);//5
return 0;
}
如图,我们用二级指针pa接收了指针p的地址,相当于接收了a的地址
那么我们应该怎么理解二级指针呢?
看图:
8.指针数组和数组指针
1.含义:
指针数组顾名思义就是存放指针的数组,和指针一样可以有各种类型。
而数组指针可厉害了,他是指向数组的指针,里面可以存放数组的地址。
给大家举个例子:
这里a是一个数组指针变量,指向了10个int字节大小的数组,就相当于把数组的地址存在数组指针里面,而且方括号里面的数字与指向的数组息息相关,只能是指向数组的大小,不可以省略不写, 因为[]比*的优先级要高,所以得加()确保a先和*结合,这样才叫数组指针!
2.数组指针的应用
那么我们来看看数组指针可以怎么用
上代码
void print(int arr[][5], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
print(arr, 3, 5);
return 0;
}
像这种二维数组传参来打印数组的函数,我们也可以用数组指针来接收二维数组的地址
像这样也可以达到目的,而我们的数组指针此时接收的就是二维数组的第一行的数组地址,我们可以把这个二维数组看成三个一维数组,每个一维数组都有五个元素,所以数组指针的[]里面就是5,打印的时候用*来获取数组的元素!
结尾:
今天就讲到这里,指针是C语言中最精华的部分,你越探索它就会越着迷,因为每次都能发现新的世界
有句话说得好:学过不等于学会,我们要沉住气慢慢摸索,迎接美好的那天!