资深流水灯工程师

资深流水灯工程师

指针是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)

四、指针与函数的关系

五、指针与堆空间的关系

六、指针经典问题分析

09-22 04:33