1 翻譯程序的第一步
2 重新定義宏
3 帶參數宏
注意:帶參數宏定義中要使用 ( ) 括號將每個參數都包圍起來,以免出錯。
4 宏定義創建字符串:#運算符
執行流程:
5 預處理器粘合劑:##運算符
6 變參宏(... 和 __VA_ARGS__)
#include <stdio.h>
/* 按照 printf 格式輸入時,fmt 爲字符串,和前後的黏合起來 */
#define debug(fmt, ...) printf("<- This is debug mode ->\n" fmt "\n", __VA_ARGS__)
int main(void)
{
debug("debug message print\n");
debug("%.2f + %.3f = %.5f", 2.1, 3.4, 2.1 + 3.4);
return 0;
}
7 文件包含 #include
8 其他指令
1 #undef 指令 取消宏定義
2 條件編譯
2.1 #ifdef #else #endif
2.2 #ifndef #else #endif (常用來防止頭文件被重複包含)
3 #if #else #endif
defined 預處理運算符
4 預定義宏 (在代碼調試中非常有用)
#include <stdio.h>
int main(void)
{
printf("This file is %s.\n", __FILE__);
printf("This function is %s.\n", __func__);
printf("This date is %s.\n", __DATE__);
printf("This line is %d.\n", __LINE__);
return 0;
}
5 #error
9 斷言 assert
C語言提供了斷言標準庫,#include <assert.h>
下面是標準庫 <assert.h> 的說明:
#ifdef NDEBUG
#define assert(expression) ((void)0)
#else
_ACRTIMP void __cdecl _wassert(
_In_z_ wchar_t const* _Message,
_In_z_ wchar_t const* _File,
_In_ unsigned _Line
);
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
#endif
這裏有一個疑問,((void)0)是什麼意思?
實際上,((void)0)表示一個什麼都不做的空語句
參考鏈接:https://stackoverflow.com/questions/2198950/why-is-void-0-a-no-operation-in-c-and-c
stm32官方提供的斷言機制(也是使用(void(0)) 或者打印文件名和行號)
// 用於斷言中檢查輸入參數
#define IS_GPIO_ALL_INSTANCE(INSTANCE) (((INSTANCE) == GPIOA) || \
((INSTANCE) == GPIOB) || \
((INSTANCE) == GPIOC) || \
((INSTANCE) == GPIOD) || \
((INSTANCE) == GPIOE) || \
((INSTANCE) == GPIOF) || \
((INSTANCE) == GPIOG))
// 使用斷言檢查參數
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
// 關於斷言的宏定義(默認關閉斷言),位於 stm32f1xx_hal_conf.h 文件中
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @param expr If expr is false, it calls assert_failed function
* which reports the name of the source file and the source
* line number of the call that failed.
* If expr is true, it returns no value.
* @retval None
*/
// 這裏使用 (void(0)) 來作爲空語句填充
// 如果斷言爲1,則爲空語句;如果斷言爲0,則調用 assert_failed 函數,這個函數需要我們自己實現
// assert_failed 函數傳入了文件和行號等作爲形參,我們可以打印來定位錯誤位置
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
// 斷言函數實現,位於 main.c 中
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
// 默認爲空,這裏還給出了推薦用法
printf("Wrong parameters value: file %s on line %d\r\n", file, line);
}
#endif /* USE_FULL_ASSERT */
10 <string.g> 中的其他函數
1 memcmp 函數
2 memcpy 函數
3 memset 函數
這個函數相當有用,可以將一塊內存清零或置一,如下所示:
#include <stdio.h>
#include <string.h>
#define SIZE 50
int main(void)
{
int data[SIZE];
memset(data, 0, sizeof(int) * SIZE); // 內存清 0
memset(data, 0XFF, sizeof(int) * SIZE); // 內存置 1
return 0;
}
當執行內存清零時,整個數組如下所示:
當執行內存置一時,整個數組如下所示: