国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

STM32中重要的C語(yǔ)言知識(shí)點(diǎn)整理

Q4MP_gh_c472c21 ? 來(lái)源:嵌入式ARM ? 作者:里米君 ? 2021-02-10 17:31 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

說(shuō)在前面的話

一位初學(xué)單片機(jī)的小伙伴讓我推薦C語(yǔ)言書(shū)籍,因?yàn)镃語(yǔ)言基礎(chǔ)比較差,想把C語(yǔ)言重新學(xué)一遍,再去學(xué)單片機(jī),我以前剛學(xué)單片機(jī)的時(shí)候也有這樣子的想法。

其實(shí)C語(yǔ)言是可以邊學(xué)單片機(jī)邊學(xué)的,學(xué)單片機(jī)的一些例程中,遇到不懂的C語(yǔ)言知識(shí),再去查相關(guān)的知識(shí)點(diǎn),這樣印象才會(huì)深刻些。

下面就列出了一些STM32中重要的C語(yǔ)言知識(shí)點(diǎn),初學(xué)的小伙伴可以多讀幾遍,其中大多知識(shí)點(diǎn)之前都有寫(xiě)過(guò),這里重新整理一下,更詳細(xì)地分析解釋可以閱讀附帶的鏈接。

assert_param

斷言(assert)就是用于在代碼中捕捉這些假設(shè),可以將斷言看作是異常處理的一種高級(jí)形式。

斷言表示為一些布爾表達(dá)式,程序員相信在程序中的某個(gè)特定點(diǎn)該表達(dá)式值為真。

可以在任何時(shí)候啟用和禁用斷言驗(yàn)證,因此可以在測(cè)試時(shí)啟用斷言,而在部署時(shí)禁用斷言。同樣,程序投入運(yùn)行后,最終用戶在遇到問(wèn)題時(shí)可以重新啟用斷言。

注意assert()是一個(gè)宏,而不是函數(shù)。

在STM32中,常常會(huì)看到類似代碼:

assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
assert_param(IS_ADC_SINGLE_DIFFERENTIAL(SingleDiff));

這是用來(lái)檢查函數(shù)傳入的參數(shù)的有效性。STM32中的assert_param默認(rèn)是不使用的,即:

2d8aa14a-5f65-11eb-8b86-12bb97331649.png

如果要使用,需要定義USE_FULL_ASSERT宏,并且需要自己實(shí)現(xiàn)assert_failed函數(shù)。特別的,使用STM32CubeMX生成代碼的話,會(huì)在main.c生成:

2fa39234-5f65-11eb-8b86-12bb97331649.png

我們?cè)谶@進(jìn)行填充就好。

下面分享一下assert的應(yīng)用例子:

//公眾號(hào):嵌入式大雜燴
#include
#include

intmain(void)
{
inta,b,c;
printf("請(qǐng)輸入b, c的值:");
scanf("%d%d",&b,&c);
a=b/c;
printf("a=%d",a);
return0;
}

此處,變量c作為分母是不能等于0,如果我們輸入2 0,結(jié)果是什么呢?結(jié)果是程序會(huì)蹦:

303ac62c-5f65-11eb-8b86-12bb97331649.png

這個(gè)例子中只有幾行代碼,我們很快就可以找到程序蹦的原因就是變量c的值為0。但是,如果代碼量很大,我們還能這么快的找到問(wèn)題點(diǎn)嗎?

這時(shí)候,assert()就派上用場(chǎng)了,以上代碼中,我們可以在a = b / c;這句代碼之前加上assert(c);這句代碼用來(lái)判斷變量c的有效性。此時(shí),再編譯運(yùn)行,得到的結(jié)果為:

30e2ed20-5f65-11eb-8b86-12bb97331649.png

可見(jiàn),程序蹦的同時(shí)還會(huì)在標(biāo)準(zhǔn)錯(cuò)誤流中打印一條錯(cuò)誤信息:

Assertion failed:c, file hello.c, line 12

這條信息包含了一些對(duì)我們查找bug很有幫助的信息:?jiǎn)栴}出在變量c,在hello.c文件的第12行。這么一來(lái),我們就可以迅速的定位到問(wèn)題點(diǎn)了。

這時(shí)候細(xì)心的朋友會(huì)發(fā)現(xiàn),上邊我們對(duì)assert()的介紹中,有這么一句說(shuō)明:

如果表達(dá)式的值為假,assert()宏就會(huì)調(diào)用_assert函數(shù)在標(biāo)準(zhǔn)錯(cuò)誤流中打印一條錯(cuò)誤信息,并調(diào)用abort()(abort()函數(shù)的原型在stdlib.h頭文件中)函數(shù)終止程序。

