由浅入深理解指针-----指针详解(指针初阶)
文章目录
前言
在C语言的学习中,指针是一个让我们非常头疼知识点,指针的知识点多,且易混淆,要想学好指针,首先要知道指针是什么,指针类型的作用是什么,指针的种类有哪些,各种指针怎么用?这些问题在这片文章中你都会得到详细解答哦!
提示:以下是本篇文章正文内容,下面案例可供参考
一、指针是什么?
首先来看看看百度百科怎么说
指针是内存中一个最小单元的编号,也就是地址,指针描述了数据在内存中的位置,标示了一个占据存储空间的实体,在这一段空间起始位置的相对距离值。
指针变量是用来存放内存地址的变量,在同一CPU构架下, 不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
在 C/C++语言中,指针一般被认为是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。
指针理解的2个要点:
- 指针是内存中一个最小单元的编号,也就是地址
- 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:指针就是地址,口语中说的指针通常指的是指针变量。
那我们就可以这样理解:
指针变量
我们可以通过&(取地址操作符)取出变量的内存其实是地址(也就是指针),把地址可以存放到一个变量中,这个变量就是指针变量
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量。(指针的大小在32位平台是4个字节,在64位平台是8个字节。)
return 0;
}
二、指针和指针类型
我们都知道,变量有不同的类型,整形,浮点型等。其实指针也是有不同的类型整形,浮点型等。那么指针类型的意义是什么?
前面说了, 不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。因此指针类型决定了所处储存的数据类型占用储存空间的大小。
char* 类型的指针是为了存放 char 类型变量的地址。占用一个字节
short* 类型的指针是为了存放 short 类型变量的地址。占用两个字节
int* 类型的指针是为了存放 int 类型变量的地址。占用四个字节
当我们在进行指针运算的时候,就可以充分认识到这一点。
指针的运算
指针±整数
指针变量±整数(n) 起始时跳过n*sizeof(指针类型)个字节
#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;
}
执行结果如图
char* 类型的指针加一向前移动了一个字节
int* 类型的指针加一向前移动了四个字节
因此 指针的类型决定了指针向前或者向后走一步有多大(距离)。
指针也是可以进行关系运算的,就是比较指针所占内存的大小。这里就不做详细讲解了,
我给大家找了一个关于指针运算的博主
想要更详细的了解指针的运算可以点击这里
指针的解引用
//演示实例
#include <stdio.h>
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
int *pi = &n;
*pc = 0; //重点在调试的过程中观察内存的变化。
*pi = 0; //重点在调试的过程中观察内存的变化。
return 0;
}
调试运行如下
这一步未执行解引用赋值
执行char* 类型解引用
执行int* 类型解引用
因此 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节
野指针
这里就简单跟大家说一下什么是野指针,不深入讲解
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针的形成原因一般是:
- 指针未初始化
- 指针越界访问
- 指针指向的空间释放
三、指针的种类有哪些
这算是指针进阶的知识,在讲不同种类的指针之前先给大家讲接一下,指针和数组
补充知识:指针和数组
1.数组名表示的是数组首元素的地址,这一点和指针很像所以在这里给大家补充一下;
int main()
{
int arr[8] = { 0,1,2,3,4,5,6,7 };
int *p = arr;
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%p\n", p);
return 0;
}
代码运行结果如下
由图可以看出三个printf执行的结果相同所以说数组名表示首元素的地址。在大多数情况下数组名表示首元素的地址但是也有两个例外:
- sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数 组。
- &数组名,取出的是数组的地址。&数组名,数组名表示整个数组
其他时候数组名都表示首元素地址
1. 字符指针
字符指针:有两种使用方法
第一种是存放字符的指针,还有一种是存放字符串的指针
这两种使用方法都是创建一个字符指针变量就行了,然后把地址放进去就可以了。
1.存放字符
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
2.存放字符串
int main()
{
const char* pstr = "hello Word.";//这里是把一个字符串放到pstr指针变量里了吗?并不是,这里的pstr存的是字符串的首字符h的地址。
printf("%s\n", pstr);
return 0;
}
但是值得注意点是字符指针变量 pstr 存的是字符串的首字母的地址;这里给大家看一个实例
int main()
{
char str1[] = "hello word.";
char str2[] = "hello word.";
const char* str3 = "hello word.";
const char* str4 = "hello word.";
if (str1 == str2)
{
printf("str1 == str2 \n");
printf("%p\n", str1);
printf("%p\n", str2);
}
else
{
printf("str1 != str2 \n");
printf("%p\n", str1);
printf("%p\n", str2);
}
if (str3 == str4)
{
printf("str3==str4 \n");
printf("%p\n", str3);
printf("%p\n", str4);
}
else
{
printf("str3!=str4 \n");
printf("%p\n", str3);
printf("%p\n", str4);
}
return 0;
}
运行结果在这里
这个是实例告诉我们两点,str1 != str2 说明内容的数组在内存中开辟了不同的空间,str3 == str4 内容相同的字符串常量在内存中的只开辟同一块内存空间
总结 这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。
但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
请大家思考一下 &str3==&str4?
2. 指针数组和数组指针
指针数组是一个存放指针的数组。存放在数组中元素都是指针形式如下
int* arr1[10]; //整形指针的数组
char* arr2[4]; //一级字符指针的数组
char** arr3[5];//二级字符指针的数组
指针数组怎么使用呐?其实指针数组多用构建二维数组,话不多说直接上代码。
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 6,7,8,9,10 };
int arr3[5] = { 11,12,13,14,15 };
int* arr[3] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d", arr[i][j]);
}
printf("\n");
}
return 0;
}
运行结果
数组指针
我们知道 整形指针: int *pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。数组指针的形式又是什么?
> int (*p)[10]; //解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个
> 指针,指向一个数组,叫数组指针。 //这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
这片文章先到这里后面的这些内容我会出单独出详细讲解各个模块。
函数指针
函数指针数组
指向函数指针数组的指针