指针是C语言的精髓,很多人都觉得指针难学,是因为内心对指针有所恐惧,把自己吓退了。一种应用语言能有多难,只要了解了指针的本质,学习起来就超级简单。
一、什么是指针
1.1、指针就是一种变量,一种特殊的变量,就这么简单。
这个特殊体现在,指针中保存的值是内存中的地址。前面了解过int型变量,char型变量,float型变量,它们保存的是对应类型的数据,int型变量保存的是int型的数据,char型变量保存的是char型的数据,float型变量保存的是float型的数据,而指针这种变量保存的是内存的地址。
1.2、这里引入了内存,那什么是内存,什么是内存地址?
内存是计算机里面的存储部件,就是常说的内存条,最小的存储单元就是一个字节
内存中每个存储单元都有一个编号,这个编号就是内存地址;
比如32位的系统,能范围的地址范围是0x00000000--0xFFFFFFFF,这个0x00000000和0xFFFFFFFF就是一个存储单元的编号,也就是内存地址。这个编号就是一个整型数。
同理64位操作系统的地址范围就是0x0000000000000000--0xFFFFFFFFFFFFFFFF。
我们还应知道,程序在运行的时候,程序里面所有的东西都在内存中,所以我们可以通过内存地址来访问程序中的所有元素。
我们知道:
一个char型变量占1个字节
一个int型变量占4个字节
......
我们既可以通过变量名来访问变量,也可以通过内存地址来访问变量。
1.3、如何获取程序元素的地址
程序元素可以是变量、数组、函数
使用&操作符可以获取程序元素的地址,获得的是元素的起始地址
内存地址本质上是一个无符号的整型,32位系统,内存地址占4个字节;64位系统,内存地址占8个字节。
程序实例1:获取变量的地址
#include <stdio.h>
int main()
{
int var = 18;
printf("var的值是%d\n",var);
printf("var的地址是%p\n", &var); //打印指针类型用%p,取地址用&操作符
return 0;
}
输出结果:本人电脑使用的是64位操作系统,输出的地址就是64位的
var的值是18
var的地址是0000008146D3FCC4
通过内存地址可以访问变量,前面有说过,我们获取变量的地址得到的是变量在内存中的起始地址,要想获得一个变量的值,还需要知道变量在内存中的长度,就是占几个内存单元,或者说是占几个字节,比如int型变量就是占4个字节,连续的4个字节。
所以C语言是通过变量的类型来确定变量的长度,用变量的起始地址确定变量的位置。
1.4、指针如何定义
指针的定义语法:
//指针定义语法:type * pointer
程序实例2:指针的定义
#include <stdio.h>
int main()
{
char* pChar; //定义一个char类型指针
int* pInt; //定义一个int类型指针
float* pFloat; //定义一个float类型指针
double* pDouble; //定义一个double类型指针
printf("pChar的地址是%p\n", &pChar); //打印指针类型用%p,取地址用&操作符
printf("pInt的地址是%p\n", &pInt); //打印指针类型用%p,取地址用&操作符
printf("pFloat的地址是%p\n", &pFloat); //打印指针类型用%p,取地址用&操作符
printf("pDouble的地址是%p\n", &pDouble); //打印指针类型用%p,取地址用&操作符
printf("pChar占用的空间是%d个字节\n", sizeof(char*)); //char*指针类型占的内存大小
printf("pInt占用的空间是%d个字节\n", sizeof(int*));
printf("pFloat占用的空间是%d个字节\n", sizeof(float*));
printf("pDouble占用的空间是%d个字节\n", sizeof(double*));
return 0;
}
输出结果:
pChar的地址是000000F60E6FF5D8
pInt的地址是000000F60E6FF5F8
pFloat的地址是000000F60E6FF618
pDouble的地址是000000F60E6FF638
pChar占用的空间是8个字节
pInt占用的空间是8个字节
pFloat占用的空间是8个字节
pDouble占用的空间是8个字节
1.5、指针内存访问
通过指针访问操作符*来访问内存数据,指针类型占据的内存大小在系统里面是固定的,32位系统的地址就是4个字节,64位系统的地址就是8个字节,前面程序实例2的输出结果就能说明问题。
程序实例3:
#include <stdio.h>
int main()
{
int var = 0; //定义一个var变量,并初始化为0
int* pVar = NULL; //定义一个int* 类型的pVar变量,并初始化为0地址
printf("var = %d\n", var); //打印的值就是0
printf("var的地址是 %p\n", &var); //打印的值就是0
printf("pVar = %p\n", pVar); //打印的地址就是0x0000000000000000
pVar = &var;//把整型变量var的地址赋值给pVar
*pVar = 18; //经pVar变量所存储的地址所指向的变量赋值18
printf("var = %d\n", var);
printf("pVar = %p\n", pVar);
return 0;
}
输出结果:
var = 0
var的地址是 000000963A2FFA14
pVar = 0000000000000000
var = 18
pVar = 000000963A2FFA14
二、指针与地址的关系
C语言指针的规范:
1、Type*类型的指针只能保存Type类型变量的地址:
如,int*类型的指针只能保存int类型变量的地址;
float*类型的指针只能保存float类型变量的地址;
2、不同类型的指针不能相互赋值:
如。int*类型的指针不能与float*类型的指针相互赋值;
3、不能将普通数值当做地址给指针赋值
指针保存的地址,必须是系统能分配的内存地址,不能瞎写个数据。
程序实例4:错误代码实例
#include <stdio.h>
int main()
{
int i = 10;
float f = 10;
int* pi = &f; //不同类型指针赋值,会有警告
float* pf = &f; //正常
printf("pi = %p, pf = %p\n", pi, pf); //能打印出同样的地址
printf("*pi = %d, *pf = %f\n", *pi, *pf); //输出不同的值
return 0;
}
前面我们知道,通过调用函数不能直接交换两个变量的值,用指针就可以轻松实现了。
想要实现函数交换变量的值,就必须要实现在函数的内部修改函数外部的变量。
程序示例5:通过指针实现在函数内部改变函数外部的变量
#include <stdio.h>
void change(int* p)
{
*p = 18; //把地址p中的变量赋值为18,实现函数内修改外部变量的值
}
int main()
{
int var = 0;
change(&var);
printf("var = %d\n", var); //打印出18
return 0;
}
程序实例6:交换两个变量的值
void swap(int* a, int* b)
{
int t = 0;
t = *a;
*a = *b;
*b = t;
}
int main()
{
int x = 10;
int y = 8;
printf("交换前:x = %d, y = %d\n", x, y);
swap(&x,&y);
printf("交换后:x = %d, y = %d\n", x, y);
return 0;
}
三、指针与数组的关系
数组的本质是一片连续的内存,那数组的地址是什么?如何获取?
数组的地址,就是那一片连续地址的起始地址,用取地址符&来获取;
数组名可看作一个指针,代表数组中第0个元素的地址;
当指针指向数组元素时,可进行指针运算
程序实例7:获取数组的地址和数组中元素的地址
#include <stdio.h>
int main()
{
int arr[] = {1, 2, 3, 4,5};
printf("数组名代表arr的地址是%p\n", arr); //数组名代表数组的地址
printf("数组arr的地址是%p\n", &arr); //打印数组的地址
printf("数组arr首个元素的地址%p\n", &arr[0]); //打印数组中首个元素的地址
return 0;
}
输出结果:
数组名代表arr的地址是000000DFC37CF8C8
数组arr的地址是000000DFC37CF8C8
数组arr首个元素的地址000000DFC37CF8C8
&arr与arr在数值上是相同的,但是表示的意义不一样
&arr表示数组arr的地址,类型是int(*)[5];
arr代表数组arr中0号元素的地址,类型是int*
程序实例8:指针运算
#include <stdio.h>
int main()
{
int arr[] = {1, 2, 3, 4, 5};
int* p = arr; //p指向数组arr
printf("*p = %d\n", *p); //打印1
printf("arr[0] = %d\n", arr[0]); //打印1
p = p + 1; //指针偏移
printf("*p = %d\n", *p); //打印2
printf("arr[1] = %d\n", arr[1]); //打印2
*p = 100;
printf("*p = %d\n", *p); //打印100
printf("arr[1] = %d\n", arr[1]); //打印100
return 0;
}
指针与数组的等效用法
a[i] == *(a + i)
四、指针与函数的关系
五、指针与堆空间的关系
六、指针经典问题分析