所以,針對(duì)我們這個(gè)例子,我們的assert()宏我們也可以用以下代碼來(lái)代替:

if(0==c)
{
puts("c的值不能為0,請(qǐng)重新輸入!");
abort();
}

這樣,也可以給我們起到提示的作用:

310ebf90-5f65-11eb-8b86-12bb97331649.png

但是,使用assert()至少有幾個(gè)好處:

1)能自動(dòng)標(biāo)識(shí)文件和出問(wèn)題的行號(hào)。

2)無(wú)需要更改代碼就能開(kāi)啟或關(guān)閉assert機(jī)制(開(kāi)不開(kāi)啟關(guān)系到程序大小的問(wèn)題)。如果認(rèn)為已經(jīng)排除了程序的bug,就可以把下面的宏定義寫(xiě)在包含assert.h的位置的前面:

#defineNDEBUG

并重新編譯程序,這樣編輯器就會(huì)禁用工程文件中所有的assert()語(yǔ)句。如果程序又出現(xiàn)問(wèn)題,可以移除這條#define指令(或把它注釋掉),然后重新編譯程序,這樣就可以重新啟用了assert()語(yǔ)句。

相關(guān)文章:【C語(yǔ)言筆記】assert()怎么用?

預(yù)處理指令

1、#error

#error"PleaseselectfirstthetargetSTM32L4xxdeviceusedinyourapplication(instm32l4xx.hfile)"

#error 指令讓預(yù)處理器發(fā)出一條錯(cuò)誤信息,并且會(huì)中斷編譯過(guò)程。

#error的例子:

//公眾號(hào):嵌入式大雜燴
#include

#defineRX_BUF_IDX100

#ifRX_BUF_IDX==0
staticconstunsignedintrtl8139_rx_config=0;
#elifRX_BUF_IDX==1
staticconstunsignedintrtl8139_rx_config=1;
#elifRX_BUF_IDX==2
staticconstunsignedintrtl8139_rx_config=2;
#elifRX_BUF_IDX==3
staticconstunsignedintrtl8139_rx_config=3;
#else
#error"Invalidconfigurationfor8139_RXBUF_IDX"
#endif

