C语言指针详解

发布时间:2024年01月23日

?指针

1:c语言中指针的应用场景

?2:指针简介

指针是一种用于存储和处理内存地址的变量类型。在计算机编程中,指针可以存储其他变量的内存地址,以便在程序中间接地访问和操作这些变量。通过指针,程序可以动态地分配和释放内存,实现数据结构的灵活应用,以及在函数之间传递参数和引用。指针在C、C++、C#、Java等编程语言中都有相应的概念和用法。指针的正确使用对于高效的内存管理和数据处理非常重要,但也容易引起一些问题,例如内存泄漏和悬挂指针等。因此,使用指针需要小心谨慎

3:计算机的存储机制

计算机是以字节为单位,每一个字节对应一个存储地址,数据的存储方式有大端储存和小端存储

?计算机的存储机制包括主存储器(RAM)和辅助存储器(硬盘、固态硬盘、光盘等)。主存储器是计算机用于存储程序和数据的主要地方,它是易失性存储器,意味着在断电后数据会丢失。主存储器以字节为单位存储数据,并通过唯一的地址来定位每个存储单元。辅助存储器通常用于长期存储数据,它相对主存储器拥有更大的容量,是非易失性存储器,数据在断电后不会丢失。计算机通过内存管理单元(MMU)来管理主存储器和辅助存储器之间的数据交换,以及内存的分配和释放。

计算机中不同数据类型所占用的字节数

在C语言中,不同的数据类型所占用的字节数是有标准规定的,但也受到编译器和操作系统的影响。以下是一般情况下不同数据类型在32位系统中所占用的字节数:

  • char: 1字节
  • int: 4字节
  • short: 2字节
  • long: 4字节
  • long long: 8字节
  • float: 4字节
  • double: 8字节
  • long double: 12字节(最常见)或8字节

需要注意的是,这只是一般情况下占用字节数,实际情况会因编译器和操作系统的不同而有所变化。另外,随着计算机技术的发展,也可能出现一些特殊情况下的数据类型,其占用字节数可能有所不同。

4:指针的定义

指针变量的定义

int a ; // 定义的是一个int类型的变量

int *p // 定义的是指向int类型数据的指针变量

5:指针的存储方式

指针的存储示意图

6:指针的操作

指针中数据的++和数据的--都是以指针指向的数据类型的宽度为依据的,但是如果把指针移动到没有定义的数据的位置会出现数组下标越界导致程序报错。

7:指针和数组的关系

?malloc的用法主要是在堆内存中开辟一块内存空间(这种开辟方式是动态的)

在C语言中,malloc()函数用于动态分配内存空间。它的用法如下:

c

void *malloc(size_t size);

其中,size表示要分配的内存空间的字节数,malloc()函数返回一个指向该内存空间起始地址的指针。如果分配成功,返回的指针指向一块连续的、大小为size字节的内存空间;如果分配失败,则返回NULL

使用malloc()函数可以在程序运行时动态地分配内存空间,这在需要根据程序运行时的实际情况来动态分配内存空间的情况下非常有用。例如,当需要存储不确定数量的数据时,可以使用malloc()函数来分配足够的内存空间。

使用malloc()函数分配内存后,在不再需要这块内存空间时,需要使用free()函数来释放该内存空间,以防止内存泄漏。

c

free(pointer);

其中,pointer是一个指向使用malloc()分配的内存空间的指针。通过调用free()函数,可以将该内存空间释放,使其可供其他部分使用。

具体的代码块如下所示

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a[] = { 0x33,0x34,0x35,0x35,0x36,0x37,0x38};

	/*
	*  
	    1:在c语言中使用malloc函数开辟内存空间,
	    malloc开辟的内存空间是在堆内存上采用动态开辟的方式
	*   2: 这个数组实现的功能和上面的数组是一样的
		int *a = malloc(3 * 4);
		*a = 0x33;
		*(a + 1) = 0x34;
		*(a + 2) = 0x35;
	*/
	// 获取数组的长度
	int len = sizeof(a) / sizeof(a[0]);
	int i;
	for (i = 0; i < len; i++) {
		printf("a[i] = %x\n", *(a+i));
	}
	printf("\n--------\n");
	//使用指针的方式引用我们的数组
	char* p;
	// 将数组赋值给指针,数组也是一个指针也可以(使用解引用的方式引用)
	p = a;
	// 使用for循环的方式通过指针获取数组中的值*(p)也就是获取
	for (i = 1; i <= len; i++) {
		printf("*P = %x\n", *(p + i));
	}
	return 0;

}   


8:指针的应用

使用指针时需要注意

指针和变量之间的赋值(尽量保证赋值的双方处在同一个级别)

