嵌入式C语言基础(二)

指针是分类型的:
指针++根据类型不同,偏移值也不同。指针和数组,如何让指针指向数组?
①用数组名 :int array[10],int* p,p=array
②用第一个元素的地址:int array[10],int* p,p=&array[0]
注意:
不要让指针的偏移位置超出了数组,否则将看到乱码。

#include <stdio.h>
#include <stdlib.h>
void arrayAdrePrint(int datas[],int size)
{int i;for(i=0;i<size;i++){printf("第%d个元素的地址是:%p\n",i+1,&datas[i]);    }
}
void arrayPrint1(int datas[],int size)
{int i;for(i=0;i<size;i++){printf("arrayPrint1打印第%d个元素的地址是:%p\n",i+1,datas);datas++;    }
}
void arrayPrint2(int* p,int size)
{int i;for(i=0;i<size;i++){printf("arrayPrint2打印第%d个元素的地址是:%p\n",i+1,p);p++;    }
}
int main()
{int *p1;//整型类型只能存放整数的地址char *p2;//字符型只能存放字符数据的地址int a=10;char c='A';p1=&a;p2=&c;//指针++printf("a的地址打印:%p\n",p1);printf("a的地址++打印:%p\n",++p1);printf("c的地址打印:%p\n",p2);printf("c的地址++打印:%p\n",++p2);//结果是:a的地址++后,向后偏移了四个字节,c的地址++后向后偏移了1个字节//原因是:整型占4个字节,字符型占一个字节int len;int array[3]={1,2,3};len=sizeof(array)/sizeof(array[0]);arrayAdrePrint(array,len);//打印数组的地址,由程序结果可知数组元素的地址是连续的。int* parray;parray=array;arrayPrint1(array,len);arrayPrint2(parray,len);//通过指针++的方式访问数组system("pause");return 0;
}

指针数组函数的综合:

#include <stdio.h>
#include <stdlib.h>
void initScores(int *datas,int size)
{int i;for(i=0;i<size;i++){printf("请输入第%d个学生的分数\n",i+1);scanf("%d",datas);if(*datas<0 || *datas>100){printf("输入错误,强制退出\n");system("pause");exit(-1);}datas++;}
}
void arrayPrint(int *datas,int size)
{int i;for(i=0;i<size;i++){printf("第%d个学生的成绩是%d\n",i+1,*datas++);}
}
int findMax(int *datas,int size)
{int i;int max=0;for(i=0;i<size;i++){if(*datas>max){max=*datas;        }datas++;}return max;
}
int findMin(int *datas,int size)
{int i;int min=*datas;for(i=0;i<size;i++){if(*datas<=min){min=*datas;        }*datas++;}return min;
}
float findAverge(int *datas,int size)
{int i;int sum=0;float ave;for(i=0;i<size;i++){sum=*datas+sum;datas++;}ave=(float)sum/size;return ave;
}
void printRet(int data1,int data2,float data3)
{printf("最高分是:%d,最低分是:%d,平均分是:%f\n",data1,data2,data3);
}
int main()
{int scores[3];int len;int max;int min;float averge;len=sizeof(scores)/sizeof(scores[0]);//1、初始化数组initScores(scores,len);//函数传参的过程实际上就是赋值的过程//1.1打印数组arrayPrint(scores,len);//2、找到最高分max=findMax(scores,len);//3、找到最低分min=findMin(scores,len);//4、算出平均分averge=findAverge(scores,len);//5、输出结果printRet(max,min,averge);system("pause");return 0;
}

为什么要用指针:

①可以将数值存放在固定的地址中

#include <stdio.h>
#include <stdlib.h>
//0060FEF8这个是我的电脑a的地址
//能不能让a也就是10,强制的保存在我要的地址 比如将a放在0060FEF4地址是十六进制的数
int main()
{int a;//a的地址肯定是系统随机分配a=10;int *p;p=&a;printf("a's address is %p\n",p);//(int *)0060FEF4表示将这个整型数强制转化为一个整型数的地址int *p2=(int*)0x0060FEF4;//这种写法在以后arm架构,裸机编程·,arm驱动可能用到*p2=10;printf("在内存的%p位置,存放值是%d\n",p2,*p2);volatile int *p2=(volatile int*)0x0060FEF4;//volatile是类型修饰符/*寄存器比内存更靠近CPU,所以数据在内存中访问的更快,当前我们写的都是单线程的程序从main函数进来,我们定义一个变量是存放在内存中的,这时候我们对变量的访问是将变量拷贝一份到寄存器,下次cpu访问的时候直接从寄存器访问,这样效率更高,若改变变量的值,它会自动的将变量的值更新到寄存器。若是多线程编程,同样是变量存放在内存中,两个线程都可以对变量进行修改(除非对内存做了一些限定),若线程二改变变量的值,他不会通知线程一将变量的值更新到寄存器,这样CPU用的只就不是最新的值,程序的结果就可能会有问题。而volatile的作用是让CPU直接访问内存,不通过寄存器,这样提高了正确性,但是降低了效率*/system("pause");return 0;
}

volatile和编译器的优化有关:
编译器的优化:
在本次线程内,当读取一个变量时,为了提高读取速度,编译器进行优化时会把变量读取到一个寄存器中,以后再读取变量值时,就直接从寄存器中读出;当变量在本线程里改变时,会同时把变量的新值copy到该寄存器中,以保持一致。
当变量因别的线程值发生改变,上面寄存器的值不会相应的改变,从而造成应用程序读取的值和实际的变量值不一致。
当寄存器因别的线程改变了值,原变量的值也不会改变,也会造成应用程序读取的值和实际的变量值不一样。

volatile详解:博客

②可以通过地址改变变量的值

在这里插入代码片#include <stdio.h>
#include <stdlib.h>
/*void swap(int a,int b)
{int tmp;tmp=a;a=b;b=tmp;
}这个函数不能交换a和b的值*/
//指针变量本身也有地址,指针变量的值是其他变量的地址
void swap(int *a,int *b)
{int tmp;tmp=*a;*a=*b;*b=tmp;
}
int main()
{int a=5;int b=10;swap(&a,&b);printf("a=%d,b=%d\n",a,b);system("pause");return 0;
}

指针数组(好多个指针放在一个数组里):

#include <stdio.h>
#include <stdlib.h>
void int1Print(int** array)//因为实参本身就是一级指针//所以要想让数组的首地址作为参数就要用二级指针
{int i;for(i=0;i<3;i++){printf("指针偏移方法:第%d个元素是;%d\n",i+1,**array);//array本身是一级指针//加一个*代表是取指针数组中所存放的地址//加二个*代表取第一个元素地址所存放的值*array++;//表示将指针后移指向数组中下一个地址}
}
void int2Print(int** array)//array是一级指针数组的首地址,它的地址是二级指针
{int i;for(i=0;i<3;i++){printf("数组元素++方法:第%d个元素是;%d\n",i+1,*array[i]);}
}
void address1Print(int** array)
{int i;for(i=0;i<3;i++){printf("第%d个元素是:%p\n",i+1,*array);array++;}
}
void address2Print(int** array)
{int i;for(i=0;i<3;i++){printf("第%d个元素是:%p\n",i+1,array[i]);}
}
int main()
{int a=1;int b=2;int c=3;int i;int array[3];//多个整数组成的数组叫做整数数组int* p;//整形指针变量//定义指针数组格式:类型名* 数组名[] int* parray[3];//指针数组就是多个指针组成的数组,数组中元素是指针变量//指针变量是存放地址的变量parray[0]=&a;parray[1]=&b;parray[2]=&c;//三个普通没有任何关系的整型变量的地址存入数组int1Print(parray);//通过指针打印数组中地址所存放的数据int2Print(parray);address1Print(parray);//输出指针数组中的地址address2Print(parray);system("pause");return 0;
}

数组指针(数组的指针):

#include <stdio.h>
#include <stdlib.h>
//上节课说的是“指针数组”,是一个数组里有多个指针
//本次课说的是“数组指针”,一个指针
//不管是何种类型的指针变量,都是存放别人地址的变量
void addressPrint(int* data)
{printf("函数内输出array2++的地址:%p\n",++data);
}
int main()
{int i;int (*array1)[3];//数组指针的定义,整型数组指针!=整型指针,因为不是一个类型的指针//比如:int* p,int (*array)[],在linux环境下不能p=array//这个数组指针指向的是整个数组,若array1++,偏移的是整个数组所占的字节=3*4=12.//定义格式:数组的类型 (*数组的名字)[]int array2[3]={1,2,3};int* p;//此指针并非是数组指针//仅仅是一个普通的整形指针,刚好指向了数组的首元素地址arrayarray1=array2;p=array2;printf("元素名称打印数组2的地址是:%p\n",array2);printf("首元素地址打印数组2的地址是:%p\n",&array2[0]);printf("用数组指针赋值打印数组2的地址是:%p\n",array1);printf("用整型指针赋值打印数组2的地址是:%p\n",p);//这几种方式输出的地址是一样的0060FEDCprintf("=================区别如下================\n");//printf("array2++的结果是:%p\n",++p);// printf("array2++的结果是:%p\n",++array2);因为array2是数组名是常量,不能用来++输出地址,可以将它赋给指针变量++输出如:printf("array2++的结果是:%p\n",++p);//但是如果将数组名作为参数传入函数,则可以用++来输出整个数组的偏移值。如函数addressPrint()addressPrint(array2);//输出结果:0060FEE0  偏移值:0060FEE0-0060FEDC=4,是一个数组内元素的大小printf("array1++的结果是:%p\n",++array1);//输出结果:0060FEE8   偏移值:0060FEE8-0060FEDC=12,正好是整个数组的大小system("pause");return 0;
}

函数指针:

#include <stdio.h>
#include <stdlib.h>
//函数指针,顾名思义它是存放一个函数的地址
//如何定义?如何通过指针调用该函数?
void printfWelcome()
{printf("欢迎来到啊哈C\n");
}
int add(int a,int b)
{return a+b;
}
int main()
{int i=10;printf("变量名:i=%d\n",i);//通过变量名来访问一个变量int* p=&i;printf("指针:i=%d\n",*p);//通过指针访问变量printfWelcome();//通过函数名来调用函数//如何定义函数指针void (*p2)();// 1、如何表示指针:用* 2、如何知道是函数:用()表示 3、函数指针是专用的,格式要求很强:参数类型,个数,返回值,就像数组指针一样//如何给函数指针赋值p2=printfWelcome;//这里不能写括号,函数名就像是地址,就像数组名一样就是地址//如何通过函数指针调用函数p2();//直接通过指针名字调用(*p2)();//取内容再调用,(*指针名字)()int (*padd)(int a,int b);//定义函数指针,形参可以省略padd=add;printf("结果是:%d\n",padd(1,2));printf("结果是:%d\n",(*padd)(1,2));system("pause");return 0;
}

无类型的指针:

这里引入malloc函数
不对指针进行写操作就不用malloc给他开辟空间
给新定义的一个指针赋值(指针),也不用给新定义的指针malloc开辟空间

malloc的函数原型是:
extern void *malloc(unsigned int num_bytes);
这个函数返回的是无类型的指针。
头文件:#include <malloc.h> 或 #include <alloc.h> (注意:alloc.h 与 malloc.h 的内容是完全一致的。)功能:分配长度为num_bytes字节的内存块说明:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。num_bytes表示大小是为无符号整型一、函数声明(函数原型)void *malloc(int size);
说明:malloc 向系统申请分配指定size个字节的内存空间。
返回类型是 void* 类型,void* 表示未确定类型的指针。
C,C++规定,void* 类型可以强制转换为任何其它类型的指针。二、malloc与new的不同点
从函数声明上可以看出。malloc 和 new 至少有两个不同:new 返回指定类型的指针,并且可以自动计算所需要大小。
比如:
int *p;p = new int; //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int);或:int* parr;parr = new int [100]; //返回类型为 int* 类型(整数型指针),分配大小为 sizeof(int) * 100;而 malloc 则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。int* p;p = (int *) malloc (sizeof(int));int* p; p++偏移4个字节
char* p; p++偏移1个字节
void* p; p++不知道偏移几个字节/*int返回的是整型,void并不是没有返回值,
而是他的返回值是:无类型的,void本身也是一个类型(无类型)*/

第一、malloc 函数返回的是 void * 类型,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int ) 来将强制转换。
第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:int
p = (int ) malloc (1);代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容全部被清空。malloc 也可以达到 new [] 的效果,申请出一段连续的内存,方法无非是指定你所需要内存大小。比如想分配100个int类型的空间:int p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100个整数的内存空间。另外有一点不能直接看出的区别是,malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。

总结:
malloc()函数其实就在内存中找一片指定大小的空间,然后将这个空间的首地址范围给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址,这要看malloc()函数中参数size的具体内容。我们这里malloc分配的内存空间在逻辑上连续的,而在物理上可以连续也可以不连续。对于我们程序员来说,我们关注的是逻辑上的连续,因为操作系统会帮我们安排内存分配,所以我们使用起来就可以当做是连续的。

实例演示(内含用户输入数组元素个数指针方式):

#include <stdio.h>
#include <stdlib.h>
//指针可以当做数组名来用,作为数组的首地址,严格的说不等于数组,但是可以认为它是个数组一样的使用而不产生任何问题。
//不过既然这样,那它应该算是个数组吧。所以,一般我们都用“动态数组”这种名字来称呼这种东西。
//malloc能操作的是程序中的堆空间,而普通的数组则是存放在栈空间里面的。
/* 堆空间是系统内存中的可用区域,和普遍意义上的“堆(Heap)”不同,基本上可以看作是由空闲内存组成的大链表。
嗯,操作系统怎么处理这东西不管了,反正你就可以认为堆空间是可用内存里的一片连续区域。malloc函数的作用就是从这一片内存中划出一块空间来。
你可以认为是malloc从内存中找到了一片可以安全存放数据的可用空间,这样你的数据就可以放在这片空间里面。这片空间的大小是你自己指定的。
通过malloc(字节数)这样简单的方法。为了找到这片空间,malloc函数会告诉你这片空间开头的地址,你可以把它赋值给一个变量存放起来。*/
int main()
{//int a[3];/* int* a=(int*)malloc(3*sizeof(int));//通过malloc开辟内存空间,并强制转换为int类型的指针//a指向开辟连续内存空间的首地址//a仅仅代表开辟的12字节的整型内存空间的首地址,所以可以作为数组名来为内存写值,如下:int i;for(i=0;i<3;i++){a[i]=i;//i是整型,占用三个字节,malloc开辟的是连续的空间,}for(i=0;i<3;i++){printf("数组打印:%d\n",a[i]);}*/int n,i;printf("请输入数组的个数:\n");scanf("%d",&n);printf("n=%d\n",n);int* a=(int*)malloc(n*sizeof(int));if(a==NULL){printf("开辟失败\n");}for(i=0;i<n;i++){printf("请输入第%d个学生的成绩:\n",i+1);scanf("%d",&a[i]);//注意要取地址}for(i=0;i<n;i++){printf("第%d个学生的成绩是:%d \n",i+1,*a++);}system("pause");return 0;
}

详解malloc函数的博客:malloc

什么是内存泄漏?如何避免?
内存泄漏:主要体现是程序刚跑起来很好,跑几个小时或者几天或者几周程序崩溃。

比如:

while(1){sleep(1);//每隔一秒int* p=(int*)malloc(1024);//开辟1M空间,malloc申请的空间,程序不会主动释放//linux中的话,程序结束后,系统会回收空间,程序不结束,不释放如何避免:1、注意,循环中有没有一直申请开辟内存2、及时合理的释放  free(p) p=NULL;若不写p=NULL可能会使p变成野指针野指针:是指在定义指针的时候没有将指针初始化,比如:int* p,p就是野指针,将p=NULL后就不是了}

指针收官之面试题小测:

#include <stdio.h>
#include <stdlib.h>
int main()
{int a;//定义整形变量int* p;//定义p为指向整型数据的指针变量p=NULL;int a[5];//定义数组a,它有5个元素int* p[4];//定义指针数组p,它由4个指向整形数组的指针元素组成int (*p)[4];//p为指向包含4个元素的一维数组的指针变量void* p;//p是一个指针变量,基本类型为void(空类型),不指向具体的对象int add(int,int);//add为返回整型的函数int (*p)(int a);//p为指向数组的指针,该函数返回一个整型int** p;//p是一个指针变量,他指向整型数据的指针变量int* p();//p为一个指针的函数,该指针指向整型数据system("pause");return 0;
}
int add(int a,int b)//返回值是整型的函数
{return a+b;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/272290.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linus中帮助命令man

使用方法&#xff1a;man 可以查看想要使用命令的详细使用方法 man 命令

大牛是怎么思考设计SQL优化方案的?

作者&#xff1a;惨绿少年https://www.cnblogs.com/clsn/p/8214048.html在进行MySQL的优化之前&#xff0c;必须要了解的就是MySQL的查询过程&#xff0c;很多查询优化工作实际上就是遵循一些原则&#xff0c;让MySQL的优化器能够按照预想的合理方式运行而已。图-MySQL查询过程…

嵌入式C语言基础(三)

字符串的定义方式及输出&#xff1a; #include <stdio.h> #include <stdlib.h> void strPrint(char* data,int size) {int i;for(i0;i<size;i){printf("函数打印数组&#xff1a;%c\n",*data);}putchar(\n); } int main() {int i0;int a[3]{1,2,3};c…

web.config配置数据库连接

第一种&#xff1a;获取连接字符串 首先要定义命名空间 system.configuration 1. string connstr string constr ConfigurationManager.AppSettings["connstring"]; web.config文件:加在<appsettings>和</appsettings> 之间 <appsettings> <a…

用 Git 和 Github 提高效率的 10 个技巧!

Git 和 GitHub 都是非常强大的工具。即使你已经使用他们很长时间&#xff0c;你也很有可能不知道每个细节。我整理了 Git 和 GitHub 可能提高日常效率的10个常用技巧。GitHub1、快捷键: t 和 w在你的源码浏览页面&#xff0c;按 t 可以快速进入模糊文件名搜索模式&#xff1a;在…

docker显示镜像的摘要信息

显示镜像的摘要信息 docker images --digests

嵌入式C语言基础(四)

为什么要用结构体&#xff1f; 在实际问题中&#xff0c;一组数据往往具有不同的数据类型。例如&#xff0c;在学生登记表中&#xff0c;姓名应为字符型;学号可为整型或字符型&#xff1b;年龄应为整型&#xff1b;性别应为字符型&#xff1b;成绩可为整型或实型。显然不能用一…

命令行分析java线程CPU占用

1.使用top命令找出占用cpu最高的JAVA进程pid号 2. 找出占用cpu最高的线程&#xff1a; top -Hp pid -d 1 -n 1 3. 打印占CPU最高JAVA进程pid的堆栈信息 jstack pid > /tmp/stacktrace.log 4. 把占CPU最高线程号码换算成16进制到stacktrace.log中寻找相应线程16进制值找到…

docker搜索镜像

docker search 要下载的 OFFICIAL 为ok的表示是官方镜像

C#操作HttpClient工具类库

using System; using System.Collections.Generic; using System.Net.Http; using System.Windows.Forms; using System.Configuration; using System.IO; using Newtonsoft.Json; namespace Dcflow { public class HttpHelper { //获取Configuration对象 public static string…

docker从仓库找镜像

docker search -s 数量 要下载的 数量表示仓库start数

bzoj 1911: [Apio2010]特别行动队 2011-12-26

1911: [Apio2010]特别行动队 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 892 Solved: 359[Submit][Status][Discuss] DescriptionInputOutputSample Input4 -1 10 -20 2 2 3 4 Sample Output9HINT Source _________________________________________ 很简单的动规方程&a…

嵌入式C语言基础链表

什么是链表&#xff1f; 链表其实就是一种数据结构&#xff0c;所谓的数据结构就是数据存放的思想。 数组、链表优缺点&#xff1a; 增加一个元素或者删除一个元素都很难&#xff0c;因为地址是连续的&#xff0c;删除一个元素可能会挪动多个元素&#xff0c;不灵活。但是对于链…

docker pull 从仓库拉取镜像

docker pull 要拉取的镜像名 等价于 docker pull 要拉取的镜像名:lastest 拉取固定的镜像&#xff1a;docker pull 要拉取的镜像名:版本号 省略lastest表设计就是拉取的最新的

理解js中的原型链,prototype与__proto__的关系

说到prototype&#xff0c;就不得不先说下new的过程。 我们先看看这样一段代码&#xff1a; 1<script type"text/javascript">2 var Person function () { };3 var p new Person();4</script>很简单的一段代码&#xff0c;我们来看看这个new究竟做了什…

C#抓取网页HTML内容

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Net; using System.Text; using System.IO; using System.Text.RegularExpressions; namespace Web { /// <summary> /// 公共方法类 /// </summary> p…

项目一感应垃圾桶(Wemos)

硬件材料&#xff1a; Wemos D1、SG90舵机、HC-SR04、杜邦线若干、蜂鸣器3.3V&#xff08;可有可无&#xff09; 软件材料&#xff1a; arduino IDE编译器、USB转串口驱动 Wemos D1&#xff1a; 特性&#xff1a; 基于ESP-8266EX及arduino做的一个融合arduino兼容&#xff0…

docker删除本地所有镜像

docker rmi -f ${docker images -qa}

PAT1069. The Black Hole of Numbers

//这是到水题&#xff0c;之前因为四位数的原因一直不能A&#xff0c;看了别人的程序&#xff0c;才明白&#xff0c;不够四位的时候没考虑到&#xff0c;坑啊。。。。。脸打肿 #include<cstdio>#include<algorithm>using namespace std;int main(){ //freopen(&qu…