C语言指针难吗???我用全网最全的指针知识告诉你。它不难!!!

这篇博客详细介绍了C语言中的指针概念,包括指针的定义、指针变量、指针与数组的关系、函数指针、指针数组和多级指针。通过实例和练习帮助读者理解和掌握指针的使用,适合C语言初学者。
摘要由CSDN通过智能技术生成

C语言指针难吗???我用全网最全的指针知识告诉你。它不难!!!

前言

  本篇博文是本人在学习嵌入式软件C语言中指针章节的笔记,适用于小白入门C语言的同学。希望能帮到大家。有什么不足可以评论告诉我。
  在阅读本篇博文时,想学习的同学可以打打博文中的代码,甚至默写代码,改写代码,有助于你的学习。C语言注重多打代码,多练习,才能学得扎实。

1.认识指针

1.指针 == 地址

1.情景引入

  假如你的女朋友和你约定在***酒店,你女朋友悄悄去了酒店开好了房间。你可以通过两种方式找到:
  1.通过酒店的房间名称:“紫萱主题酒店森林主题房”——类比为:变量名
  2.通过地址:“xx省xx市xx路xx号”——类比为:变量的地址

2.定义一个整型变量——int a = 10 ;

  变量的四要素:类型、变量名、内存地址、值
  变量的访问:1.通过变量名访问。2.通过地址也能访问('&'取地址运算符 '*'将地址内的值读出运算符)
指针引入图示

2.指针变量 == 存放地址的变量

1.如何定义一个指针变量
int *a; //'*'的标识作用,只产生在指针变量定义或声明的时候
2.如何使用一个指针变量
int a = 10;
int *pa = &a;  //定义一个指针变量指向 a 
printf("*pa = %d\n",*pa);  //'*'的运算作用,取出地址中的值
3.如何访问这两种方式

  1.直接按照变量名进行访问,称为“直接访问”方式
  2.“间接访问”方式,即将变量 a 的地址存放在另一变量中,然后通过该变量来找到变量 a 的地址,从而访问a变量

#include <stdio.h>

int main()
{
   
    int a = 10;
    int *pa = &a;  //定义一个指针变量指向 a 
	printf("a = %d\n",a); //直接访问
    printf("*pa = %d\n",*pa);  //'*'的运算作用,取出地址中的值,间接访问
	return 0;
}
4.类型

  1.类型决定了指向空间的大小: int *pint; char *pchar;
  2.决定增量。

#include <stdio.h>
/*
   时间      2023年6月1日22:41:57
   程序功能:指针变量为什么要求类型
*/
int main()
{
   
	int a = 0x1234;
	int *p;
	char *c;
	p = &a;
	c = &a;
	puts("程序功能:指针变量为什么要求类型");
	puts("a通过指针变量int *p间接访问为 a = 1234");
	puts("a通过指针变量char *c间接访问为 a = 34");
	puts("是因为整型变量占4个字节,字符型变量占1个字节,一个字节内存空间无法存放16进制数");
	puts("0x1234,所以显示为34\n\n");
	puts("printf(“p+1 = %p杠n”,++p);");
	puts("printf(“c+1 = %p杠n”,++p);");
	puts("输出的结果分别是:");
	puts("p+1 = 0x7fff665f6408");
	puts("c+1 = 0x7fff665f6405");
	puts("从输出的结果可以看出:");
	puts("整型指针变量偏移4个字节,地址加4");
	puts("字符型指针变量偏移1个字节,地址加1");
	
	printf("p = %p\n",p);
	printf("c = %p\n",c);
	
	printf("a通过指针变量int *p间接访问为 a = %x\n",*p);
	printf("a通过指针变量char *c间接访问为 a = %x\n",*c);
	
	printf("p+1 = %p\n",++p);
	printf("c+1 = %p\n",++c);
	
	return 0;
}

3.完整代码示例