intmain(void)
{
printf("helloworld
");
return0;
}

這段示例代碼很簡(jiǎn)單,當(dāng)RX_BUF_IDX宏的值不為0~3時(shí),在預(yù)處理階段就會(huì)通過(guò)#error 指令輸出一條錯(cuò)誤提示信息:

"Invalid configuration for 8139_RXBUF_IDX"

下面編譯看一看結(jié)果:

31e7c60a-5f65-11eb-8b86-12bb97331649.png

2、#if、#elif、#else、#endif、#ifdef、#ifndef

(1)#if

#if(USE_HAL_ADC_REGISTER_CALLBACKS==1)
void(*ConvCpltCallback)(struct__ADC_HandleTypeDef*hadc);
//......
#endif/*USE_HAL_ADC_REGISTER_CALLBACKS*/

#if的使用一般使用格式如下

#if整型常量表達(dá)式1
程序段1
#elif整型常量表達(dá)式2
程序段2
#else
程序段3
#endif

執(zhí)行起來(lái)就是,如果整形常量表達(dá)式為真,則執(zhí)行程序段1,以此類推,最后#endif是#if的結(jié)束標(biāo)志。

(2)#ifdef、#ifndef

#ifdefHAL_RTC_MODULE_ENABLED
#include"stm32l4xx_hal_rtc.h"
#endif/*HAL_RTC_MODULE_ENABLED*/

#ifdef的作用是判斷某個(gè)宏是否定義,如果該宏已經(jīng)定義則執(zhí)行后面的代碼,一般使用格式如下:

#ifdef宏名
程序段1
#else
程序段2
#endif

它的意思是,如果該宏已被定義過(guò),則對(duì)程序段1進(jìn)行編譯,否則對(duì)程序段2進(jìn)行編譯,通#if一樣,#endif也是#ifdef的結(jié)束標(biāo)志。

#ifndef__STM32L4xx_HAL_ADC_EX_H
#define__STM32L4xx_HAL_ADC_EX_H
//......
#endif

#ifndef的作用與#ifdef的作用相反,用于判斷某個(gè)宏是否沒(méi)被定義。

(3)#if defined、#if !defined

defined用于判斷某個(gè)宏是否被定義, !defined與defined的作用相反。這樣一來(lái)#if defined可以達(dá)到與#ifdef一樣的效果。如例子:

#ifdefined(STM32L412xx)
#include"stm32l412xx.h"
#elifdefined(STM32L422xx)
#include"stm32l422xx.h"
//........
#elifdefined(STM32L4S9xx)
#include"stm32l4s9xx.h"
#else
#error"PleaseselectfirstthetargetSTM32L4xxdeviceusedinyourapplication(instm32l4xx.hfile)"
#endif

如果STM32L412xx宏被定義,則包含頭文件stm32l412xx.h,以此類推。

既然已經(jīng)有#ifdef、#ifndef了,#if defined與#if !defined是否是多余的?

不是的,#ifdef和#ifndef僅能一次判斷一個(gè)宏名,而defined能做到一次判斷多個(gè)宏名,例如:

#ifdefined(STM32L4R5xx)||defined(STM32L4R7xx)||defined(STM32L4R9xx)||defined(STM32L4S5xx)||defined(STM32L4S7xx)||defined(STM32L4S9xx)
//......
#endif/*STM32L4R5xx||STM32L4R7xx||STM32L4R9xx||STM32L4S5xx||STM32L4S7xx||STM32L4S9xx*/

更進(jìn)一步,可以構(gòu)建一些更密切地因果處理,如:

#ifdefined(__ARMCC_VERSION)&&(__ARMCC_VERSION
#definePI(3.14)
#defineR(6)

#ifdefined(PI)&&defined(R)
#defineAREA(PI*R*R)
#endif

3、#pragma指令

#pragma指令為我們提供了讓編譯器執(zhí)行某些特殊操作提供了一種方法。這條指令對(duì)非常大的程序或需要使用特定編譯器的特殊功能的程序非常有用。

#pragma指令的一般形式為:#pragma para,其中,para為參數(shù)。如

#ifdefined(__GNUC__)
#pragmaGCCdiagnosticpush
#pragmaGCCdiagnosticignored"-Wsign-conversion"
#pragmaGCCdiagnosticignored"-Wconversion"
#pragmaGCCdiagnosticignored"-Wunused-parameter"
#endif

這一段的作用是忽略一些gcc的警告。#pragma命令中出現(xiàn)的命令集在不同的編譯器上是不一樣的,使用時(shí)必須查閱所使用的編譯器的文檔來(lái)了解有哪些命令、以及這些命令的功能。

下面簡(jiǎn)單看一下#pragma命令的常見(jiàn)用法。

(1)、#pragma pack

我們可以利用#pragma pack來(lái)改變編譯器的對(duì)齊方式:

#pragmapack(n)/*指定按n字節(jié)對(duì)齊*/
#pragmapack()/*取消自定義字節(jié)對(duì)齊*/

我們使用#pragma pack指令來(lái)指定對(duì)齊的字節(jié)數(shù)。例子:

①指定按1字節(jié)對(duì)齊

32690288-5f65-11eb-8b86-12bb97331649.png

運(yùn)行結(jié)果為:

32e162be-5f65-11eb-8b86-12bb97331649.png

②指定2字節(jié)對(duì)齊

3344df38-5f65-11eb-8b86-12bb97331649.png

運(yùn)行結(jié)果為:

34721150-5f65-11eb-8b86-12bb97331649.png

可見(jiàn),指定的對(duì)齊的字節(jié)數(shù)不一樣,得到的結(jié)果也不一樣。指定對(duì)齊有什么用呢,大概就是可以避免了移植過(guò)程中編譯器的差異帶來(lái)的代碼隱患吧。比如兩個(gè)編譯器的默認(rèn)對(duì)齊方式不一樣,那可能會(huì)帶來(lái)一些bug。

(2)#pragma message

該指令用于在預(yù)處理過(guò)程中輸出一些有用的提示信息,如:

3504cc66-5f65-11eb-8b86-12bb97331649.png

運(yùn)行結(jié)果為:

356976f2-5f65-11eb-8b86-12bb97331649.png

如上,我們平時(shí)可以在一些條件編譯塊中加上類似信息,因?yàn)樵谝恍┖赀x擇較多的情況下,可能會(huì)導(dǎo)致代碼理解起來(lái)會(huì)混亂。不過(guò)現(xiàn)在一些編譯器、編輯器都會(huì)對(duì)這些情況進(jìn)行一些很明顯的區(qū)分了,比如哪塊代碼沒(méi)有用到,那塊代碼的背景色就會(huì)是灰色的。

(3)#pragma warning

該指令允許選擇性地修改編譯器警告信息。

例子:

#pragmawarning(disable:450734;once:4385;error:164)

等價(jià)于:

#pragmawarning(disable:450734)//不顯示4507和34號(hào)警告信息
#pragmawarning(once:4385)//4385號(hào)警告信息僅報(bào)告一次
#pragmawarning(error:164)//把164號(hào)警告信息作為一個(gè)錯(cuò)

這個(gè)指令暫且了解這么多,知道有這么一回事就可以。

關(guān)于#pragma指令還有很多用法,但比較冷門(mén),這里暫且不列舉,有興趣的朋友可以自行學(xué)習(xí)。

