指针是一种用于存储和处理内存地址的变量类型。在计算机编程中,指针可以存储其他变量的内存地址,以便在程序中间接地访问和操作这些变量。通过指针,程序可以动态地分配和释放内存,实现数据结构的灵活应用,以及在函数之间传递参数和引用。指针在C、C++、C#、Java等编程语言中都有相应的概念和用法。指针的正确使用对于高效的内存管理和数据处理非常重要,但也容易引起一些问题,例如内存泄漏和悬挂指针等。因此,使用指针需要小心谨慎
计算机是以字节为单位,每一个字节对应一个存储地址,数据的存储方式有大端储存和小端存储
计算机的存储机制包括主存储器(RAM)和辅助存储器(硬盘、固态硬盘、光盘等)。主存储器是计算机用于存储程序和数据的主要地方,它是易失性存储器,意味着在断电后数据会丢失。主存储器以字节为单位存储数据,并通过唯一的地址来定位每个存储单元。辅助存储器通常用于长期存储数据,它相对主存储器拥有更大的容量,是非易失性存储器,数据在断电后不会丢失。计算机通过内存管理单元(MMU)来管理主存储器和辅助存储器之间的数据交换,以及内存的分配和释放。
计算机中不同数据类型所占用的字节数
在C语言中,不同的数据类型所占用的字节数是有标准规定的,但也受到编译器和操作系统的影响。以下是一般情况下不同数据类型在32位系统中所占用的字节数:
- char: 1字节
- int: 4字节
- short: 2字节
- long: 4字节
- long long: 8字节
- float: 4字节
- double: 8字节
- long double: 12字节(最常见)或8字节
需要注意的是,这只是一般情况下占用字节数,实际情况会因编译器和操作系统的不同而有所变化。另外,随着计算机技术的发展,也可能出现一些特殊情况下的数据类型,其占用字节数可能有所不同。
指针变量的定义
int a ; // 定义的是一个int类型的变量
int *p // 定义的是指向int类型数据的指针变量
指针的存储示意图
指针中数据的++和数据的--都是以指针指向的数据类型的宽度为依据的,但是如果把指针移动到没有定义的数据的位置会出现数组下标越界导致程序报错。
malloc的用法主要是在堆内存中开辟一块内存空间(这种开辟方式是动态的)
在C语言中,
malloc()
函数用于动态分配内存空间。它的用法如下:c
void *malloc(size_t size);
其中,
size
表示要分配的内存空间的字节数,malloc()
函数返回一个指向该内存空间起始地址的指针。如果分配成功,返回的指针指向一块连续的、大小为size
字节的内存空间;如果分配失败,则返回NULL
。使用
malloc()
函数可以在程序运行时动态地分配内存空间,这在需要根据程序运行时的实际情况来动态分配内存空间的情况下非常有用。例如,当需要存储不确定数量的数据时,可以使用malloc()
函数来分配足够的内存空间。使用
malloc()
函数分配内存后,在不再需要这块内存空间时,需要使用free()
函数来释放该内存空间,以防止内存泄漏。c
free(pointer);
其中,
pointer
是一个指向使用malloc()
分配的内存空间的指针。通过调用free()
函数,可以将该内存空间释放,使其可供其他部分使用。
具体的代码块如下所示
#include
#include
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;
}
使用指针时需要注意!
指针和变量之间的赋值(尽量保证赋值的双方处在同一个级别)
以上就是指针的基本原理除此之外还有一级指针,二级指针,数组指针等
c语言传值调用展示及原理的阐述
在C语言中,函数参数的传递方式有两种:传值调用(call by value)和传址调用(call by reference)。在传值调用中,函数的参数值被复制到函数的形式参数中,函数内部对形式参数的修改不会影响实际参数的值。换句话说,函数内部对形式参数的修改不会影响函数外部实际参数的值。
下面是传值调用的一些特点和解释:
- 实际参数的值被复制到形式参数中,函数内部对形式参数的修改不会影响实际参数的值。
- 传值调用适用于基本数据类型,如int、float、char等。
- 传值调用的优点是简单、直观,但当需要修改实际参数的值时,传值调用无法实现。
示例:
c
#include
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
#include
// 寻找最大值的函数,在子函数中添加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
#include
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
#include
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;
}
使用文件操作的方式理解指针的作用
这个是单片机中的作用:使用单片机演示这个作用(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
#include
#include
//引脚配置:
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;i0;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
#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
#include
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
#include
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的数据外还可以使用指针操作其他类型的数据...