1.指针 == 地址
#include <stdio.h>
/*
   时间      2023年5月31日18:25:24
   程序功能:指针 == 地址
*/
int main()
{
   
	int a;
	a = 10;
	puts("程序功能:指针 == 地址");
	puts("");
	puts("printf(“a的值为 %d杠n”,*(&a)); //*号为取值运算符,他把后面跟的内存地址中的数据");
	puts("“取出来”");
	puts("");
	printf("a的值为 %d\n",a);
	printf("a的地址为 %p\n",&a);
	printf("a的值为 %d\n",*(&a)); //*号为取值运算符,他把后面跟的内存地址中的数据“取出来”
	return 0;
}
2.指针变量
#include <stdio.h>
/*
   时间      2023年5月31日21:01:07
   程序功能:指针变量
*/
int main()
{
   
	int a;
	int *p; //这里的*是一个标识符,告诉系统我是一个指针变量,是用来保存别人地址的,和下方
	a  = 10;//的运算符不同
	p = &a;
	puts("什么是整型变量,存放整型数的变量");
	puts("什么是字符变量,存放字符型数据的变量");
	puts("什么是指针变量,存放指针的变量");
	puts("什么是指针变量,存放地址的变量");
	puts("");
	puts("int *p; //这里的*是一个标识符,告诉系统我是一个指针变量,是用来保存别人地址的,");
	puts("和下方的运算符不同");
	puts("");
	puts("不能这样写 《*p = &a》会出现这样的警告:");
	puts("assignment to 'int' from 'int *' makes integer from pointer without a cast [-Wint-conversion]*p = &a;");
	puts("从'int *'对'int'赋值使指针的整数不进行强制转换[-Wint-conversion] *p = a;");
	puts("类型不相同 *p=a  *号只是一个标识符");
	puts("");
	puts("");
	puts("");
	printf("变量名访问a:%d\n",a);
	printf("a的地址为:0x%p\n",&a);
	printf("地址访问a:%d\n",*(&a));//*号为取值运算符,他把后面跟的内存地址中的数据“取出来”
	printf("指针变量的方式访问a:%d\n",*p);
	return 0;
}
3.指针变量为什么要求类型
#include <stdio.h>
/*
   时间      2023年6月1日22:41:57
   程序功能:指针变量为什么要求类型
*/
int main()
{
   
	int a = 0x1234;
	int *p;
	char *c;
	p = &a;
	c = &a;
	puts("程序功能:指针变量为什么要求类型");
	puts("a通过指针变量int *p间接访问为 a = 1234");
	puts("a通过指针变量char *c间接访问为 a = 34");
	puts("是因为整型变量占4个字节,字符型变量占1个字节,一个字节内存空间无法存放16进制数");
	puts("0x1234,所以显示为34");
	printf("p = %p\n",p);
	printf("c = %p\n",c);
	
	printf("a通过指针变量int *p间接访问为 a = %x\n",*p);
	printf("a通过指针变量char *c间接访问为 a = %x\n",*c);
	
	printf("p+1 = %p\n",++p);
	printf("c+1 = %p\n",++c);
	
	return 0;
}

2.为什么需要指针——通过两个练习来说明

练习1:封装一个函数,实现两个数交换

  1.两数交换算法解析

#include <stdio.h>

int main()
{
   
	int a = 10;
	int b = 20;
	int tmp;   //定义一个过渡变量
	printf("交换前a和b的值分别是%d %d\n",a,b);
	//下面是交换算法
	tmp = a;   
	a   = b;
	b   = tmp;
	printf("交换后a和b的值分别是%d %d\n",a,b);
	return 0;
}
/*
    交换算法:
	假设有两个杯子a和b,a存放100mL水,b存放50mL水。如果要将两个杯子里的水互换,就应该拿出c杯子用于过渡存放。
	方法步骤为:
	1.将a杯子中的水倒入c中
	2.将b中的水倒入a中
	3.将c中的水倒入b中
	这时你将会发现a和b中的水进行了互换。
	写成代码就是这样的:
	tmp = a;
	a   = b;
	b   = tmp;
*/

  2.程序代码

#include <stdio.h>
/*
   时间      2023年6月2日20:45:22
   程序功能:练习1:封装一个函数,实现两个数交换
*/
void Exchange(int *pdata1,int *pdata2)
{
   
	int temp;
	temp    = *pdata1;  //这里的*号为取值运算,他把后面跟的内存地址中的数据“取出来”
	*pdata1 = *pdata2;  //也就相当于data1,data2本身的值在做交换
	*pdata2 =  temp;
}
int main()
{
   
	int data1 = 10;
	int data2 = 20;
	puts("语句:");
	puts("temp    = *pdata1;");
	puts("*pdata1 = *pdata2;");
	puts("*pdata2 =  temp;");
	puts("这里的*号为取值运算,他把后面跟的内存地址中的数据“取出来”");
	puts("");
	puts("");
	puts("语句:");
	puts("void Exchange(int *pdata1,int *pdata2)");
	puts("这里的*是一个标识符,告诉系统我是一个指针变量,是用来保存别人地址的");
	puts("");
	puts("");
	printf("两数交换前的值为data1 = %d,data2 = %d\n",data1,data2);
	Exchange(&data1,&data2);
	printf("两数交换后的值为data1 = %d,data2 = %d\n",data1,data2);
	return 0;
}

练习2:指针指向固定的区域

  1.程序代码

#include <stdio.h>
/*
   时间      2023年6月2日21:57:39
   程序功能:练习2:指针指向固定的区域
*/

int main()
{
   
	int a = 10;
	puts("程序功能:练习2:指针指向固定的区域");
	puts("语句:");
	puts("int *p = (int *)0x000000000000000B;");
	puts("(int *):代表把后面的地址“0x000000000000000B”16进制数强制转换成指针类型才能赋给指针变量");
	puts("");
	puts("");
	puts("语句:");
	puts("volatile unsigned int *q = (volatile unsigned int *)0x000000000000000c;");
	puts("volatile的作用为防止编译器优化地址,导致地址改变");
	puts("unsigned无符号类型,存放地址的类型应是无符号类型");
	puts("");
	puts("");
	printf("a的地址是:0x%p\n",a);
	int *p = (int *)0x000000000000000B; //将16进制的地址强制转换成为指针类型才能赋给指针变量
	volatile unsigned int *q = (volatile unsigned int *)0x000000000000000c;
	printf("p = 0x%p\n",p);
	printf("q = 0x%p\n",q);
	
	return 0;
}

