STM32中C語言知識點:初學者必看,老鳥複習(長文總結)

2021-02-14 嵌入式Linux


說在前面的話

一位初學單片機的小夥伴讓我推薦C語言書籍,因為C語言基礎比較差,想把C語言重新學一遍,再去學單片機,我以前剛學單片機的時候也有這樣子的想法。

其實C語言是可以邊學單片機邊學的,學單片機的一些例程中,遇到不懂的C語言知識,再去查相關的知識點,這樣印象才會深刻些。

下面就列出了一些STM32中重要的C語言知識點,初學的小夥伴可以多讀幾遍,其中大多知識點之前都有寫過,這裡重新整理一下,更詳細地分析解釋可以閱讀附帶的連結。

assert_param

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

斷言表示為一些布爾表達式,程式設計師相信在程序中的某個特定點該表達式值為真。

可以在任何時候啟用和禁用斷言驗證,因此可以在測試時啟用斷言,而在部署時禁用斷言。同樣,程序投入運行後,最終用戶在遇到問題時可以重新啟用斷言。

注意assert()是一個宏,而不是函數。

在STM32中,常常會看到類似代碼:

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

這是用來檢查函數傳入的參數的有效性。STM32中的assert_param默認是不使用的,即:


如果要使用,需要定義USE_FULL_ASSERT宏,並且需要自己實現assert_failed函數。特別的,使用STM32CubeMX生成代碼的話,會在main.c生成:


我們在這進行填充就好。

下面分享一下assert的應用例子:

// 公眾號:嵌入式大雜燴
#include 
#include 
 
int main(void)
{
 int a, b, c;
 printf("請輸入b, c的值:");
 scanf("%d %d", &b, &c);
 a = b / c;
 printf("a = %d", a);
 return 0;
}

此處,變量c作為分母是不能等於0,如果我們輸入2 0,結果是什麼呢?結果是程序會蹦:


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

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


可見,程序蹦的同時還會在標準錯誤流中列印一條錯誤信息:

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

這條信息包含了一些對我們查找bug很有幫助的信息:問題出在變量c,在hello.c文件的第12行。這麼一來,我們就可以迅速的定位到問題點了。

這時候細心的朋友會發現,上邊我們對assert()的介紹中,有這麼一句說明:

如果表達式的值為假,assert()宏就會調用_assert函數在標準錯誤流中列印一條錯誤信息,並調用abort()(abort()函數的原型在stdlib.h頭文件中)函數終止程序。

所以,針對我們這個例子,我們的assert()宏我們也可以用以下代碼來代替:

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

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


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

1)能自動標識文件和出問題的行號。

2)無需要更改代碼就能開啟或關閉assert機制(開不開啟關係到程序大小的問題)。如果認為已經排除了程序的bug,就可以把下面的宏定義寫在包含assert.h的位置的前面:

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

相關文章:【C語言筆記】assert()怎麼用?

預處理指令1、#error#error "Please select first the target STM32L4xx device used in your application (in stm32l4xx.h file)"

#error 指令讓預處理器發出一條錯誤信息,並且會中斷編譯過程。

#error的例子:

// 公眾號:嵌入式大雜燴
#include 

#define  RX_BUF_IDX  100

#if RX_BUF_IDX == 0
static const unsigned int rtl8139_rx_config = 0;
#elif RX_BUF_IDX == 1
static const unsigned int rtl8139_rx_config = 1;
#elif RX_BUF_IDX == 2
static const unsigned int rtl8139_rx_config = 2;
#elif RX_BUF_IDX == 3
static const unsigned int rtl8139_rx_config = 3;
#else
#error "Invalid configuration for 8139_RXBUF_IDX"
#endif

int main(void)
{
 printf("hello world\n");
 return 0;
}

這段示例代碼很簡單,當RX_BUF_IDX宏的值不為0~3時,在預處理階段就會通過#error 指令輸出一條錯誤提示信息:

"Invalid configuration for 8139_RXBUF_IDX"

下面編譯看一看結果:

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 整型常量表達式1
  程序段1
#elif 整型常量表達式2
  程序段2
#else
  程序段3
#endif

執行起來就是,如果整形常量表達式為真,則執行程序段1,以此類推,最後#endif是#if的結束標誌。

(2)#ifdef、#ifndef#ifdef HAL_RTC_MODULE_ENABLED
  #include "stm32l4xx_hal_rtc.h"
#endif /* HAL_RTC_MODULE_ENABLED */

#ifdef的作用是判斷某個宏是否定義,如果該宏已經定義則執行後面的代碼,一般使用格式如下:

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

它的意思是,如果該宏已被定義過,則對程序段1進行編譯,否則對程序段2進行編譯,通#if一樣,#endif也是#ifdef的結束標誌。

#ifndef __STM32L4xx_HAL_ADC_EX_H
#define __STM32L4xx_HAL_ADC_EX_H
// .
#endif

#ifndef的作用與#ifdef的作用相反,用於判斷某個宏是否沒被定義。

(3)#if defined、#if !defined

defined用於判斷某個宏是否被定義, !defined與defined的作用相反。這樣一來#if defined可以達到與#ifdef一樣的效果。如例子:

#if defined(STM32L412xx)
  #include "stm32l412xx.h"
#elif defined(STM32L422xx)
  #include "stm32l422xx.h"
//...
#elif defined(STM32L4S9xx)
  #include "stm32l4s9xx.h"
#else
 #error "Please select first the target STM32L4xx device used in your application (in stm32l4xx.h file)"
#endif

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

既然已經有#ifdef、#ifndef了,#if defined與#if !defined是否是多餘的?

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

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

更進一步,可以構建一些更密切地因果處理,如:

#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677)
  #error "Please use ARM Compiler Toolchain V4.0.677 or later!"
#endif
#define PI (3.14)
#define R  (6)
 
#if defined(PI) && defined(R) 
#define AREA (PI*R*R) 
#endif
3、#pragma指令

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

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

#if defined ( __GNUC__ )
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

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

下面簡單看一下#pragma命令的常見用法。

(1)、#pragma pack

我們可以利用#pragma pack來改變編譯器的對齊方式:

#pragma pack(n)  /* 指定按n字節對齊 */
#pragma pack()   /* 取消自定義字節對齊 */

我們使用#pragma pack指令來指定對齊的字節數。例子:

①指定按1位元組對齊


運行結果為:


②指定2位元組對齊


運行結果為:

可見,指定的對齊的字節數不一樣,得到的結果也不一樣。指定對齊有什麼用呢,大概就是可以避免了移植過程中編譯器的差異帶來的代碼隱患吧。比如兩個編譯器的默認對齊方式不一樣,那可能會帶來一些bug。

(2)#pragma message

該指令用於在預處理過程中輸出一些有用的提示信息,如:


運行結果為:

如上,我們平時可以在一些條件編譯塊中加上類似信息,因為在一些宏選擇較多的情況下,可能會導致代碼理解起來會混亂。不過現在一些編譯器、編輯器都會對這些情況進行一些很明顯的區分了,比如哪塊代碼沒有用到,那塊代碼的背景色就會是灰色的。

(3)#pragma warning

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

例子:

#pragma warning( disable : 4507 34; once : 4385; error : 164 )

等價於:

#pragma warning(disable:4507 34) // 不顯示4507和34號警告信息
#pragma warning(once:4385)       // 4385號警告信息僅報告一次
#pragma warning(error:164)       // 把164號警告信息作為一個錯

這個指令暫且了解這麼多,知道有這麼一回事就可以。

關於#pragma指令還有很多用法,但比較冷門,這裡暫且不列舉,有興趣的朋友可以自行學習。

相關文章:認識認識#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"後,會指示編譯器這部分代碼按C語言(而不是C++)的方式進行編譯。因為C、C++編譯器對函數的編譯處理是不完全相同的,尤其對於C++來說,支持函數的重載,編譯後的函數一般是以函數名和形參類型來命名的。

例如函數void fun(int, int),編譯後的可能是_fun_int_int(不同編譯器可能不同,但都採用了類似的機制,用函數名和參數類型來命名編譯後的函數名);而C語言沒有類似的重載機制,一般是利用函數名來指明編譯後的函數名的,對應上面的函數可能會是_fun這樣的名字。

相關文章:乾貨 | extern "C"的用法解析

#與##運算符#define __STM32_PIN(index, gpio, gpio_index) \
{ \
index, GPIO##gpio##_CLK_ENABLE, GPIO##gpio, GPIO_PIN_##gpio_index \
}
1、#運算符

