初识指针和指针变量 您所在的位置:网站首页 指针变量的使用 初识指针和指针变量

初识指针和指针变量

#初识指针和指针变量| 来源: 网络整理| 查看: 265

说到指针,估计还是有很多小伙伴都还是云里雾里的,有点“知其然,而不知其所以然”。但是,不得不说,学了指针,C语言才能算是入门了。指针是C语言的「精华」,可以说,对对指针的掌握程度,「直接决定」了你C语言的编程能力。

在讲指针之前,我们先来了解下变量在「内存」中是如何存放的。

在程序中定义一个变量,那么在程序编译的过程中,系统会根据你定义变量的类型来分配「相应尺寸」的内存空间。那么如果要使用这个变量,只需要用变量名去访问即可。

通过变量名来访问变量,是一种「相对安全」的方式。因为只有你定义了它,你才能够访问相应的变量。这就是对内存的基本认知。但是,如果光知道这一点的话,其实你还是不知道内存是如何存放变量的,因为底层是如何工作的,你依旧不清楚。

那么如果要继续深究的话,你就需要把变量在内存中真正的样子是什么搞清楚。内存的最小索引单元是1字节,那么你其实可以把内存比作一个超级大的「字符型数组」。在上一节我们讲过,数组是有下标的,我们是通过数组名和下标来访问数组中的元素。那么内存也是一样,只不过我们给它起了个新名字:地址。每个地址可以存放「1字节」的数据,所以如果我们需要定义一个整型变量,就需要占据4个内存单元。

那么,看到这里你可能就明白了:其实在程序运行的过程中,完全不需要变量名的参与。变量名只是方便我们进行代码的编写和阅读,只有程序员和编译器知道这个东西的存在。而编译器还知道具体的变量名对应的「内存地址」,这个是我们不知道的,因此编译器就像一个桥梁。当读取某一个变量的时候,编译器就会找到变量名所对应的地址,读取对应的值。

初识指针和指针变量

那么我们现在就来切入正题,指针是个什么东西呢?

所谓指针,就是内存地址(下文简称地址)。C语言中设立了专门的「指针变量」来存储指针,和「普通变量」不一样的是,指针变量存储的是「地址」。

定义指针

指针变量也有类型,实际上取决于地址指向的值的类型。那么如何定义指针变量呢:

很简单:类型名* 指针变量名

char*pa;//定义一个字符变量的指针,名称为pa int*pb;//定义一个整型变量的指针,名称为pb float*pc;//定义一个浮点型变量的指针,名称为pc

注意,指针变量一定要和指向的变量的类型一样,不然类型不同可能在内存中所占的位置不同,如果定义错了就可能导致出错。

取地址运算符和取值运算符

获取某个变量的地址,使用取地址运算符&,如:

char*pa=&a; int*pb=&f;

如果反过来,你要访问指针变量指向的数据,那么你就要使用取值运算符*,如:

printf("%c,%d ",*pa,*pb);

这里你可能发现,定义指针的时候也使用了*,这里属于符号的「重用」,也就是说这种符号在不同的地方就有不同的用意:在定义的时候表示「定义一个指针变量」,在其他的时候则用来「获取指针变量指向的变量的值」。

直接通过变量名来访问变量的值称之为直接访问,通过指针这样的形式访问称之为间接访问,因此取值运算符有时候也成为「间接运算符」。

比如:

//Example01 //代码来源于网络,非个人原创 #include intmain(void) { chara='f'; intf=123; char*pa=&a; int*pf=&f; printf("a=%c ",*pa); printf("f=%d ",*pf); *pa='c'; *pf+=1; printf("now,a=%c ",*pa); printf("now,f=%d ",*pf); printf("sizeofpa=%d ",sizeof(pa)); printf("sizeofpf=%d ",sizeof(pf)); printf("theaddrofais:%p ",pa); printf("theaddroffis:%p ",pf); return0; }

程序实现如下:

//Consequence 01 a = f f = 123 now, a = c now, f = 124 sizeof pa = 4 sizeof pf = 4 the addr of a is: 00EFF97F the addr of f is: 00EFF970

避免访问未初始化的指针

voidf() { int*a; *a=10; }

像这样的代码是十分危险的。因为指针a到底指向哪里,我们不知道。就和访问未初始化的普通变量一样,会返回一个「随机值」。但是如果是在指针里面,那么就有可能覆盖到「其他的内存区域」,甚至可能是系统正在使用的「关键区域」,十分危险。不过这种情况,系统一般会驳回程序的运行,此时程序会被「中止」并「报错」。要是万一中奖的话,覆盖到一个合法的地址,那么接下来的赋值就会导致一些有用的数据被「莫名其妙地修改」,这样的bug是十分不好排查的,因此使用指针的时候一定要注意初始化。

指针和数组

有些读者可能会有些奇怪,指针和数组又有什么关系?这俩货明明八竿子打不着井水不犯河水。别着急,接着往下看,你的观点有可能会改变。

数组的地址

我们刚刚说了,指针实际上就是变量在「内存中的地址」,那么如果有细心的小伙伴就可能会想到,像数组这样的一大摞变量的集合,它的地址是啥呢?

我们知道,从标准输入流中读取一个值到变量中,用的是scanf函数,一般貌似在后面都要加上&,这个其实就是我们刚刚说的「取地址运算符」。如果你存储的位置是指针变量的话,那就不需要。

//Example02 intmain(void) { inta; int*p=&a; printf("请输入一个整数:"); scanf("%d",&a);//此处需要& printf("a=%d ",a); printf("请再输入一个整数:"); scanf("%d",p);//此处不需要& printf("a=%d ",a); return0; }

程序运行如下:

//Consequence 02 请输入一个整数:1 a = 1 请再输入一个整数:2 a = 2

在普通变量读取的时候,程序需要知道这个变量在内存中的地址,因此需要&来取地址完成这个任务。而对于指针变量来说,本身就是「另外一个」普通变量的「地址信息」,因此直接给出指针的值就可以了。

试想一下,我们在使用scanf函数的时候,是不是也有不需要使用&的时候?就是在读取「字符串」的时候:

//Example03 #include intmain(void) { charurl[100]; url[99]=''; printf("请输入TechZone的域名:"); scanf("%s",url);//此处也不用& printf("你输入的域名是:%s ",url); return0; }

程序执行如下:

//Consequence 03 请输入TechZone的域名:www.techzone.ltd 你输入的域名是:www.techzone.ltd

因此很好推理:数组名其实就是一个「地址信息」,实际上就是数组「第一个元素的地址」。咱们试试把第一个元素的地址和数组的地址做个对比就知道了:

//Example03V2 #include intmain(void) { charurl[100]; printf("请输入TechZone的域名:"); url[99]=''; scanf("%s",url); printf("你输入的域名是:%s ",url); printf("url的地址为:%p ",url); printf("url[0]的地址为:%p ",&url[0]); if(url==&url[0]) { printf("两者一致!"); } else { printf("两者不一致!"); } return0; }

程序运行结果为:

//Comsequense 03 V2 请输入TechZone的域名:www.techzone.ltd 你输入的域名是:www.techzone.ltd url的地址为:0063F804 url[0]的地址为:0063F804 两者一致!

这么看,应该是实锤了。那么数组后面的元素也就是依次往后放置,有兴趣的也可以自己写代码尝试把它们输出看看。

指向数组的指针

刚刚我们验证了数组的地址就是数组第一个元素的地址。那么指向数组的指针自然也就有两种定义的方法:

... char*p; //方法1 p=a; //方法2 p=&a[0];

指针的运算

当指针指向数组元素的时候,可以对指针变量进行「加减」运算,+n表示指向p指针所指向的元素的「下n个元素」,-n表示指向p指针所指向的元素的「上n个元素」。并不是将地址加1。

如:

//Example04 #include intmain(void) { inta[]={1,2,3,4,5}; int*p=a; printf("*p=%d,*(p+1)=%d,*(p+2)=%d ",*p,*(p+1),*(p+2)); printf("*p->%p,*(p+1)->%p,*(p+2)->%p ",p,p+1,p+2); return0; }

执行结果如下:

//Consequence 04 *p = 1, *(p+1) = 2, *(p+2) = 3 *p -> 00AFF838, *(p+1) -> 00AFF83C, *(p+2) -> 00AFF840

有的小伙伴可能会想,编译器是怎么知道访问下一个元素而不是地址直接加1呢?

其实就在我们定义指针变量的时候,就已经告诉编译器了。如果我们定义的是整型数组的指针,那么指针加1,实际上就是加上一个sizeof(int)的距离。相对于标准的下标访问,使用指针来间接访问数组元素的方法叫做指针法。

其实使用指针法来访问数组的元素,不一定需要定义一个指向数组的单独的指针变量,因为数组名自身就是指向数组「第一个元素」的指针,因此指针法可以直接作用于数组名:

... printf("p->%p,p+1->%p,p+2->%p ",a,a+1,a+2); printf("a=%d,a+1=%d,a+2=%d",*a,*(a+1),*(a+2)); ...

执行结果如下:

p->00AFF838,p+1->00AFF83C,p+2->00AFF840 b=1,b+1=2,b+2=3

现在你是不是感觉,数组和指针有点像了呢?不过笔者先提醒,数组和指针虽然非常像,但是绝对「不是」一种东西。

甚至你还可以直接用指针来定义字符串,然后用下标法来读取每一个元素:

//Example05 //代码来源于网络 #include #include intmain(void) { char*str="IloveTechZone!"; inti,length; length=strlen(str); for(i=0;i

程序运行如下:

//Consequence 05 I love TechZone!

在刚刚的代码里面,我们定义了一个「字符指针」变量,并且初始化成指向一个字符串。后来的操作,不仅在它身上可以使用「字符串处理函数」,还可以用「下标法」访问字符串中的每一个字符。

当然,循环部分这样写也是没毛病的:

... for(i=0,i

这就相当于利用了指针法来读取。

指针和数组的区别

刚刚说了许多指针和数组相互替换的例子,可能有的小伙伴又开始说:“这俩货不就是一个东西吗?”

随着你对指针和数组越来越了解,你会发现,C语言的创始人不会这么无聊去创建两种一样的东西,还叫上不同的名字。指针和数组终究是「不一样」的。

比如笔者之前看过的一个例子:

//Example06 //代码来源于网络 #include intmain(void) { charstr[]="IloveTechZone!"; intcount=0; while(*str++!='') { count++; } printf("总共有%d个字符。 ",count); return0; }

当编译器报错的时候,你可能会开始怀疑你学了假的C语言语法:

//Error in Example 06 错误(活动)E0137表达式必须是可修改的左值 错误C2105“++”需要左值

我们知道,*str++ != ‘’是一个复合表达式,那么就要遵循「运算符优先级」来看。具体可以回顾《C语言运算符优先级及ASCII对照表》。

str++比*str的优先级「更高」,但是自增运算符要在「下一条语句」的时候才能生效。所以这个语句的理解就是,先取出str所指向的值,判断是否为,若是,则跳出循环,然后str指向下一个字符的位置。

看上去貌似没啥毛病,但是,看看编译器告诉我们的东西:表达式必须是可修改的左值

++的操作对象是str,那么str到底是不是「左值」呢?

如果是左值的话,那么就必须满足左值的条件。

拥有用于识别和定位一个存储位置的标识符

存储值可修改

第一点,数组名str是可以满足的,因为数组名实际上就是定位数组第一个元素的位置。但是第二点就不满足了,数组名实际上是一个地址,地址是「不可以」修改的,它是一个常量。如果非要利用上面的思路来实现的话,可以将代码改成这样:

//Example06V2 //代码来源于网络 #include intmain(void) { charstr[]="IloveTechZone!"; char*target=str; intcount=0; while(*target++!='') { count++; } printf("总共有%d个字符。 ",count); return0; }

这样就可以正常执行了:

//Consequence 06 V2 总共有16个字符。

这样我们就可以得出:数组名只是一个「地址」,而指针是一个「左值」。

指针数组?数组指针?

看下面的例子,你能分辨出哪个是指针数组,哪个是数组指针吗?

int*p1[5]; int(*p2)[5];

单个的我们都可以判断,但是组合起来就有些难度了。

答案:

int*p1[5];//指针数组 int(*p2)[5];//数组指针

我们挨个来分析。

指针数组

数组下标[]的优先级是最高的,因此p1是一个有5个元素的「数组」。那么这个数组的类型是什么呢?答案就是int*,是「指向整型变量的指针」。因此这是一个「指针数组」。

那么这样的数组应该怎么样去初始化呢?

你可以定义5个变量,然后挨个取地址来初始化。

不过这样太繁琐了,但是,并不是说指针数组就没什么用。

比如:

//Example07 #include intmain(void) { char*p1[5]={ "人生苦短,我用Python。", "PHP是世界上最好的语言!", "Onemorething...", "一个好的程序员应该是那种过单行线都要往两边看的人。", "C++语言很容易让你犯错误;C++看起来好一些,但当你用它时,你会发现会死的更惨。" }; inti; for(i=0;i

结果如下:

//Consequence 07 人生苦短,我用Python。 PHP是世界上最好的语言! One more thing... 一个好的程序员应该是那种过单行线都要往两边看的人。 C语言很容易让你犯错误;C++看起来好一些,但当你用它时,你会发现会死的更惨。

这样是不是比二维数组来的更加直接更加通俗呢?

数组指针

()和[]在优先级里面属于「同级」,那么就按照「先后顺序」进行。

int(*p2)将p2定义为「指针」, 后面跟随着一个5个元素的「数组」,p2就指向这个数组。因此,数组指针是一个「指针」,它指向的是一个数组。

但是,如果想对数组指针初始化的时候,千万要小心,比如:

//Example08 #include intmain(void) { int(*p2)[5]={1,2,3,4,5}; inti; for(i=0;i

Visual Studio 2019报出以下的错误:

//Error and Warning in Example 08 错误(活动)E0146初始值设定项值太多 错误C2440“初始化”: 无法从“initializer list”转换为“int (*)[5]” 警告C4477“printf”: 格式字符串“%d”需要类型“int”的参数,但可变参数 1 拥有了类型“int *”

这其实是一个非常典型的错误使用指针的案例,编译器提示说这里有一个「整数」赋值给「指针变量」的问题,因为p2归根结底还是指针,所以应该给它传递一个「地址」才行,更改一下:

//Example08V2 #include intmain(void) { inttemp[5]={1,2,3,4,5}; int(*p2)[5]=temp; inti; for(i=0;i

可是怎么还是有问题呢?

我们回顾一下,指针是如何指向数组的。

inttemp[5]={1,2,3,4,5}; int*p=temp;

我们原本以为,指针p是指向数组的指针,但是实际上「并不是」。仔细想想就会发现,这个指针实际上是指向的数组的「第一个元素」,而不是指向数组。因为数组里面的元素在内存中都是挨着个儿存放的,因此只需要知道第一个元素的地址,就可以访问到后面的所有元素。

但是,这么来看的话,指针p指向的就是一个「整型变量」的指针,并不是指向「数组」的指针。而刚刚我们用的数组指针,才是指向数组的指针。因此,应该将「数组的地址」传递给数组指针,而不是将第一个元素的地址传入,尽管它们值相同,但是「含义」确实不一样:

//Example08V3 //Example08V2 #include intmain(void) { inttemp[5]={1,2,3,4,5}; int(*p2)[5]=&temp;//此处取地址 inti; for(i=0;i

程序运行如下:

//Consequence 08 1 2 3 4 5

指针和二维数组

在上一节《C语言之数组》我们讲过「二维数组」的概念,并且我们也知道,C语言的二维数组其实在内存中也是「线性存放」的。

假设我们定义了:int array[4][5]

array

array作为数组的名称,显然应该表示的是数组的「首地址」。由于二维数组实际上就是一维数组的「线性拓展」,因此array应该就是指的指向包含5个元素的数组的指针。

如果你用sizeof()去测试array和array+1的话,就可以测试出来这样的结论。

*(array+1)

首先从刚刚的问题我们可以得出,array+1同样也是指的指向包含5个元素的数组的指针,因此*(array+1)就是相当于array[1],而这刚好相当于array[1][0]的数组名。因此*(array+1)就是指第二行子数组的第一个元素的地址。

*(*(array+1)+2)

有了刚刚的结论,我们就不难推理出,这个实际上就是array[1][2]。是不是感觉非常简单呢?

总结一下,就是下面的这些结论,记住就好,理解那当然更好:

*(array+i)==array[i] *(*(array+i)+j)==array[i][j] *(*(*(array+i)+j)+k)==array[i][j][k] ...

数组指针和二维数组

我们在上一节里面讲过,在初始化二维数组的时候是可以偷懒的:

intarray[][3]={ {1,2,3}, {4,5,6} };

刚刚我们又说过,定义一个数组指针是这样的:

int(*p)[3];

那么组合起来是什么意思呢?

int(*p)[3]=array;

通过刚刚的说明,我们可以知道,array是指向一个3个元素的数组的「指针」,所以这里完全可以将array的值赋值给p。

其实C语言的指针非常灵活,同样的代码用不同的角度去解读,就可以有不同的应用。

那么如何使用指针来访问二维数组呢?没错,就是使用「数组指针」:

//Example09 #include intmain(void) { intarray[3][4]={ {0,1,2,3}, {4,5,6,7}, {8,9,10,11} }; int(*p)[4]; inti,j; p=array; for(i=0,i

运行结果:

//Consequence 09 0 1 2 3 4 5 6 7 8 9 10 11

void指针

void实际上是无类型的意思。如果你尝试用它来定义一个变量,编译器肯定会「报错」,因为不同类型所占用的内存有可能「不一样」。但是如果定义的是一个指针,那就没问题。void类型中指针可以指向「任何一个类型」的数据,也就是说,任何类型的指针都可以赋值给void指针。

将任何类型的指针转换为void是没有问题的。但是如果你要反过来,那就需要「强制类型转换」。此外,不要对void指针「直接解引用」,因为编译器其实并不知道void指针会存放什么样的类型。

//Example10 #include intmain(void) { intnum=1024; int*pi=# char*ps="TechZone"; void*pv; pv=pi; printf("pi:%p,pv:%p ",pi,pv); printf("*pv:%d ",*pv); pv=ps; printf("ps:%p,pv:%p ",ps,pv); printf("*pv:%s ",*pv); }

这样会报错:

//Error in Example 10 错误C2100非法的间接寻址 错误C2100非法的间接寻址

如果一定要这么做,那么可以用「强制类型转换」:

//Example10V2 #include intmain(void) { intnum=1024; int*pi=# char*ps="TechZone"; void*pv; pv=pi; printf("pi:%p,pv:%p ",pi,pv); printf("*pv:%d ",*(int*)pv); pv=ps; printf("ps:%p,pv:%p ",ps,pv); printf("*pv:%s ",pv); }

当然,使用void指针一定要小心,由于void指针几乎可以「通吃」所有类型,所以间接使得不同类型的指针转换变得合法,如果代码中存在不合理的转换,编译器也不会报错。

因此,void指针能不用则不用,后面讲函数的时候,还可以解锁更多新的玩法。

NULL指针

在C语言中,如果一个指针不指向任何数据,那么就称之为「空指针」,用「NULL」来表示。NULL其实是一个宏定义:

#defineNULL((void*)0)

在大部分的操作系统中,地址0通常是一个「不被使用」的地址,所以如果一个指针指向NULL,就意味着不指向任何东西。为什么一个指针要指向NULL呢?

其实这反而是一种比较指的推荐的「编程风格」——当你暂时还不知道该指向哪儿的时候,就让它指向NULL,以后不会有太多的麻烦,比如:

//Example11 #include intmain(void) { int*p1; int*p2=NULL; printf("%d ",*p1); printf("%d ",*p2); return0; }

第一个指针未被初始化。在有的编译器里面,这样未初始化的变量就会被赋予「随机值」。这样指针被称为「迷途指针」,「野指针」或者「悬空指针」。如果后面的代码对这类指针解引用,而这个地址又刚好是合法的话,那么就会产生莫名其妙的结果,甚至导致程序的崩溃。因此养成良好的习惯,在暂时不清楚的情况下使用NULL,可以节省大量的后期调试的时间。

指向指针的指针

开始套娃了。其实只要你理解了指针的概念,也就没什么大不了的。

//Example12 #include intmain(void) { intnum=1; int*p=# int**pp=&p; printf("num:%d ",num); printf("*p:%d ",*p); printf("**p:%d ",**pp); printf("&p:%p,pp:%p ",&p,pp); printf("&num:%p,p:%p,*pp:%p ",&num,p,*pp); return0; }

程序结果如下:

//Consequence 12 num: 1 *p: 1 **p: 1 &p: 004FF960, pp: 004FF960 &num: 004FF96C, p: 004FF96C, *pp: 004FF96C

当然你也可以无限地套娃,一直指下去。不过这样会让代码可读性变得「很差」,过段时间可能你自己都看不懂你写的代码了。

指针数组和指向指针的指针

那么,指向指针的指针有什么用呢?

它可不是为了去创造混乱代码,在一个经典的实例里面,就可以体会到它的用处:

char*Books[]={ "《C专家编程》", "《C和指针》", "《C的陷阱与缺陷》", "《CPrimerPlus》", "《Python基础教程(第三版)》" };

然后我们需要将这些书进行分类。我们发现,其中有一本是写Python的,其他都是C语言的。这时候指向指针的指针就派上用场了。首先,我们刚刚定义了一个指针数组,也就是说,里面的所有元素的类型「都是指针」,而数组名却又可以用指针的形式来「访问」,因此就可以使用「指向指针的指针」来指向指针数组:

... char**Python; char**CLang[4]; Python=&Books[5]; CLang[0]=&Books[0]; CLang[1]=&Books[1]; CLang[2]=&Books[2]; CLang[3]=&Books[3]; ...

因为字符串的取地址值实际上就是其「首地址」,也就是一个「指向字符指针的指针」,所以可以这样赋值。

这样,我们就利用指向指针的指针完成了对书籍的分类,这样既避免了浪费多余的内存,而且当其中的书名要修改,只需要改一次即可,代码的灵活性和安全性都得到了提升。

常量和指针

常量,在我们目前的认知里面,应该是这样的:

520, 'a'

或者是这样的:

#defineMAX1000 #defineB'b'

常量和变量最大的区别,就是前者「不能够被修改」,后者可以。那么在C语言中,可以将变量变成像具有常量一样的特性,利用const即可。

constintmax=1000; constchara='a';

在const关键字的作用下,变量就会「失去」本来具有的可修改的特性,变成“只读”的属性。

指向常量的指针

强大的指针当然也是可以指向被const修饰过的变量,但这就意味着「不能通过」指针来修改它所引用的值。总结一下,就是以下4点:

指针可以修改为指向不同的变量

指针可以修改为指向不同的常量

可以通过解引用来读取指针指向的数据

不可以通过解引用来修改指针指向的数据

常量指针

指向非常量的常量指针

指针本身作为一种「变量」,也是可以修改的。因此,指针也是可以被const修饰的,只不过位置稍稍「发生了点变化」:

... int*constp=# ...

这样的指针有如下的特性:

指针自身不能够被修改

指针指向的值可以被修改

指向常量的常量指针

在定义普通变量的时候也用const修饰,就得到了这样的指针。不过由于限制太多,一般很少用到:

... intnum=100; constintcnum=200; constint*constp=&cnum; ...

责任编辑:lq

原文标题:花15分钟快速掌握C语言中的指针

文章出处:【微信号:c-stm32,微信公众号:STM32嵌入式开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

扫一扫,分享给好友

复制链接分享 评论

发布评论请先 登录

相关推荐

C语言编程中main()函数退出之后程序去哪儿了 对于嵌入式系统,如果没有运行RTOS,那么程序开发中的主函数main()需要通过某种机制使其永远愉快.... 的头像 电子工程世界 发表于 03-22 17:44 • 556次 阅读 FPGA基础知识之学习误区 学习FPGA,笔者推崇的学习方法是先整体再局部。先对FPGA 有一个整体的认识,包括知道有哪些知识点.... 发表于 03-22 14:47 • 6次 阅读 FPGA基础知识之学习误区 详解#define的奇妙用法 在 C/C++ 编程语言中,当程序被编译时,被发送到编译器,编译器将程序转换为机器语言,然后完成编译.... 的头像 STM32嵌入式开发 发表于 03-22 09:03 • 78次 阅读 C语言常见问题 C语言常见问题 发表于 03-21 14:57 • 5次 阅读 RISC-V GNU编译器工具链 这是RISC-V C和 C++交叉编译器。它支持两种构建模式:通用ELF/Newlib工具链和更复杂.... 发表于 03-17 16:36 • 16次 阅读 第九章 AVR C语言的应用.PDF 本教程讲解的是AVR C语言编程,包括开发环境、语言语法、特殊用法基础知识的讲解 发表于 03-17 14:21 • 14次 阅读 GH集成开发环境和编译器 说实话,以前也用过正版的编译器,我记得之前用过正版的IAR编译器license也没有多贵,而最近用了.... 的头像 嵌入式程序猿 发表于 03-16 17:08 • 315次 阅读 为什么说C语言程序运行时要比其他语言编写的程序快得多 C语言程序运行时要比其他语言编写的程序快得多,因为它“离底层机器很近”,这个说法正确吗? 的头像 C语言编程学习基地 发表于 03-16 08:45 • 154次 阅读 C语言如何使用正则表达式 C语言的正则表达式规则,特此跟大家分享。 的头像 C语言编程学习基地 发表于 03-16 08:41 • 189次 阅读 VScode编译器如何配置C/C++编译环境 昨天有伙伴私信我,为什么我用C语言写的hello world几行代码,在编译器里面报错了呢? 的头像 C语言编程学习基地 发表于 03-16 08:38 • 151次 阅读 单片机实现24C02存储上次使用中状态的C语言实例 单片机实现24C02存储上次使用中状态的C语言实例 发表于 03-15 14:19 • 31次 阅读 Verilog逻辑设计中的循环语句和运算符 “ 本文主要分享了在Verilog设计过程中一些经验与知识点,主要包括循环语句(forever、re.... 的头像 FPGA之家 发表于 03-15 11:41 • 205次 阅读 请问rt_thread的stack_size该如何去定呢 我创建了一个thread,里面只有一个64字节的变量,rt_thread_create时,参数stack_size为128时就运行不正常,而大于192就运行正常... 发表于 03-15 09:17 • 1066次 阅读 关于python包导入的三个冷门知识点 使用 from module import * 默认情况下会导入 module 里的所有变量,若你只.... 的头像 python爬虫知识分享 发表于 03-14 16:33 • 232次 阅读 AbsInt代码安全性能分析套件产品介绍 德国AbsInt公司是专注于安全苛求软件研发、确认、验证和认证的工具链供应商。能够为客户提供较完整的.... 的头像 经纬恒润 发表于 03-14 15:32 • 244次 阅读 S7-1200使用Slice方式对变量进行寻址 可以选择包含所声明变量的特定地址区域,可以访问宽为 1 位、8 位、16 位或 32 位的区域,这种.... 的头像 机器人及PLC自动化应用 发表于 03-14 14:16 • 160次 阅读 C语言使用回调函数模拟委托与反射 函数是C语言的核心概念。主调函数(caller)调用被调函数(callee)是一般的调用关系,如果被.... 的头像 STM32嵌入式开发 发表于 03-14 10:19 • 149次 阅读 第一个C语言编译器的来源 不知道你有没有想过,某种编程语言的第一个编译器是怎么来的呢?这不就是“鸡生蛋,蛋生鸡”的问题吗? 的头像 嵌入式ARM 发表于 03-12 12:36 • 684次 阅读 详解C语言中的短路现象   C语言一经出现,就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语.... 的头像 嵌入式ARM 发表于 03-12 12:23 • 585次 阅读 详解C语言中的短路现象 如何快速学习并精通C语言 学习C语言不是一朝一夕的事情,但也不需要花费十年时间才能精通。如何以最小的代价学习并精通C语言是本文.... 的头像 嵌入式ARM 发表于 03-12 11:59 • 443次 阅读 编写一个简单的C51程序 对于嵌入式系统,如果没有运行RTOS,那么程序开发中的 主函数(main())需要通过某种机制使其永.... 的头像 嵌入式ARM 发表于 03-12 11:17 • 406次 阅读 C语言操作寄存器的常见手法 使用C语言对寄存器赋值时,常常需要用到C语言的位操作方法。把寄存器某位清零 假设a代表寄存器,.... 的头像 硬件攻城狮 发表于 03-12 09:06 • 220次 阅读 ​为什么嵌入式开发要选择C语言? 简洁紧凑,使用灵活的语法机制,并能直接访问硬件能够直接访问硬件的语言有:汇编和C语言汇编属于低级语言.... 的头像 硬件攻城狮 发表于 03-11 10:21 • 172次 阅读 PCB分析常见方法 软件代码有bug,可以通过人工查找,也可以通过编译发现,同时也可以通过代码静态分析工具找到错误或警告.... 的头像 strongerHuang 发表于 03-10 17:55 • 836次 阅读 C语言有哪些语法技巧和功能 C语言常常让人觉得它所能表达的东西非常有限。它不具有类似第一级函数和模式匹配这样的高级功能。但是C非.... 的头像 strongerHuang 发表于 03-10 17:42 • 828次 阅读 c语言嵌入式编程 比较详尽的嵌入式C语言解答和分析 发表于 03-10 14:53 • 499次 阅读 使用GoRK3288库的时候需要用到的语法有哪些呢 使用GoRK3288库的时候需要用到的语法有哪些呢?... 发表于 03-10 06:49 • 499次 阅读 C/C++在线编译器的功能 作为程序员,使用编译器是必备技能,但是从入门到放弃,基本上就是在开发环境安装、配置这一步。。。 的头像 strongerHuang 发表于 03-09 17:52 • 3746次 阅读 C语言简单模拟委托与简单模拟反射 函数是C语言的核心概念。主调函数(caller)调用被调函数(callee)是一般的调用关系,如果被.... 的头像 硬件攻城狮 发表于 03-09 17:17 • 3464次 阅读 C语言中内存四区模型的本质区别和代码分析 ●数据类型可理解为创建变量的模具:是固定内存大小的别名。 的头像 嵌入式ARM 发表于 03-09 14:45 • 266次 阅读 鲲鹏开发套件DevKit 2.0加速应用迁移 多样性计算时代,跨平台的应用迁移和开发不简单数字经济时代,自动驾驶、智能制造、电子政务、智慧金融等新.... 的头像 华为开发者社区 发表于 03-09 14:14 • 263次 阅读 采用FMDft61t133芯片点亮LED的代码 由于目前本人零时朝8bit单片机方向走所以其内容接下来也是8bit单片机。 的头像 互联网电子设计 发表于 03-09 09:19 • 1593次 阅读 采用FMDft61t133芯片点亮LED的代码 如何调试TAS5805? 如何调试TAS5805? 发表于 03-09 06:38 • 85次 阅读 如何通过驱动的方式点亮LED灯? 如何通过驱动的方式点亮LED灯? 发表于 03-09 06:34 • 223次 阅读 RK3399 ffmpeg的编译步骤有哪些 RK3399 ffmpeg的编译步骤有哪些?求解答 发表于 03-08 08:30 • 285次 阅读 【RISC-V 生态软件系列】HHB AI部署工具实战: HAAS AI EDK精度验证和性能测试 测试和验证模型在上的运行精度和性能主要分成三步:1)生成模型;2)上板执行;3)结果分析。 1. 生成模型 在资源下载页面下... 发表于 03-08 07:13 • 145次 阅读 YL-236指导书C语言版 YL-236指导书C语言版,方便大赛训练时使用。 发表于 03-07 15:47 • 18次 阅读 分享一些单片机基础入门级的知识 很多在校的大学生或者在岗位想加深自己职业技能的人,都想通过学习单片机来补充自己,以便与不被社会淘汰,.... 发表于 03-07 14:18 • 95次 阅读 分享一些单片机基础入门级的知识 RK3399如何启动shell脚本? RK3399如何启动shell脚本? 发表于 03-04 06:04 • 134次 阅读 单片机存储器的入门知识分享 特殊功能寄存器的概念理解清楚了,单片机的编程就不难了,整个的单片机的存储空间的一个结构图,这也就是我.... 发表于 03-03 10:40 • 67次 阅读 单片机存储器的入门知识分享 C语言中枚举精讲分析      在使用带有数字性质的名称时有的时候需要用到枚举的方式进行定义函数名字,按照对应的数字格式来.... 的头像 嵌入式应用开发 发表于 03-02 10:58 • 1783次 阅读 C语言中枚举精讲分析 怎样去编写RTC实时驱动的代码呢 实时时钟是什么? 怎样去编写RTC实时驱动的代码呢? ... 发表于 03-02 09:50 • 191次 阅读 STM32F103系列相关开发软件及环境安装与搭建 STM32F103系列相关开发软件及环境安装与搭建文章目录STM32F103系列相关开发软件及环境安装与搭建前言一、开发环境搭建1、... 发表于 03-02 07:46 • 6112次 阅读 关于系统应用core dump的分析 解决段错误,系统应用core dump的分析 这个错误在单片机上往往以hardfault的错误出现,分析思路都是分析堆栈的位置;单片机的编译... 发表于 03-02 06:10 • 113次 阅读 大小端定义与应用场景及C语言中大小端的实现 大端模式:数据的高字节保存在内存的低地址中,数据的低字节保存在内存的高地址中; 的头像 嵌入式应用开发 发表于 02-28 08:52 • 281次 阅读 arduino开发实战指南 arduino开发实战指南 发表于 02-22 14:56 • 67次 阅读 《c#高级编程》第六版pdf c#入门经典第五版由清华大学出版社出版,全面介绍C#编程的基础知识,从该语言本身一直到windows.... 发表于 02-21 14:25 • 56次 阅读 Xilinx SystemVerilog中的基本联合体 在 SystemVerilog 中,联合体只是信号,可通过不同名称和纵横比来加以引用。 其工作方式为.... 的头像 赛灵思 发表于 02-19 19:01 • 213次 阅读 Xilinx SystemVerilog中的基本联合体 一文详细了解单片机的大小端模式   咱们中文中常常提到的大小端的英文名字为“endianness”,英文直译的意思是“字节序”,是内.... 的头像 硬件攻城狮 发表于 02-16 17:44 • 617次 阅读 一文详细了解单片机的大小端模式 为什么要用C语言实现面向对象   不知道有多少人去了解过语言的发展史,早期C语言的语法功能其实比较简单。随着应用需求和场景的变化,.... 的头像 硬件攻城狮 发表于 02-16 16:19 • 421次 阅读 为什么要用C语言实现面向对象 STC8G/STC8H系列MCU的C语言封装库下载 FwLib_STC8 是面向 STC8G/STC8H 系列 MCU 的C语言封装库 发表于 02-15 15:05 • 113次 阅读 一文详细了解五种IO模型 五种IO模型包括:阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO。 的头像 硬件攻城狮 发表于 02-14 14:38 • 543次 阅读 一文详细了解五种IO模型 C语言常见内存错误及解决方法   本文将带您了解一些良好的和内存相关的编码实践,以将内存错误保持在控制范围内。内存错误是 C 和 .... 的头像 硬件攻城狮 发表于 02-14 13:10 • 491次 阅读 《C陷阱与缺陷》pdf 《C陷阱与缺陷》pdf 发表于 02-14 11:02 • 80次 阅读 《C语言程序设计》第四版谭浩强 《C语言程序设计》第四版谭浩强 发表于 02-14 10:09 • 173次 阅读 C语言在STM32中的内存分配   num_byte、num_word、num_byte_static和point_heap存储在内.... 的头像 硬件攻城狮 发表于 02-10 14:57 • 527次 阅读 C语言在STM32中的内存分配


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有