3.对前面的指针知识咱们可以做一次作业来巩固

作业1:输入三个数a,b,c;要求不管怎么输入,在输出的时候,a,b,c就是由大到小的顺序输出,用函数封装实现

1.作业内由大到小算法演示图

三个数由大到小排序算法图

2.程序代码
#include <stdio.h>
/*
   时间      2023年6月3日19:40:31
   程序功能:输入三个数a,b,c;要求不管怎么输入,在输出的时候,a,b,c就是由大到小的顺序输出,
   用函数封装实现
*/
void dataSort(float *data1,float *data2,float *data3)
{
   
	float temp;
	if(*data1<*data2)
	{
   
		temp   = *data1;
		*data1 = *data2;
		*data2 = temp;
	}
	if(*data2<*data3)
	{
   
		temp   = *data2;
		*data2 = *data3;
		*data3 = temp;
	}
	if(*data1<*data2)
	{
   
		temp   = *data1;
		*data1 = *data2;
		*data2 = temp;
	}
}
int main()
{
   
	float data1; 
    float data2;
	float data3;
	puts("程序功能:输入三个数a,b,c;要求不管怎么输入,在输出的时候,a,b,c就是由大到小的");
    puts("顺序输出,用函数封装实现");
	puts("");
	puts("");
	puts("");
	puts("请输入三个数,以空格间隔,按回车结束");
	scanf("%f %f %f",&data1,&data2,&data3);
	dataSort(&data1,&data2,&data3);
	printf("你排序后的结果为 %f\t%f\t%f\t\n",data1,data2,data3);
	return 0;
}

4.通过指针引用数组

1.定义一个指针变量指向数组

1.指向数组首元素的地址

  可以用一个指针变量指向一个数组元素。例如:

int arr[10] = {
   1,3,5,7,9,11,13,15,17,19};  //定义arr为包含10个整型数据的数组
int *p;                                    //定义p为指向整型变量的指针变量
p = &arr[0];                               //把arr[0]元素的地址赋给指针变量p
2.数组名就是数组首元素地址:指向数组起始位置

  在c语言中,数组名(不包括形参数组名,形参数组并不占据实际的内存单元)代表数组中首元素(即序号为0的元素)的地址。因此下面两个语句等价:

p = &arr[0];  //p的值a[0]的地址
p = arr;      //p的值是数组arr首元素(即arr[0])的地址
3.完整代码演示
#include <stdio.h>
/*
   时间  2023年6月4日18:46:48
   程序功能:定义一个指针变量指向数组
*/
int main()
{
   
	int arr[3]={
   1,2,3};
	int *p;
	int *q;
	
	puts("程序功能:定义一个指针变量指向数组");
	puts("p = &arr[0]; 指针变量p指向数组arr的首元素地址,也就是数组arr的首地址");
	puts("q = arr;数组名arr代表数组arr的首地址。并且指针变量q指向数组arr的首元");
	puts("素地址,也就是数组arr的首地址");
	puts("");
	puts("");
	
	p = &arr[0];
	printf("p = &arr[0]; p = %d\n",*p);
	q = arr;
	printf("q = arr; q = %d\n",*q);
	
	return 0;
}

2.指针增量和数组的关系

1.代码演示
#include <stdio.h>
/*
   时间      2023年6月4日21:42:39
   程序功能:指针增量和数组关系
*/
int main()
{
   
	int arr[3];  //定义一个数组
	int i;       //循环条件变量i
	int *p;      //定义一个指针变量p
	p = arr;     //指针变量指向数组首元素
	
	puts("程序功能:指针增量和数组关系");
	puts("scanf(“%d”,&*(p+i));//取指针变量地址上的值的地址来进行遍历");
	puts("其中p+i,p+1为指针变量偏移");
	puts("指针变量偏移时地址加上的值通常是类型大小(字节数)的倍数。这是因为变量在内存中占用");
	puts("的空间大小与其数据类型有关,而整型类型通常会占用4个字节,因此偏移时地址加4是为了");
	puts("移动到相邻的变量的起始地址。");
	puts("");
	puts("");
	puts("开始输入");
	for(i=0; i<3; i++)
	{
   
		printf("请输入第%d个元素的值。\n",i+1);
		scanf("%d",&*(p+i));//取指针变量地址上的值的地址来进行遍历
	}
	puts("输入结束!!!");
	puts("");
	puts("");
	puts("开始输出");
	for(i=0; i<3; i++)
	{
   
		printf("%d\t",*(p+i));
	}
    puts("\n输出结束!!!");
	return 0;
}
2.图示说明

图示说明

3.指针变量补充,记得回到数组首地址
#include <stdio.h>
/*
   时间   2023年6月5日20:37:24
   程序功能:指针变量补充,记得回到数组首地址
*/
void introduce()
{
   
	puts("程序功能:指针变量补充,记得回到数组首地址");
	puts("指针偏移还可以这样写:
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值