以上就是指针的基本原理除此之外还有一级指针,二级指针,数组指针等

9:指针的实战

c语言传值调用展示及原理的阐述

在C语言中,函数参数的传递方式有两种:传值调用(call by value)和传址调用(call by reference)。在传值调用中,函数的参数值被复制到函数的形式参数中,函数内部对形式参数的修改不会影响实际参数的值。换句话说,函数内部对形式参数的修改不会影响函数外部实际参数的值。

下面是传值调用的一些特点和解释:

  1. 实际参数的值被复制到形式参数中,函数内部对形式参数的修改不会影响实际参数的值。
  2. 传值调用适用于基本数据类型,如int、float、char等。
  3. 传值调用的优点是简单、直观,但当需要修改实际参数的值时,传值调用无法实现。

示例:

c

#include <stdio.h>

void changeValue(int num) {
    num = 100; // 修改形式参数的值
}

int main() {
    int x = 10;
    changeValue(x); // 传值调用
    printf("%d", x); // 输出结果为10,因为changeValue函数内部对形式参数的修改不会影响实际参数的值
    return 0;
}

在上面的示例中,即使在changeValue函数内部将num的值修改为100,但是在main函数中输出x的值仍然是10,这表明传值调用无法修改实际参数的值。

这种情况下修改子函数中变量的值是不会改变主函数中变量的值的

通过指针传递可以解决这个问题:以下是对传递指数值的理解(指针传指调用的展示)

具体的实现代码

#include<stdio.h>
#include<stdlib.h>
// 寻找最大值的函数,在子函数中添加const的时候主函数的值只能读取不能修改
int findMax(const int *array,int count) {
	int i;
	int max = array[0];
	for (i = 1; i < count; i++) {
		if (array[i] > max) {
			max = array[i];
		}
	}
	return max;
}

int main()
{
    // 定义一个数组
	int arr[] = {1,3,5,7,8,10,12,14,16};
	int Max;
	Max = findMax(arr,9);
	printf("max = %d\n", Max);
	return 0;
}   

使用指针实现我们的多返回值:在子函数中更改了这个值也就是输出参数

#include<stdio.h>
#include<stdlib.h>
void findMaxandCount(int *max,int *count,const int *array,int length) {
	int i;
	*max = array[0];
	*count = 1;
	for (i = 1; i < length; i++) {
		if (array[i] > *max) {
			*max = array[i];
			*count = 1;
		}
		else if (array[i] == *max) {
			(*count)++;
		}
	}	
}
int main()
{
	int arr[] = {1,3,5,7,8,10,12,14,16};
	int Max;
	int Count;
	findMaxandCount(&Max,&Count,arr,6);
	printf("Max=%d\n",Max);
	printf("Count=%d\n", Count);
	return 0;
}   

指针返回值的传递

#include<stdio.h>
#include<stdlib.h>
void findMaxandCount(int *max,int *count,const int *array,int length) {
	int i;
	*max = array[0];
	*count = 1;
	for (i = 1; i < length; i++) {
		if (array[i] > *max) {
			*max = array[i];
			*count = 1;
		}
		else if (array[i] == *max) {
			(*count)++;
		}
	}	
}
// 使用函数实现时钟的功能
int Time[] = { 23,59,55 }; 
// 使用指针作为返回值(句柄)也就是把手的意思然后实现间接操作的方式
int *GetTime(void) {
	// 这里还可以做一些合法性的判断等 
	return Time;
}
int main(void)
{
	// 通过指针的方式间接的进行访问
	int* p;
	p = GetTime();
	// 可以使用数组下标也可以使用指针的方式进行访问
	// 使用这种方式操作局部函数,不要吧全局变量设置为局部变量否则会报错
	printf("pt[0] = %d\n",p[0]);
	printf("pt[1] = %d\n", p[1]);
	printf("pt[2] = %d\n", p[2]);
	// 另外的一种访问方式
	printf("\n-----------------\n");
	printf("*pt[0] = %d\n", *p);
	printf("*pt[1] = %d\n", *(p+1));
	printf("*pt[2] = %d\n", *(p+2));
	return 0;
}   

10:指针文件操作的实现

使用文件操作的方式理解指针的作用

11:使用指针实现物理地址的访问(使用到51单片机以及oled组件)

这个是单片机中的作用:使用单片机演示这个作用(STC52在ARM上

单片机中的函数代码

LCD1602头文件

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

LCD1602.C文件

//引脚配置:
sbit LCD_RS=P3^5;
sbit LCD_RW=P3^6;
sbit LCD_EN=P3^4;
#define LCD_DataPort P0

不同单片机的引脚不同需要根据自己单片机的引脚进行切换

#include <REGX52.H>
#include <stdlib.h>
#include <stdio.h>

//引脚配置:
sbit LCD_RS=P3^5;
sbit LCD_RW=P3^6;
sbit LCD_EN=P3^4;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

?LCE1602主函数文件

#include <REGX52.H>
#include "LCD1602.h"

int main(){
	 // 定义一个无符号的指向char类型数据的指针

	 int i  = 0;
	 unsigned  char  *p;

   LCD_Init();
   LCD_ShowString(1,1,"HelloWord"); 
	 // 使用指针读取当前单片机的ID号
   p = (unsigned char  *) 0xF1;
	 // 使用指针完成对单片机ID号的访问
		/*
				 for(i = 0; i < 7; i++ ){
						LCD_ShowNum(2,i+2,*(p+i),2);
				}
		*/
	 LCD_ShowNum(2,1,*p,2);
     LCD_ShowNum(2,3,*(p+1),2);
	 LCD_ShowNum(2,5,*(p+2),2);
	 LCD_ShowNum(2,7,*(p+3),2);
	 LCD_ShowNum(2,9,*(p+4),2);
	 LCD_ShowNum(2,11,*(p+5),2);
	 LCD_ShowNum(2,13,*(p+6),2);
	 while(1){
	 

	 }

}

使用指针发送和接收数据

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void findMaxandCount(int *max,int *count,const int *array,int length) {
	int i;
	*max = array[0];
	*count = 1;
	for (i = 1; i < length; i++) {
		if (array[i] > *max) {
			*max = array[i];
			*count = 1;
		}
		else if (array[i] == *max) {
			(*count)++;
		}
	}	
}

unsigned char AirData[20];
// 发生数据的函数
void SendData(const unsigned char* data, unsigned char count) {
	unsigned char i;
	for (i = 0; i < count; i++) {
		AirData[i] = data[i];
	}
}
// 接收数据的函数
void ReceiveData(unsigned char * data,unsigned char count) {
	unsigned char i;
	for (i = 0; i < count; i++) {
		//使用这个函数将我们发送的数据读取出来
		data[i] = AirData[i];
	}
}

int main(void)
{
	unsigned char i;
	// 使用指针发送复杂的数据
	unsigned char dataSend[] = { 0x12,0x34,0x56,0x78 };

	// 这个就是一个地址,使用指针接收地址是可以使用的,他们两个之间的级别是一个等级的
	SendData(dataSend,4);
	printf("\nAirData=");
	for (i = 0; i < 20; i++) {
		printf("%x ", AirData[i]);
	}
	
	// 将数据发送过去的同时吧数据读取出来
	unsigned char DataReceive[4];
	ReceiveData(DataReceive, 4);
	
	printf("\nDataReceive=");
	for (i = 0; i < 4; i++) {
		printf("%x ", DataReceive[i]);
	}
	return 0;

}   

将复杂的数据转换为字节方便通讯和存储:(存储器都是以字节的方式进行存储的)

以发送和接收float类型的数据为例子

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void findMaxandCount(int *max,int *count,const int *array,int length) {
	int i;
	*max = array[0];
	*count = 1;
	for (i = 1; i < length; i++) {
		if (array[i] > *max) {
			*max = array[i];
			*count = 1;
		}
		else if (array[i] == *max) {
			(*count)++;
		}
	}	
}

unsigned char AirData[20];
// 发生数据的函数
void SendData(const unsigned char* data, unsigned char count) {
	unsigned char i;
	for (i = 0; i < count; i++) {
		AirData[i] = data[i];
	}
}
// 接收数据的函数
void ReceiveData(unsigned char * data,unsigned char count) {
	unsigned char i;
	for (i = 0; i < count; i++) {
		//使用这个函数将我们发送的数据读取出来
		data[i] = AirData[i];
	}
}

int main(void)
{
	unsigned char i;
	// 使用指针发送复杂的数据
	unsigned char dataSend[] = { 0x12,0x34,0x56,0x78 };
	float num = 12.345;
	unsigned char* p;
	p = (unsigned char*) & num;
	// 把p当做一个数组将p的值发送过去
	SendData(p,4);

	printf("\nAirData=");
	for (i = 0; i < 20; i++) {
		printf("%x ", AirData[i]);
	}
	
	// 将数据发送过去的同时吧数据读取出来
	unsigned char DataReceive[4];
	float* fp;
	ReceiveData(DataReceive, 4);
	fp = (float*)DataReceive;


	printf("\nnum = %f",*fp);
	for (i = 0; i < 4; i++) {
		printf("%x ", DataReceive[i]);
	}
	return 0;

}   

以上代码的函数执行原理

除float的数据外还可以使用指针操作其他类型的数据...

文章来源:https://blog.csdn.net/qq_45973003/article/details/135774356
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。