我們平時使用帶參宏時,字符串中的宏參數是沒有被替換的。例如:


輸出結果為:


然而,我們期望輸出的結果是:

這該怎麼做呢?其實,C語言允許在字符串中包含宏參數。在類函數宏(帶參宏)中,#號作為一個預處理運算符,可以把記號轉換成字符串。

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


輸出結果為:


這就達到我們想要的結果了。所以,#運算符可以完成字符串化(stringizing)的過程。

2、##運算符

與#運算符類似,##運算符可用於類函數宏(帶參宏)的替換部分。##運算符可以把兩個記號組合成一個記號。例如,可以這樣做:

然後,宏XNAME(4)將展開x4。以下程序演示##運算符的用法:


輸出結果為:

注意:PRINT_XN()宏用#運算符組合字符串,##運算符把記號組合為一個新的標識符。

其實,##運算符在這裡看來並沒有起到多大的便利,反而會讓我們感覺到不習慣。但是,使用##運算符有時候是可以提高封裝性及程序的可讀性的。

相關文章:這兩個C運算符你可能沒用過,但卻很有用~

_IO、 _I、 _O、volatile

一些底層結構體成員中,常常使用_IO、 _O、 _I這三個宏來修飾,如:

typedef struct
{
  __IO uint32_t TIR;  /*!< CAN TX mailbox identifier register */
  __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */
  __IO uint32_t TDLR; /*!< CAN mailbox data low register */
  __IO uint32_t TDHR; /*!< CAN mailbox data high register */
} CAN_TxMailBox_TypeDef;


而這三個宏其實是volatile的替換,即:

#define     __I     volatile             /*!< Defines 'read only' permissions */
#define     __O     volatile             /*!< Defines 'write only' permissions */
#define     __IO    volatile             /*!< Defines 'read / write' permissions */

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

中斷服務程序中修改的供其它程序檢測的變量需要加 volatile;多任務環境下各任務間共享的標誌應該加 volatile;存儲器映射的硬體寄存器通常也要加 volatile 說明,因為每次對它的讀寫都可能由不 同意義;

例如:

/* 假設REG為寄存器的地址 */
uint32 *REG;
*REG = 0;  /* 點燈 */
*REG = 1;  /* 滅燈 */

此時若是REG不加volatile進行修飾,則點燈操作將被優化掉,只執行滅燈操作。

位操作

STM32中,使用外設都得先配置其相關寄存器,都是使用一些位操作。比如庫函數的內部實現就是一些位操作:

static void TI4_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection,
                       uint16_t TIM_ICFilter)
{
  uint16_t tmpccmr2 = 0, tmpccer = 0, tmp = 0;

  /* Disable the Channel 4: Reset the CC4E Bit */
  TIMx->CCER &= (uint16_t)~TIM_CCER_CC4E;
  tmpccmr2 = TIMx->CCMR2;
  tmpccer = TIMx->CCER;
  tmp = (uint16_t)(TIM_ICPolarity << 12);

  /* Select the Input and set the filter */
  tmpccmr2 &= ((uint16_t)~TIM_CCMR1_CC2S) & ((uint16_t)~TIM_CCMR1_IC2F);
  tmpccmr2 |= (uint16_t)(TIM_ICSelection << 8);
  tmpccmr2 |= (uint16_t)(TIM_ICFilter << 12);

  /* Select the Polarity and set the CC4E Bit */
  tmpccer &= (uint16_t)~(TIM_CCER_CC4P | TIM_CCER_CC4NP);
  tmpccer |= (uint16_t)(tmp | (uint16_t)TIM_CCER_CC4E);

  /* Write to TIMx CCMR2 and CCER registers */
  TIMx->CCMR2 = tmpccmr2;
  TIMx->CCER = tmpccer ;
}

看似很複雜,其實就是按照規格書來配置就可以。雖然實際應用中,很少會採用直接配置寄存器的方法來使用,但是也需要掌握,一些特殊的地方可以直接操控寄存器,比如中斷中。

位操作簡單例子:

首先,以下是按位運算符:


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


當我們要設置第0位bit0的值為1時,可能會這樣進行設置:

