指针是什么
指针是什么?
指针理解的2个要点:
- 指针是内存中一个最小单元的编号,也就是地址;
- 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量;
指针就是地址,指向某一块内存空间。
![image-20220518225220643](https://cdn.jsdelivr.net/gh/sxfinn/CDN/img/202212021604389.png)
我们有一个变量在栈中被创建,如果我们想找到它有几种方式呢?
- 通过变量名去访问
- 通过地址访问
这一个个编号就是虚拟内存地址,相当于将内存分为以一个字节为基本单位的很多单元体,通过对这些单元体的编号,我们就可以在任何位置找到一个对应的编号。
像不像我们现实中的地址门牌号?
指针变量
我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量。
int main()
{
int a = 10;//在内存栈中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
//中,p就是一个之指针变量。
return 0;
}
指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
那这里的问题是:
- 一个小的单元到底是多大?(1个字节)
- 如何编址?
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);
这样每个地址就可以用32个二进制位表示。每个二进制位有两种情况。
那么32根地址线产生的地址就会是:232
每个地址标识一个字节,那我们就可以给 (2^32Byte = 2^32/1024KB = 232 / 1024/1024MB = 2^32/1024/1024/1024GB = 4GB) 4G的空闲进行编址。
这里我们就明白:
-
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以
-
一个指针变量的大小就应该是4个字节。
-
那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
总结:
-
指针是用来存放地址的,地址是唯一标示一块地址空间的。
-
指针的大小在32位平台是4个字节,在64位平台是8个字节。
指针和指针类型
先看看基本类型:
整形
int
char
short
long
longlong
浮点型
double
float
那么指针指向不同的类型,是不是就代表着指针类型的不同呢?
可以确定的说:当然。
如下代码:
int a = 0;
int* p = &a;
p就是一个指针变量,指向一个int类型的数据。
根据a的类型的不同,p的类型也要做出相应的调正。
我们知道p就是一个指针变量,那它的类型是怎样的呢?
我们给指针变量相应的类型。
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
可以看到任何任何类型的指针的变量前都有一个 * , 大致模样是
type + * + 变量名
char*
类型的指针是为了存放 char
类型变量的地址。
short*
类型的指针是为了存放 short
类型变量的地址。
int*
类型的指针是为了存放 int
类型变量的地址。
指针可以根据指向不同的类型来分类,那么它们作用上有什么不同?
通常来说,想通过地址访问变量,不同变量的存储方式,大小都不同,那么指针去访问的时候的权限以及解引用访问方式是不是应该也不同。
int i;
double d;
//假如它们的首地址相同
int* p1 = &i;
double *p2 = &d;
p1 == p2;
int 和 double的类型长度不同,那么通过解引用方式取得 i 和 d的值的时候,所访问的字节长度也不同。
//演示实例
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;
}
输出:
可以看到指针类型决定了指针加一或者减一时跳过几个字节。
指针的解引用
int main()
{
int n = 0x11223344;
char* pc = (char*)&n;
int* pi = &n;
*pc = 0; (1)
*pi = 0; (2)
return 0;
}
上图为(1)被执行
下图为(2)被执行
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
野指针
成因
- 指针未初始化
int main()
{
int* p;
int a = *p;
return 0;
}
会报错:使用了未初始化的局部变量。
- 指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 11; i++)
{
*(p++) = i;//当指针指向的范围超出数组arr的范围时,p就是野指针
}
return 0;
}
如何规避野指针的使用
- 指针初始化;
- 注意指针的越界;
- 指针使用后及时置NULL;
- 函数体中不应返回局部变量的地址;
- 检查指针的有效性;
#include <stdio.h>
int main()
{
int *p = NULL;
//....
int a = 10;
p = &a;
if(p != NULL)
{
*p = 20;
}
return 0;
}
指针运算
- 指针 + 整数
- 指针 - 整数
- 指针 - 指针
指针 +- 整数
指针的值增加(减少) = 指向的类型的字节数 * 整数。
指针 - 指针
结果等于:两个指针相差的字节数 / 指向类型的字节数。
指针的关系运算
就是普通的地址值的比较。
值得注意的是,在对数组的操作中:
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
指针和数组
看代码:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
数组名和数组首元素的地址的值是一样的。
但是注意类型是不同的,只是在数值上相等。
我们既然可以知道数组元素的指针,那么自然可以实现对其元素地址的逐一访问。
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;
}
输出:
可以看到&arr[i] == (arr + i )。
既然连地址都可以逐一访问了,那么地址上所存储的数据自然可以轻松获得:
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 ", *(p + i));
}
return 0;
}
二级指针
指针是存储变量地址的变量,那么自然指针变量也是需要一块内存空间来存储它的值。
如果我们想得到指针变量的地址,只能对指针变量&操作。
如果我们也想找一个变量用来存储指针变量的值,那么这个变量的类型是什么?
这就是二级指针。
int main()
{
int num = 10;
int* pa = #
int** ppa = &pa;
return 0;
}
那么可以通过二级指针获取int变量的值吗?
是的,我们既然二级指针记录了一级指针的值,就可以取得一级指针值,而一级指针的值又是int变量的地址,层层递进解引用,便可以获取int变量的值。
*ppa = pa;
**ppa = *pa = num
指针数组
元素类型为整形集合的数组叫什么?
答案是整形数组,那么理所应当,元素类型为指针的数组就叫指针数组了。
我们创建一个整型数组或者字符数组如下:
int arr1[10];
char arr2[10];
数组名可以看作一个变量名,[10]代表这是个数组中有十个元素,那么剩下的就是元素类型。
我们创建一个有是个元素,每个元素为int*指针的数组:
我们创建一个int*变量是如何创建的?
- 确定变量名
- 确定变量的类型
- 在变量名前加上类型名
那么数组也是差不多的。
首先写出想要的数组名
arr;
arr是个数组,并且有十个元素,我们加上[10];
arr[10];
元素类型是什么? int*
int* arr[10];
整形数组在内存中的存储
![image-20220518225336391](https://cdn.jsdelivr.net/gh/sxfinn/CDN/img/202212021605633.png)
![image-20220518225342798](https://cdn.jsdelivr.net/gh/sxfinn/CDN/img/202212021605083.png)
指针数组呢?
可以看到,每个元素都是一个指针,指向一块特定的内存空间。
思考:指针数组的元素可以是数组名吗?
答案是的,除了对数组名进行sizeof和&操作的两种情况外,其他时候的数组名,一律当作指针来使用。
标签:arr,变量,int,C语言,地址,详解,数组,指针 From: https://www.cnblogs.com/ncphoton/p/16950152.html