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;
}
当执行内存清零时,整个数组如下所示:
当执行内存置一时,整个数组如下所示: