买球·(中国)投注APP官方网站

新闻

足球投注app按照在硬件的区域不同-买球·(中国)投注APP官方网站

发布日期:2026-01-24 06:31    点击次数:166

足球投注app按照在硬件的区域不同-买球·(中国)投注APP官方网站

本文讲演镶嵌式斥地中需要了解的C讲话常识和要点,但愿每个读到这篇著述的东谈主都能有所成绩。

有东谈主说C讲话吵嘴常简略的,也有东谈主说学了十年照旧莫得学显著。事实上,编写优质镶嵌式C设施并非易事,需要了解揣测硬件特点和劣势,还需要了解相应地编译旨趣。

一、关节字

的确每一门讲话中都揣测键字,具有特殊功能,C讲话也不例外,按照功能可分为:

▪ 数据类型(常用char, short, int, long, unsigned, float, double)

▪ 运算和抒发式( =, +, -, *, while, do-while, if, goto, switch-case)

▪ 数据存储(auto, static, extern,const, register,volatile,restricted)

▪ 结构(struct, enum, union,typedef)

▪ 位操作和逻辑运算(<<, >>, &, |, ~,^, &&

▪ 预处理(#define, #include, #error,#if...#elif...#else...#endif等)

▪ 平台彭胀关节字(__asm, __inline,__syscall)

这些关节字共同组成了镶嵌式平台的C讲话语法,镶嵌式的应用从逻辑上不错抽象为以下三个部分:

▪ 数据的输入(如传感器,信号,接口输入)

▪ 数据的处理(如公约的解码和封包,AD采样值的调度等)

▪ 数据的输出(GUI的涌现,输出的引脚状态,DA的输出戒指电压,PWM波的占空比等)

连结在系数这个词镶嵌式应用斥地的经由中,对数据的不休包含以下几部分:

▪ 数据类型

▪ 存储空间

▪ 位和逻辑操作

▪ 数据结构

为了应酬镶嵌式斥地中受限的资源环境,C讲话从语法上相沿上述功能的达成,并提供相应的优化机制。

二、数据类型

C讲话援助常用的字符型,整型,浮点型变量,有些编译器如keil还彭胀援助bit(位)和sfr(寄存器)等数据类型来骄慢特殊的地址操作。C讲话只法规了每种基本数据类型的最小取值限度,因此在不同芯片平台上雷同类型可能占用不同长度的存储空间,这就需要在代码达成时谈判后续移植的兼容性,而C讲话提供的typedef即是用于处理这种情况的关节字,在大部分援助跨平台的软件阵势中被经受,典型的如下:

typedef unsigned char uint8_t;

typedef unsigned short uint16_t;

typedef unsigned int uint32_t;

......

typedef signed int int32_t;

既然不同平台的基本数据宽度不同,那么何如细目面前平台的基础数据类型如int的宽度,这就需要C讲话提供的接口sizeof,达成如下。

printf("int size:%d, short size:%d, char size:%d\n", sizeof(int), sizeof(char), sizeof(short));

这里还有弥留的常识点,即是指针的宽度,如

char *p;

printf("point p size:%d\n", sizeof(p));

其实这就和芯片的可寻址宽度揣测,如32位MCU的宽度即是4,64位MCU的宽度即是8,在有些技巧这亦然搜检MCU位宽比拟简略的方式。

三、内存不休和存储架构

C讲话允许设施变量在界说时就细目内存地址,通过作用域,以及关节字extern,static,达成了雅致的处理机制,按照在硬件的区域不同,内存分拨有三种方式:

▪ 从静态存储区域分拨。内存在设施编译的技巧就照旧分拨好,这块内存在设施的系数这个词运行时间都存在。例如全局变量,static 变量。

▪ 在栈上创建。在实施函数时,函数内局部变量的存储单位都不错在栈上创建,函数实施收尾时这些存储单位自动被开释。栈内存分拨运算内置于处理器的请示蚁合 ,遵循很高,可是分拨的内存容量有限。

▪ 从堆上分拨,亦称动态内存分拨。设施在运行的技巧用 malloc 或 new 苦求轻易若干的内存,设施员我方认真在何时用 free 或 delete 开释内存。动态内存的生计期由设施员决定,使用相称活泼,但同期碰到问题也最多。

这里先看个简略的C讲话实例。

//main.c

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

static int st_val; //静态全局变量 -- 静态存储区

int ex_val; //全局变量 -- 静态存储区

int main(void)

{

int a = 0; //局部变量 -- 栈上苦求

int *ptr = NULL; //指针变量

static int local_st_val = 0; //静态变量

local_st_val += 1;

a = local_st_val;

ptr = (int *)malloc(sizeof(int)); //从堆上苦求空间

if(ptr != NULL)

{

printf("*p value:%d", *ptr);

free(ptr);

ptr = NULL;

//free后需要将ptr置空,不然会导致后续ptr的校验失效,出现野指针

}

}

C讲话的作用域不仅描画了标记符的可走访的区域,其实也法规了变量的存储区域,在文献作用域的变量st_val和ex_val被分拨到静态存储区,其中static关节字主要限制变量能否被其它文献走访,而代码块作用域中的变量a, ptr和local_st_val则要左证类型的不同,分拨到不同的区域,其中a是局部变量,被分拨到栈中,ptr手脚指针,由malloc分拨空间,因此界说在堆中,而local_st_val则被关节字限制,暗意分拨到静态存储区,这里就触及到弥留常识点,static在文献作用域和代码块作用域的意旨是不同的:在文献作用域用于限制函数和变量的外部连络性(能否被其它文献走访), 在代码块作用域则用于将变量分拨到静态存储区。

对于C讲话,若是通顺上述常识对于内存不休基本就豪阔。

但对于镶嵌式C编程来说,界说一个变量,它不一定在内存,也即是SRAM中,也有可能在FLASH空间,或径直由寄存器存储(register界说变量或者高优化等第下的部分局部变量),如界说为const的全局变量界说在FLASH中,界说为register的局部变量会被优化到径直放在通用寄存器中,在优化运行速率,或者存储受限时,通顺这部分常识对于代码的爱戴就很特意旨。

此外,镶嵌式C讲话的编译器中会彭胀内存不休机制,如援助散布加载机制和__attribute__((section("用户界说区域"))),允许指定变量存储在特殊的区域如(SDRAM, SQI FLASH), 这强化了对内存的不休,以适合复杂的应用环境场景和需求。

LD_ROM 0x00800000 0x10000 { ;load region size_region

EX_ROM 0x00800000 0x10000 { ;load address = execution address

*.o (RESET, +First)

*(InRoot$$Sections)

.ANY (+RO)

}

EX_RAM 0x20000000 0xC000 { ;rw Data

.ANY (+RW +ZI)

}

EX_RAM1 0x2000C000 0x2000 {

.ANY(MySection)

}

EX_RAM2 0x40000000 0x20000{

.ANY(Sdram)

}

}

int a[10] __attribute__((section("Mysection")));

int b[100] __attribute__((section("Sdram")));

经受这种方式,咱们就不错将变量指定到需要的区域,这在某些情况下是必须的,如作念GUI或者网页时因为要存储多半图片和文档,里面FLASH空间可能不及,这时就不错将变量声明到外部区域,另外内存中某些部分的数据比拟弥留,为了幸免被其它内容覆盖,可能需要单独永诀SRAM区域,幸免被误修改导致致命性的造作,这些教化在实质的居品斥地中是常用且弥留,不外因为篇幅原因,这里只节略的提供例子,若是责任中碰到这种需求,提出驻守去了解下。

至于堆的使用,对于镶嵌式Linux来说,使用起来和圭臬C讲话一致,安定malloc后的检验,开释跋文得置空,幸免"野指针“,不外对于资源受限的单片机来说,使用malloc的场景一般较少,若是需要频频苦求内存块的场景,都会构建基于静态存储区和内存块分割的一套内存不休机制,一方面遵循会更高(用固定大小的块提前分割,在使用时径直查找编号处理),另一方濒临于内存块的使用可控,不错灵验幸免内存碎屑的问题,常见的如RTOS和汇注LWIP都是经受这种机制,我个东谈主民俗也经受这种方式,是以对于堆的细节不在描画,若是但愿了解,不错参考<C Primer Plus>中对于存储揣测的讲解。

四、指针和数组

数组和指针时时是引起设施bug的主要原因,如数组越界,指针越界,犯警地址走访,非对王人走访,这些问题背后时时都有指针和数组的影子,因此通顺和掌持指针和数组,是成为及格C讲话斥地者的必经之路。

数组是由雷同类型元素组成,当它被声明时,编译器就左证里面元素的特点在内存均分拨一段空间,另外C讲话也提供多维数组,以应酬特殊场景的需求,而指针则是提供使用地址的记号秩序,惟有指向具体的地址才特意旨,C讲话的指针具有最大的活泼性,在被走访前,不错指向任何地址,这大大方便了对硬件的操作,但同期也对斥地者有了更高的要求。

参考如下代码:

int main(void)

{

char cval[] = "hello";

int i;

int ival[] = {1, 2, 3, 4};

int arr_val[][2] = {{1, 2}, {3, 4}};

const char *pconst = "hello";

char *p;

int *pi;

int *pa;

int **par;

p = cval;

p++; //addr加多1

pi = ival;

pi+=1; //addr加多4

pa = arr_val[0];

pa+=1; //addr加多4

par = arr_val;

par++; //addr加多8

for(i=0; i<sizeof(cval); i++)

{

printf("%d ", cval[i]);

}

printf("\n");

printf("pconst:%s\n", pconst);

printf("addr:%d, %d\n", cval, p);

printf("addr:%d, %d\n", icval, pi);

printf("addr:%d, %d\n", arr_val, pa);

printf("addr:%d, %d\n", arr_val, par);

}

/* PC端64位系统下运行收场

0x68 0x65 0x6c 0x6c 0x6f 0x0

pconst:hello

addr:6421994, 6421995

addr:6421968, 6421972

addr:6421936, 6421940

addr:6421936, 6421944 */

对于数组来说,一般从0运行取得值,以length-1手脚收尾,通过[0, length)半开半闭区间走访,这一般不会出问题,可是某些技巧,咱们需要倒着读取数组时,有可能造作的将length手脚肇端点,从而导致走访越界,另外皮操作数组时,随机为了简约空间,将走访的下标变量i界说为unsigned char类型,而C讲话中unsigned char类型的限度是0~255,若是数组较大,会导致数组向上时无法截止,从而堕入死轮回,这种在领先代码构建时很容易幸免,但后期若是更变需求,在加大数组后,在使用数组的其它场地都会有隐患,需要荒芜安定。

指针占有的空间与芯片的寻址宽度揣测,32位平台为4字节,64位为8字节,而指针的加减运算中的长度又与它的类型揣测,如char类型为1,int类型为4,若是你仔细不雅察上头的代码就会发现par的值加多了8,这是因为指向指针的指针,对应的变量是指针,也即是长度即是指针类型的长度,在64位平台下为8,若是在32位平台则为4,这些常识通顺起来并不勤勉,可是这些特点在工程应用中稍有失慎,就会埋下不易察觉的问题。另外指针还援助强制调度,这在某些情况下尽头有用,参考如下代码:

#include <stdio.h>

typedef struct

{

int b;

int a;

}STRUCT_VAL;

static __align(4) char arr[8] = {0x12, 0x23, 0x34, 0x45, 0x56, 0x12, 0x24, 0x53};

int main(void)

{

STRUCT_VAL *pval;

int *ptr;

pval = (STRUCT_VAL *)arr;

ptr = (int *)&arr[4];

printf("val:%d, %d", pval->a, pval->b);

printf("val:%d,", *ptr);

}

//0x45342312 0x53241256

//0x53241256

基于指针的强制调度,在公约领会,数据存储不休中高效快捷的处置了数据领会的问题,可是在处理经由中触及的数据对王人,大小端,是常见且十分易错的问题,如上头arr字符数组,通过__align(4)强制界说为4字节对王人是必要的,这里不错保证后续调度成int指针走访时,不会触发非对王人走访很是,若是莫得强制界说,char默许是1字节对王人的,诚然这并不即是一定触发很是(由系数这个词内存的布局决定arr的地址,也与实质使用的空间是否援助非对王人走访揣测,如部分SDRAM使用非对王人走访时,会触发很是), 这就导致可能增减其它变量,就可能触发这种很是,而出很是的场地时时和添加的变量毫无关系,况且代码在某些平台运行宽泛,切换平台后触发很是,这种笼罩的阵势是镶嵌式中很难查找处置的问题。另外,C讲话指针还有特殊的用法即是通过强制调度给特定的物理地址走访,通过函数指针达成回调,如下:

#include <stdio.h>

typedef int (*pfunc)(int, int);

int func_add(int a, int b){

return a+b;

}

int main(void)

{

pfunc *func_ptr;

*(volatile uint32_t *)0x20001000 = 0x01a23131;

func_ptr = func_add;

printf("%d\n", func_ptr(1, 2));

}

这里讲解下,volatile易变的,可变的,一般用于以下几种景象:

▪ 并行树立的硬件寄存器(如:状态寄存器)

▪ 一个中断作事子设施中会走访到的非自动变量(Non-automatic variables)

▪ 多线程应用中被几个任务分享的变量

volatile不错处置用户模式和很是中断走访合并个变量时,出现的不同步问题,另外皮走访硬件地址时,volatile也谢透顶地址走访的优化,从而确保走访的实质的地址,忽闪volatile的应用,在镶嵌式底层中十分弥留,亦然镶嵌式C从业者的基本要求之一。函数指针在一般镶嵌式软件的斥地中并不常见,但对许多弥留的达成如异步回调,驱动模块,使用函数指针就不错利用简略的方式达成好多应用,诚然我这里只可说是投砾引珠,许多细节常识是值得驻守去了解掌持的。

五、结构类型和对王人

C讲话提供自界说数据类型来描画一类具有雷同特征点的事务,主要援助的有结构体,陈列和合股体。其中陈列通过别称限制数据的走访,不错让数据更直不雅,易读,达成如下:

typedef enum {spring=1, summer, autumn, winter }season;

season s1 = summer;

合股体的是能在合并个存储空间里存储不同类型数据的数据类型,对于合股体的占用空间,则是以其中占用空间最大的变量为准,如下:

typedef union{

char c;

short s;

int i;

}UNION_VAL;

UNION_VAL val;

int main(void)

{

printf("addr:0x%x, 0x%x, 0x%x\n",

(int)(&(val.c)), (int)(&(val.s)), (int)(&(val.i)));

val.i = 0x12345678;

if(val.s == 0x5678)

printf("小端模式\n");

else

printf("大端模式\n");

}

/*

addr:0x407970, 0x407970, 0x407970

小端模式

*/

合股体的用途主要通过分享内存地址的方式,达成对数据里面段的走访,这在领会某些变量时,提供了更为方便的方式,此外测试芯片的大小端模式亦然合股体的常见应用,诚然利用指针强制调度,也能达成该打算,达成如下:

int data = 0x12345678;

short *pdata = (short *)&data;

if(*pdata = 0x5678)

printf("%s\n", "小端模式");

else

printf("%s\n", "大端模式");

不错看出使用合股体在某些情况下不错幸免对指针的奢侈。结构体则是将具有共通特征的变量组成的围聚,比起C++的类来说,它莫得安全走访的限制,不援助径直里面带函数,但通过自界说数据类型,函数指针,仍然约略达成好多近似于类的操作,对于大部分镶嵌式阵势来说,结构化处理数据对于优化举座架构以及后期爱戴大有便利,底下例如讲解:

typedef int (*pfunc)(int, int);

typedef struct{

int num;

int profit;

pfunc get_total;

}STRUCT_VAL;

int GetTotalProfit(int a, int b)

{

return a*b;

}

int main(void){

STRUCT_VAL Val;

STRUCT_VAL *pVal;

Val.get_total = GetTotalProfit;

Val.num = 1;

Val.profit = 10;

printf("Total:%d\n", Val.get_total(Val.num, Val.profit)); //变量走访

pVal = &Val;

printf("Total:%d\n", pVal->get_total(pVal->num, pVal->profit)); //指针走访

}

/*

Total:10

Total:10

*/

C讲话的结构体援助指针和变量的方式走访,通过调度不错领会轻易内存的数据(如咱们之前提到的通过指针强制调度领会公约),另外通过将数据和函数指针打包,在通过指针传递,是达成驱动层实接口切换的弥留基础,有着弥留的实际意旨,另外基于位域,合股体,结构体,不错达成另一种位操作,这对于封装底层硬件寄存器具有弥介意旨,实际如下:

typedef unsigned char uint8_t;

union reg{

struct{

uint8_t bit0:1;

uint8_t bit1:1;

uint8_t bit2_6:5;

uint8_t bit7:1;

}bit;

uint8_t all;

};

int main(void)

{

union reg RegData;

RegData.all = 0;

RegData.bit.bit0 = 1;

RegData.bit.bit7 = 1;

printf("0x%x\n", RegData.all);

RegData.bit.bit2_6 = 0x3;

printf("0x%x\n", RegData.all);

}

/*

0x81

0x8d

*/

通过合股体和位域操作,不错达成对数据内bit的走访,这在寄存器以及内存受限的平台,提供了方便且直不雅的处理方式,另外对于结构体的另一个弥留常识点即是对王人了,通过对王人走访,不错大幅度提升运行遵循,可是因为对王人引入的存储长度问题,亦然容易出错的问题,对于对王人的通顺,不错分类为如下讲解:

▪ 基础数据类型:以默许的的长度对王人,如char以1字节对王人,short以2字节对王人等

▪ 数组 :按照基本数据类型对王人,第一个对王人了背面的当然也就对王人了

▪ 合股体 :按其包含的长度最大的数据类型对王人

▪ 结构体:结构体中每个数据类型都要对王人,结构体自己以里面最大数据类型长度对王人

union DATA{

int a;

char b;

};

struct BUFFER0{

union DATA data;

char a;

//reserved[3]

int b;

short s;

//reserved[2]

}; //16字节

struct BUFFER1{

char a;

//reserved[0]

short s;

union DATA data;

int b;

};//12字节

int main(void)

{

struct BUFFER0 buf0;

struct BUFFER1 buf1;

printf("size:%d, %d\n", sizeof(buf0), sizeof(buf1));

printf("addr:0x%x, 0x%x, 0x%x, 0x%x\n",

(int)&(buf0.data), (int)&(buf0.a), (int)&(buf0.b), (int)&(buf0.s));

printf("addr:0x%x, 0x%x, 0x%x, 0x%x\n",

(int)&(buf1.a), (int)&(buf1.s), (int)&(buf1.data), (int)&(buf1.b));

}

/*

size:16, 12

addr:0x61fe10, 0x61fe14, 0x61fe18, 0x61fe1c

addr:0x61fe04, 0x61fe06, 0x61fe08, 0x61fe0c

*/

其中union合股体的大小与里面最大的变量int一致,为4字节,左证读取的值,就知谈实质内存布局和填充的位置是一致,事实上学领略过填充来通顺C讲话的对王人机制,是灵验且快捷的方式。

六、预处理机制

C讲话提供了丰富的预处理机制,方便了跨平台的代码的达成,此外C讲话通过宏机制达成的数据和代码块替换,字符串形势化,代码段切换,对于工程应器用有弥介意旨,底下按照功能需求,描画在C讲话应用中的常用预处理机制。

#include 包含文献号令,在C讲话中,它实施的后果是将包含文献中的系数内容插入到面前位置,这不单包含头文献,一些参数文献,配置文献,也不错使用该文献插入到面前代码的指定位置。其中<>和""分别暗意从圭臬库旅途照旧用户自界说旅途运行检索。

#define宏界说,常见的用法包含界说常量或者代码段别称,诚然某些情况下和洽##形势化字符串,不错达成接口的合股化处理,实例如下:

#define MAX_SIZE 10

#define MODULE_ON 1

#define ERROR_LOOP() do{\

printf("error loop\n");\

}while(0);

#define global(val) g_##val

int global(v) = 10;

int global(add)(int a, int b)

{

return a+b;

}

#if..#elif...#else...#endif, #ifdef..#endif, #ifndef...#endif条目聘用判断,条目聘用主要用于切换代码块,这种抽象性阵势和跨平台阵势中为了骄慢多种情况下的需求时时会被使用。

#undef 取消界说的参数,幸免重界说问题。

#error,#warning用于用户自界说的告警信息,和洽#if,#ifdef使用,不错限制造作的预界说配置。

#pragma 带参数的预界说处理,常见的#pragma pack(1), 不外使用后会导致后续的系数这个词文献都以树立的字节对王人,和洽push和pop不错处置这种问题,代码如下:

#pragma pack(push)

#pragma pack(1)

struct TestA

{

char i;

int b;

}A;

#pragma pack(pop); //隆要害调用pop,不然会导致后续文献都以pack界说值对王人,实施不适合预期

等同于

struct _TestB{

char i;

int b;

}__attribute__((packed))A;足球投注app