但是,這樣設置是不夠準確的,因為這時候已經同時操作到了高7位:bit1~bit7,如果這高7位沒有用到的話,這麼設置沒有什麼影響;但是,如果這7位正在被使用,結果就不是我們想要的了。

在這種情況下,我們就可以借用按位操作運算符進行配置。

對於二進位位操作來說,不管該位原來的值是0還是1,它跟0進行&運算,得到的結果都是0,而跟1進行&運算,將保持原來的值不變;不管該位原來的值是0還是1,它跟1進行|運算,得到的結果都是1,而跟0進行|運算,將保持原來的值不變。

所以,此時可以設置為:

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

這種寫法可以一定程度上簡化代碼,是 C 語言常用的一種編程風格。設置寄存器的某一位還有另一種操作方法,以上的等價方法如:

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

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

相關文章:C語言、嵌入式位操作精華技巧大匯總

do {}while(0)

這是在宏定義中用的,STM32的標準庫中沒有使用這種用法,HAL庫中有大量的用法例子,如:

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

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

// 公眾號:嵌入式大雜燴
#define  DEBUG   1  

#if DEBUG
  #define DBG_PRINTF(fmt, args...)  \
  {\
    printf("<> ", __FILE__, __LINE__, __FUNCTION__);\
    printf(fmt, ##args);\
  }
#else
  #define DBG_PRINTF(fmt, args...)   
#endif

這個宏列印有什麼缺陷?

我們與if、else使用的時候,會有這樣的一種使用情況:


此時會報語法錯誤。為什麼呢?

同樣的,我們可以先來看一下我們的demo代碼預處理過後,相應的宏代碼會被轉換為什麼。如:


這裡我們可以看到,我們的if、else結構代碼被替換為如下形式:

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

顯然,出現了語法錯誤。if之後的大括號之後不能加分號,這裡的分號其實可以看做一條空語句,這個空語句會把if與else給分隔開來,導致else不能正確匹配到if,導致語法錯誤。

為了解決這個問題,有幾種方法。第一種方法是:把分號去掉。代碼變成:


第二種方法是:在if之後使用DBG_PRINTF列印調試時總是加{}。代碼變成:


以上兩種方法都可以正常編譯、運行了。

但是,我們C語言中,每條語句往往以分號結尾;並且,總有些人習慣在if判斷之後只有一條語句的情況下不加大括號;而且我們創建的DBG_PRINTF宏函數的目的就是為了對標printf函數,printf函數的使用加分號在任何地方的使用都是沒有問題的。

基於這幾個原因,我們有必要再對我們的DBG_PRINTF宏函數進行一個改造。

下面引入do{}while(0)來對我們的DBG_PRINTF進行一個簡單的改造。改造後的DBG_PRINTF宏函數如下:

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

這裡的do...while循環的循環體只執行一次,與不加循環是效果一樣。並且,可以避免了上面的問題。預處理文件:


我們的宏函數實體中,while(0)後面不加分號,在實際調用時補上分號,既符合了C語言語句分號結尾的習慣,也符合了do...while的語法規則。

使用do{}while(0)來封裝宏函數可能會讓很多初學者看著不習慣,但必須承認的是,這確確實實是一種很常用的方法。

推薦文章:C語言、嵌入式中幾個非常實用的宏技巧

static與extern1、static


static主要有三種用法:在函數內用於修飾變量、用於修飾函數、用於修飾本.c文件全局變量。後兩個容易理解,用於修飾函數與全局變量表明變量與函數在本模塊內使用。

下面看看static在函數內用於修飾變量的例子:

// 公眾號:嵌入式大雜燴
#include 

void test(void)
{
    int normal_var = 0;
    static int static_var = 0;

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

int main(void)
{
     int i;

     for ( i = 0; i < 3; i++)
     {
       test();
     }

     return 0;
}

運行結果:


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

2、extern

extern的用法簡單,用於聲明多個模塊共享的全局變量、聲明外部函數。

相關焦點

  • 長文 | 花了兩天時間整理了STM32中的一些C語言知識點,初學者福利!老鳥複習
    說在前面的話 一位初學單片機的小夥伴讓我推薦C語言書籍,因為C語言基礎比較差,想把C語言重新學一遍,再去學單片機,我以前剛學單片機的時候也有這樣子的想法。其實C語言是可以邊學單片機邊學的,學單片機的一些例程中,遇到不懂的C語言知識,再去查相關的知識點,這樣印象才會深刻些。
  • 高三語文必修三文言文知識點歸納總結 高三語文一輪複習必看
    高三語文必修三文言文知識點歸納總結 高三語文一輪複習必看高三複習是考生最為緊張的備考階段,高三學生可根據自己的不足,來進行針對性的複習。下面有途網小編為大家編輯整理了高三語文必修三文言文知識點歸納總結,高三語文一輪複習必看,希望對你有幫助。
  • 學習C語言必看的最經典書籍
    來源:互動出版網學習c語言必看的最經典書籍推薦一:《新概念51單片機C語言教程--入門
  • 總結嵌入式開發中的C語言知識點
    #endif等)平臺擴展關鍵字(__asm, __inline,__syscall)    這些關鍵字共同構成了嵌入式平臺的C語言語法,嵌入式的應用從邏輯上可以抽象為以下三個部分:    貫穿在整個嵌入式應用開發的過程中,對數據的管理包含以下幾部分:    為了應對嵌入式開發中受限的資源環境,C語言從語法上支撐上述功能的實現,並提供相應的優化機制
  • 20考生必看:出題老師總結的二建必考知識點,教你輕鬆壓卷答題
    20考生必看:出題老師總結99%的二建必考知識點,教你輕鬆壓卷答題如何在短時間內有針對性地進行複習,全面、系統地領會吃透註冊執業考試考試各門課程的學習要點,最大限度地提高考試成績,是廣大考生最為關心的話題。在複習中要想提高效率,就必須把握重點。
  • c語言計算機二級學霸手把手教你,怎麼複習一次就過!
    因為專業屬性,我們考二級老師直接讓報名c語言,沒有討價還價的餘地,所以就乖乖的安心備考就好啦在開始複習之前,去微機室登錄題庫做一套題,了解清楚都會考什麼內容,大致分為哪幾個模塊。概括講主要分為硬體、軟體、編程。
  • 學習c語言,知道這三個資源就行了
    c語言作為目前最熱門的程式語言之一,一直是學生、程式設計師必須學習的課程之一,但目前許多學校的課程太過老舊,教材和教程也參差不齊,這裡就給大家推薦幾個比較好的學習c語言的資源。第一個推薦的就是《C Primer Plus》,作為一本廣受大家認可的工具書,書中詳細地講解了C語言的基本概念和編程技巧,並且包含了許多習題和例子,可以有效的幫助初學者建立對於C語言的知識體系,是一本對小白比較友好,且內容全面的入門教材。
  • 初學者:如何學好C語言?
    所有初學者面臨的第一個問題便是:如何選擇教材。好的開始是成功的一半,選擇一本優秀的教材是事半功倍的關鍵因素。不幸的是,學校通常會幫你指定一本很差勁的C語言課本;而幸運的是,你還可以再次選擇。大名鼎鼎的譚浩強教授出了一本《C語言程序設計》,據說發行量有超過400萬,據我所知,很多學校都會推薦這本書作為C語言課本。雖然本人的名字(譚浩宇)跟教授僅僅一字之差,但我是無比堅定地黑他這本書的。
  • 高中語文複習知識點歸納總結
    高中語文複習知識點歸納總結高中語文是主要的科目之一,該如何學好高中語文呢?下面是小編整理的高中語文複習知識點歸納總結,僅供大家參考。高中語文複習知識點歸納總結1.小說三要素:A人物 B情節 C環境2.議論文三要素:A論點 B論據 C論證3.比喻三要素:A本體 B喻體 C喻詞4.記敘文六要素(五W+H):何時when 何地where 何人who 何因why 何過how 何果what5.律詩四條件:A八句四聯(首頷頸尾) B偶尾同韻 C中聯對偶 D平聲合調
  • 初學者要如何學好C語言
    學習C語言不是一朝一夕的事情,但也不需要花費十年時間才能精通。如何以最小的代價學習並精通C語言是本文的主題。請注意,即使是「最小的代價」,也絕不是什麼捷徑,而是以最短的時間取得最多的收穫,同時也意味著你需要經歷艱苦的過程。所有初學者面臨的第一個問題便是:如何選擇教材。好的開始是成功的一半,選擇一本優秀的教材是事半功倍的關鍵因素。
  • c語言程序設計自學教程
    如果您不甘落後,那麼請自製自控,自學c語言程序設計也是完全可能的。C語言編譯的程序稱為源程序它以ASCII數值存放在文本文件中。2、#define PI 3.1415926; 這個寫法是錯誤的,一定不能出現分號。 3、每個C語言程序中main函數是有且只有一個4、在函數中不可以再定義函數。
  • 盤點目前初學者適合用的C語言編程工具!C語言初學者必看!
    手機軟體1.C語言編譯器:這是手機上的一個C語言編程軟體,可以直接在手機上編譯運行baiC語言程序,2.C++編譯器:也即C4droid,手機上的一個C/C++編程軟體,基本功能和C語言編譯器差不多,也可以直接編譯運行C語言程序,、電腦軟體
  • 高三生物複習知識點總結
    高三生物複習知識點總結生物學是一門基礎自然學科,研究生命現象和生命活動規律。人們的生活處處都離不開生物。社會的發展與人類文明的進步,個人生活質量的提高,都要靠生物學的發展和應用。而對於學生來說高中生物最終要的莫過於這是一門高考中不能丟分的科目,有途網小編給大家整理了高三生物複習知識點。1.
  • c語言重點知識點複習資料(5)
    5、移動指針(經常加入到考試中其他題目綜合考試) char *s=「meikanshu」 while(*s){printf(「%c」,*s);s++;}這個s首先會指向第一個字母m然後通過循環會一次列印出一個字符,s++是地址移動,列印了一個字母後,就會移動到下一個字母!
  • 初中生必看:初中數學知識點總結!
    初中生必看:初中數學知識點總結!吃透基礎點公式概念是計算解題能力的關鍵哦!有家長和我說,為什麼我的孩子數學計算做多少練習題都是錯的呀?太粗心了!其實不是孩子粗心,而是他對數學對公式概念定理等根本就沒有吃透,沒有一個正確的步驟引導他去正確地解題!
  • 初中化學:九年級上冊知識點大全,32頁詳細匯總,複習提分必看!
    初中化學:九年級上冊知識點大全,32頁詳細匯總,考前複習提分必看!隨著時間的推移,開學已經半月有餘,初三學生已經走進化學世界,開啟了新課程的學習。今天,小編老師對九年級上冊的知識點進行了整理,一共匯總成32頁,幫助同學們在考前進行複習以及日常的學習鞏固。家有初中生的孩子,強烈建議列印一份,整個學期都需要用到,考前提分必看!
  • IAR STM32 函數和變量的絕對地址定位
    經過查找資料和反覆實驗,終於實現了,現總結如下:1) 把函數定位在FLASH高端的指定位置,以後更新,只更新那小塊地方就可以了。方法一: IAR裡面進行函數定位, 必須要在.icf裡面,進行定義。 } .icf文件,加入這樣一句:place at address mem:0x08017000 { readonly section .sendstr};方法二)把要更新的函數,單獨放在一個.c文件中,然後再.icf文件裡面,對該文件進行定位:test.cint f1(int a,int b){if(a>0){
  • C語言初學者常見問題與錯誤
    解決方案:把b定義為長整型long,即long b = 1;(其實這裡還涉及到隱式轉換,所以,更為正確的方法是把a也定義為長整型) 7、C語言中的「除法」 例如: #include int main(void) { printf("請輸入一個華氏溫度"); float a,c; scanf("%f
  • 部編版九年級下冊歷史知識點總結:複習就用這份資料
    部編版九年級下冊歷史知識點總結:複習就用這份資料今年面臨升學考試的學生們,在學習上可謂一波三折,從長達兩個月的網課,到全封閉式管理的學習環境,再加上中考的壓力,十分消耗學生們的精力。如趣學君這邊有升學壓力的孩子,晚自習從以前九點結束變為十點結束,節假日不放假,全力為中考備戰。學生們學習之苦,由此可見。
  • 五年級數學上冊知識點歸納與總結,實用性很強,孩子考前必看一遍
    導語:五年級數學上冊知識點歸納與總結,實用性很強,孩子考前必看一遍孩子到了五年級,已經屬於小學高年級了,五六年級也是非常關鍵的兩年,因為之後,孩子就要升入初中了,升入初中之後,學習的科目增多,難度加大了,為了讓孩子能較快適應以後的初中生活