相關(guān)文章:認(rèn)識(shí)認(rèn)識(shí)#pragma、#error指令

extern "C"

#ifndef__STM32L4S7xx_H
#define__STM32L4S7xx_H

#ifdef__cplusplus
extern"C"{
#endif/*__cplusplus*/

#ifdef__cplusplus
}
#endif/*__cplusplus*/

#endif/*__STM32L4S7xx_H*/

加上extern "C"后,會(huì)指示編譯器這部分代碼按C語(yǔ)言(而不是C++)的方式進(jìn)行編譯。因?yàn)镃、C++編譯器對(duì)函數(shù)的編譯處理是不完全相同的,尤其對(duì)于C++來(lái)說(shuō),支持函數(shù)的重載,編譯后的函數(shù)一般是以函數(shù)名和形參類型來(lái)命名的。

例如函數(shù)void fun(int, int),編譯后的可能是_fun_int_int(不同編譯器可能不同,但都采用了類似的機(jī)制,用函數(shù)名和參數(shù)類型來(lái)命名編譯后的函數(shù)名);而C語(yǔ)言沒(méi)有類似的重載機(jī)制,一般是利用函數(shù)名來(lái)指明編譯后的函數(shù)名的,對(duì)應(yīng)上面的函數(shù)可能會(huì)是_fun這樣的名字。

相關(guān)文章:干貨 | extern "C"的用法解析

#與##運(yùn)算符

#define__STM32_PIN(index,gpio,gpio_index)
{
index,GPIO##gpio##_CLK_ENABLE,GPIO##gpio,GPIO_PIN_##gpio_index
}

1、#運(yùn)算符

我們平時(shí)使用帶參宏時(shí),字符串中的宏參數(shù)是沒(méi)有被替換的。例如:

35aeaa24-5f65-11eb-8b86-12bb97331649.jpg

輸出結(jié)果為:

36103dde-5f65-11eb-8b86-12bb97331649.jpg

然而,我們期望輸出的結(jié)果是:

5+20=25
13+14=27

這該怎么做呢?其實(shí),C語(yǔ)言允許在字符串中包含宏參數(shù)。在類函數(shù)宏(帶參宏)中,#號(hào)作為一個(gè)預(yù)處理運(yùn)算符,可以把記號(hào)轉(zhuǎn)換成字符串。

例如,如果A是一個(gè)宏形參,那么#A就是轉(zhuǎn)換為字符串"A"的形參名。這個(gè)過(guò)程稱為字符串化(stringizing)。以下程序演示這個(gè)過(guò)程:

3659ffc8-5f65-11eb-8b86-12bb97331649.jpg

輸出結(jié)果為:

368ee36e-5f65-11eb-8b86-12bb97331649.jpg

這就達(dá)到我們想要的結(jié)果了。所以,#運(yùn)算符可以完成字符串化(stringizing)的過(guò)程。

2、##運(yùn)算符

與#運(yùn)算符類似,##運(yùn)算符可用于類函數(shù)宏(帶參宏)的替換部分。##運(yùn)算符可以把兩個(gè)記號(hào)組合成一個(gè)記號(hào)。例如,可以這樣做:

#defineXNAME(n)x##n

然后,宏XNAME(4)將展開(kāi)x4。以下程序演示##運(yùn)算符的用法:

39f2fc7a-5f65-11eb-8b86-12bb97331649.jpg

輸出結(jié)果為:

3d91189e-5f65-11eb-8b86-12bb97331649.jpg

注意:PRINT_XN()宏用#運(yùn)算符組合字符串,##運(yùn)算符把記號(hào)組合為一個(gè)新的標(biāo)識(shí)符。

其實(shí),##運(yùn)算符在這里看來(lái)并沒(méi)有起到多大的便利,反而會(huì)讓我們感覺(jué)到不習(xí)慣。但是,使用##運(yùn)算符有時(shí)候是可以提高封裝性及程序的可讀性的。

相關(guān)文章:這兩個(gè)C運(yùn)算符你可能沒(méi)用過(guò),但卻很有用~

_IO、 _I、 _O、volatile

一些底層結(jié)構(gòu)體成員中,常常使用_IO、 _O、 _I這三個(gè)宏來(lái)修飾,如:

typedefstruct
{
__IOuint32_tTIR;/*!

而這三個(gè)宏其實(shí)是volatile的替換,即:

#define__Ivolatile/*!

volatile的作用就是不讓編譯器進(jìn)行優(yōu)化,即每次讀取或者修改值的時(shí)候,都必須重新從內(nèi)存或者寄存器中讀取或者修改。在我們嵌入式中, volatile 用在如下的幾個(gè)地方:

中斷服務(wù)程序中修改的供其它程序檢測(cè)的變量需要加 volatile;

多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)該加 volatile;

存儲(chǔ)器映射的硬件寄存器通常也要加 volatile 說(shuō)明,因?yàn)槊看螌?duì)它的讀寫(xiě)都可能由不 同意義;

例如:

/*假設(shè)REG為寄存器的地址*/
uint32*REG;
*REG=0;/*點(diǎn)燈*/
*REG=1;/*滅燈*/

此時(shí)若是REG不加volatile進(jìn)行修飾,則點(diǎn)燈操作將被優(yōu)化掉,只執(zhí)行滅燈操作。

位操作

STM32中,使用外設(shè)都得先配置其相關(guān)寄存器,都是使用一些位操作。比如庫(kù)函數(shù)的內(nèi)部實(shí)現(xiàn)就是一些位操作:

staticvoidTI4_Config(TIM_TypeDef*TIMx,uint16_tTIM_ICPolarity,uint16_tTIM_ICSelection,
uint16_tTIM_ICFilter)
{
uint16_ttmpccmr2=0,tmpccer=0,tmp=0;

/*DisabletheChannel4:ResettheCC4EBit*/
TIMx->CCER&=(uint16_t)~TIM_CCER_CC4E;
tmpccmr2=TIMx->CCMR2;
tmpccer=TIMx->CCER;
tmp=(uint16_t)(TIM_ICPolarity<CCMR2=tmpccmr2;
TIMx->CCER=tmpccer;
}

看似很復(fù)雜,其實(shí)就是按照規(guī)格書(shū)來(lái)配置就可以。雖然實(shí)際應(yīng)用中,很少會(huì)采用直接配置寄存器的方法來(lái)使用,但是也需要掌握,一些特殊的地方可以直接操控寄存器,比如中斷中。

位操作簡(jiǎn)單例子:

首先,以下是按位運(yùn)算符:

3dd5a702-5f65-11eb-8b86-12bb97331649.png

在嵌入式編程中,常常需要對(duì)一些寄存器進(jìn)行配置,有的情況下需要改變一個(gè)字節(jié)中的某一位或者幾位,但是又不想改變其它位原有的值,這時(shí)就可以使用按位運(yùn)算符進(jìn)行操作。下面進(jìn)行舉例說(shuō)明,假如有一個(gè)8位的TEST寄存器:

3e4bb1fe-5f65-11eb-8b86-12bb97331649.png

當(dāng)我們要設(shè)置第0位bit0的值為1時(shí),可能會(huì)這樣進(jìn)行設(shè)置:

TEST=0x01;

但是,這樣設(shè)置是不夠準(zhǔn)確的,因?yàn)檫@時(shí)候已經(jīng)同時(shí)操作到了高7位:bit1~bit7,如果這高7位沒(méi)有用到的話,這么設(shè)置沒(méi)有什么影響;但是,如果這7位正在被使用,結(jié)果就不是我們想要的了。

在這種情況下,我們就可以借用按位操作運(yùn)算符進(jìn)行配置。

對(duì)于二進(jìn)制位操作來(lái)說(shuō),不管該位原來(lái)的值是0還是1,它跟0進(jìn)行&運(yùn)算,得到的結(jié)果都是0,而跟1進(jìn)行&運(yùn)算,將保持原來(lái)的值不變;不管該位原來(lái)的值是0還是1,它跟1進(jìn)行|運(yùn)算,得到的結(jié)果都是1,而跟0進(jìn)行|運(yùn)算,將保持原來(lái)的值不變。

所以,此時(shí)可以設(shè)置為:

TEST=TEST|0x01;

其意義為:TEST寄存器的高7位均不變,最低位變成1了。在實(shí)際編程中,常改寫(xiě)為:

TEST|=0x01;

這種寫(xiě)法可以一定程度上簡(jiǎn)化代碼,是 C 語(yǔ)言常用的一種編程風(fēng)格。設(shè)置寄存器的某一位還有另一種操作方法,以上的等價(jià)方法如:

TEST|=(0x01<

第幾位要置1就左移幾位。

同樣的,要給TEST的低4位清0,高4位保持不變,可以進(jìn)行如下配置:

TEST&=0xF0;

相關(guān)文章:C語(yǔ)言、嵌入式位操作精華技巧大匯總

do {}while(0)

這是在宏定義中用的,STM32的標(biāo)準(zhǔn)庫(kù)中沒(méi)有使用這種用法,HAL庫(kù)中有大量的用法例子,如:

#define__HAL_FLASH_INSTRUCTION_CACHE_RESET()do{SET_BIT(FLASH->ACR,FLASH_ACR_ICRST);
CLEAR_BIT(FLASH->ACR,FLASH_ACR_ICRST);
}while(0)

下面以一個(gè)例子來(lái)分析do {}while(0)的用法:

//公眾號(hào):嵌入式大雜燴
#defineDEBUG1

#ifDEBUG
#defineDBG_PRINTF(fmt,args...)
{
printf("<>",__FILE__,__LINE__,__FUNCTION__);
printf(fmt,##args);
}
#else
#defineDBG_PRINTF(fmt,args...)
#endif

這個(gè)宏打印有什么缺陷?

我們與if、else使用的時(shí)候,會(huì)有這樣的一種使用情況:

3eb2b3c2-5f65-11eb-8b86-12bb97331649.png

此時(shí)會(huì)報(bào)語(yǔ)法錯(cuò)誤。為什么呢?

同樣的,我們可以先來(lái)看一下我們的demo代碼預(yù)處理過(guò)后,相應(yīng)的宏代碼會(huì)被轉(zhuǎn)換為什么。如:

3ef6807a-5f65-11eb-8b86-12bb97331649.png

這里我們可以看到,我們的if、else結(jié)構(gòu)代碼被替換為如下形式:

if(c)
{/*.......*/};
else
{/*.......*/};

顯然,出現(xiàn)了語(yǔ)法錯(cuò)誤。if之后的大括號(hào)之后不能加分號(hào),這里的分號(hào)其實(shí)可以看做一條空語(yǔ)句,這個(gè)空語(yǔ)句會(huì)把if與else給分隔開(kāi)來(lái),導(dǎo)致else不能正確匹配到if,導(dǎo)致語(yǔ)法錯(cuò)誤。

為了解決這個(gè)問(wèn)題,有幾種方法。第一種方法是:把分號(hào)去掉。代碼變成:

3fcd7b2a-5f65-11eb-8b86-12bb97331649.png

第二種方法是:在if之后使用DBG_PRINTF打印調(diào)試時(shí)總是加{}。代碼變成:

400db794-5f65-11eb-8b86-12bb97331649.png

以上兩種方法都可以正常編譯、運(yùn)行了。

但是,我們C語(yǔ)言中,每條語(yǔ)句往往以分號(hào)結(jié)尾;并且,總有些人習(xí)慣在if判斷之后只有一條語(yǔ)句的情況下不加大括號(hào);而且我們創(chuàng)建的DBG_PRINTF宏函數(shù)的目的就是為了對(duì)標(biāo)printf函數(shù),printf函數(shù)的使用加分號(hào)在任何地方的使用都是沒(méi)有問(wèn)題的。

基于這幾個(gè)原因,我們有必要再對(duì)我們的DBG_PRINTF宏函數(shù)進(jìn)行一個(gè)改造。

下面引入do{}while(0)來(lái)對(duì)我們的DBG_PRINTF進(jìn)行一個(gè)簡(jiǎn)單的改造。改造后的DBG_PRINTF宏函數(shù)如下:

#defineDBG_PRINTF(fmt,args...)
do
{
printf("<>",__FILE__,__LINE__,__FUNCTION__);
printf(fmt,##args);
}while(0)

這里的do...while循環(huán)的循環(huán)體只執(zhí)行一次,與不加循環(huán)是效果一樣。并且,可以避免了上面的問(wèn)題。預(yù)處理文件:

4070f78c-5f65-11eb-8b86-12bb97331649.png

我們的宏函數(shù)實(shí)體中,while(0)后面不加分號(hào),在實(shí)際調(diào)用時(shí)補(bǔ)上分號(hào),既符合了C語(yǔ)言語(yǔ)句分號(hào)結(jié)尾的習(xí)慣,也符合了do...while的語(yǔ)法規(guī)則。

使用do{}while(0)來(lái)封裝宏函數(shù)可能會(huì)讓很多初學(xué)者看著不習(xí)慣,但必須承認(rèn)的是,這確確實(shí)實(shí)是一種很常用的方法。

推薦文章:C語(yǔ)言、嵌入式中幾個(gè)非常實(shí)用的宏技巧

static與extern

1、static

40eb53a6-5f65-11eb-8b86-12bb97331649.png

static主要有三種用法:在函數(shù)內(nèi)用于修飾變量、用于修飾函數(shù)、用于修飾本.c文件全局變量。后兩個(gè)容易理解,用于修飾函數(shù)與全局變量表明變量與函數(shù)在本模塊內(nèi)使用。

下面看看static在函數(shù)內(nèi)用于修飾變量的例子:

//公眾號(hào):嵌入式大雜燴
#include

voidtest(void)
{
intnormal_var=0;
staticintstatic_var=0;

printf("normal_var:%dstatic_var:%d
",normal_var,static_var);
normal_var++;
static_var++;
}

intmain(void)
{
inti;

for(i=0;i

運(yùn)行結(jié)果:

4165b448-5f65-11eb-8b86-12bb97331649.jpg

可以看出,函數(shù)每次被調(diào)用,普通局部變量都是重新分配,而static修飾的變量保持上次調(diào)用的值不變,即只被初始化一次。

2、extern

extern的用法簡(jiǎn)單,用于聲明多個(gè)模塊共享的全局變量、聲明外部函數(shù)。

42255c58-5f65-11eb-8b86-12bb97331649.png

責(zé)任編輯:xj

原文標(biāo)題:STM32中C語(yǔ)言知識(shí)點(diǎn):初學(xué)者必看,老鳥(niǎo)復(fù)習(xí)(長(zhǎng)文總結(jié))

文章出處:【微信公眾號(hào):嵌入式ARM】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • STM32
    +關(guān)注

    關(guān)注

    2309

    文章

    11162

    瀏覽量

    373431
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    183

    文章

    7644

    瀏覽量

    145581
  • 開(kāi)發(fā)板
    +關(guān)注

    關(guān)注

    26

    文章

    6291

    瀏覽量

    118075

原文標(biāo)題:STM32中C語(yǔ)言知識(shí)點(diǎn):初學(xué)者必看,老鳥(niǎo)復(fù)習(xí)(長(zhǎng)文總結(jié))

文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    手把手教你學(xué)51單片機(jī)-C語(yǔ)言

    基本語(yǔ)法開(kāi)始講解,逐步深入,講解單片機(jī)內(nèi)部資源和C語(yǔ)言的各種用法,并穿插介紹實(shí)際項(xiàng)目開(kāi)發(fā)常用的電路設(shè)計(jì)思路和編程技巧等。本書(shū)在知識(shí)講解的過(guò)程,有些
    發(fā)表于 03-05 11:47

    C語(yǔ)言C++的區(qū)別及聯(lián)系

    缺點(diǎn):性能比面向過(guò)程低。 二、具體語(yǔ)言上的區(qū)別 1、關(guān)鍵字的不同 C語(yǔ)言有32個(gè)關(guān)鍵字;C++有63個(gè)關(guān)鍵字。 2、后綴名不同 C
    發(fā)表于 12-24 07:23

    C語(yǔ)言單元測(cè)試在嵌入式軟件開(kāi)發(fā)的作用及專業(yè)工具的應(yīng)用

    一、C語(yǔ)言在嵌入式開(kāi)發(fā)的現(xiàn)狀與重要C語(yǔ)言在嵌入式開(kāi)發(fā)領(lǐng)域仍然占據(jù)主導(dǎo)地位,根據(jù)Barr集團(tuán)
    發(fā)表于 12-18 11:46

    單片機(jī)C語(yǔ)言編程的心得

    寫(xiě)這個(gè)8*8按鍵程序的過(guò)程,不管是在自己寫(xiě)還是參考別人程序的過(guò)程,發(fā)現(xiàn)自己對(duì)C語(yǔ)言有些基本知識(shí)點(diǎn)和編程規(guī)范有很多不懂的地方,有些是自己以
    發(fā)表于 12-08 07:44

    C語(yǔ)言特性

    1、高效性:直接操作硬件 C 語(yǔ)言代碼的執(zhí)行效率極高,這是其最為顯著的優(yōu)勢(shì)之一。它能夠直接訪問(wèn)硬件資源,與底層硬件進(jìn)行緊密交互,充分發(fā)揮硬件的性能潛力。在嵌入式開(kāi)發(fā),硬件資源往往十分有限,對(duì)程序
    發(fā)表于 11-24 07:01

    C語(yǔ)言在嵌入式開(kāi)發(fā)的應(yīng)用

    穩(wěn)定性控制系統(tǒng)(VSC)等關(guān)鍵部件的開(kāi)發(fā),C 語(yǔ)言都發(fā)揮著至關(guān)重要的作用。 以工業(yè)自動(dòng)化生產(chǎn)線的運(yùn)動(dòng)控制系統(tǒng)為例,C
    發(fā)表于 11-21 08:09

    C語(yǔ)言和單片機(jī)C語(yǔ)言有什么差異

    單片機(jī)c語(yǔ)言相對(duì)于普通C語(yǔ)言增加了一些基本的指令,還有變量的賦值是16進(jìn)制,當(dāng)然單片機(jī)c語(yǔ)言只牽
    發(fā)表于 11-14 07:55

    C語(yǔ)言的printf基本用法介紹

    個(gè)小數(shù)。f 是 float 的簡(jiǎn)寫(xiě)。 除了這些,printf 支持更加復(fù)雜和優(yōu)美的輸出格式,考慮到讀者的基礎(chǔ)暫時(shí)不夠,我們將在《C語(yǔ)言數(shù)據(jù)輸出大匯總以及輕量進(jìn)階》一節(jié)展開(kāi)講解。 我們把代碼補(bǔ)充完整
    發(fā)表于 11-12 07:04

    C語(yǔ)言精彩編程百例-364頁(yè)

    大量經(jīng)驗(yàn),從而可以熟練地進(jìn)行C程序設(shè)計(jì)。 全文共分為四篇,全面、系統(tǒng)地講述了C語(yǔ)言各個(gè)方面的知識(shí)點(diǎn)和程序設(shè)計(jì)的基本方法,以及編寫(xiě)程序過(guò)程中值得注意的地方,內(nèi)容深入淺出,通俗易懂。對(duì)于
    發(fā)表于 06-13 17:28

    主流的 MCU 開(kāi)發(fā)語(yǔ)言為什么是 C 而不是 C++?

    在單片機(jī)的地界兒里,C語(yǔ)言穩(wěn)坐中軍帳,C++想分杯羹?難嘍。咱電子工程師天天跟那針尖大的內(nèi)存空間較勁,C++那些花里胡哨的玩意兒,在這兒真玩不轉(zhuǎn)。先說(shuō)內(nèi)存這道坎兒。您當(dāng)
    的頭像 發(fā)表于 05-21 10:33 ?1040次閱讀
    主流的 MCU 開(kāi)發(fā)<b class='flag-5'>語(yǔ)言</b>為什么是 <b class='flag-5'>C</b> 而不是 <b class='flag-5'>C</b>++?

    C51單片機(jī)及C語(yǔ)言知識(shí)點(diǎn)必備秘籍

    單片機(jī)關(guān)鍵知識(shí)點(diǎn)一覽: 系列一 1:?jiǎn)纹瑱C(jī)簡(jiǎn)敘 2:?jiǎn)纹瑱C(jī)引腳介紹 3:?jiǎn)纹瑱C(jī)存儲(chǔ)器結(jié)構(gòu) 4:第一個(gè)單片機(jī)小程序 5:?jiǎn)纹瑱C(jī)延時(shí)程序分析 6:?jiǎn)纹瑱C(jī)并行口結(jié)構(gòu) 7:?jiǎn)纹瑱C(jī)的特殊
    發(fā)表于 05-15 14:00

    深入理解C語(yǔ)言C語(yǔ)言循環(huán)控制

    C語(yǔ)言編程,循環(huán)結(jié)構(gòu)是至關(guān)重要的,它可以讓程序重復(fù)執(zhí)行特定的代碼塊,從而提高編程效率。然而,為了避免程序進(jìn)入無(wú)限循環(huán),C
    的頭像 發(fā)表于 04-29 18:49 ?2044次閱讀
    深入理解<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>:<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>循環(huán)控制

    電機(jī)選型計(jì)算公式與知識(shí)點(diǎn)匯總

    純分享帖,需要者可點(diǎn)擊附件獲取完整資料~~~*附件:電機(jī)選型計(jì)算公式與知識(shí)點(diǎn)匯總.pdf 【免責(zé)聲明】?jī)?nèi)容轉(zhuǎn)自今日電機(jī),因轉(zhuǎn)載眾多,無(wú)法確認(rèn)真正原始作者,故僅標(biāo)明轉(zhuǎn)載來(lái)源。版權(quán)歸原出處所有,純分享帖,侵權(quán)請(qǐng)聯(lián)系刪除內(nèi)容以保證您的權(quán)益。
    發(fā)表于 04-29 16:10

    如何在 樹(shù)莓派 上編寫(xiě)和運(yùn)行 C 語(yǔ)言程序?

    在本教程,我將討論C編程語(yǔ)言是什么,C編程的用途,以及如何在RaspberryPi上編寫(xiě)和運(yùn)行C程序。本文的目的是為您介紹在Raspber
    的頭像 發(fā)表于 03-25 09:28 ?1156次閱讀
    如何在 樹(shù)莓派 上編寫(xiě)和運(yùn)行 <b class='flag-5'>C</b> <b class='flag-5'>語(yǔ)言</b>程序?

    電氣工程師必知必會(huì)的100個(gè)電?知識(shí)點(diǎn)分享

    電??程師也都是從電?學(xué)徒??步?步積累成長(zhǎng)起來(lái)的。積跬步?千?,匯細(xì)流成江海!朋友們,現(xiàn)在讓我們??個(gè)捷徑,花半個(gè)?時(shí)的時(shí)間來(lái)積累100個(gè)必知必會(huì)的電?知識(shí)點(diǎn)吧!
    的頭像 發(fā)表于 03-14 11:05 ?1849次閱讀