C语言宏定义-再看ARRAY_SIZE

本文详细介绍了C语言中的ARRAY_SIZE宏,用于计算数组元素个数,并探讨了其安全性增强——__must_be_array(arr)。同时,文章讲解了__builtin_types_compatible_p函数的作用,用于判断两种类型是否相等。此外,还阐述了typeof()关键字的用途。最后,列举了相关宏如CMO_ARRAY_SIZE、CMO_PART_OF_ARRAY等,用于数组和结构体成员的大小及偏移计算。

C语言宏定义-再看ARRAY_SIZE

1. 简介

做过linux驱动开发的人员,应该都用过ARRAY_SIZE(x)宏,目的求出数组包含的最大个数,在2007内核提交了一个patch,更新了这个宏加入__must_be_array(arr)是防止被误用,比如传入的是指针而不是数组。

#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]) + __must_be_array(arr))

image-20201114223142406.png

2. __BUILTIN_TYPES_COMPATIBLE_P

名字:
__builtin_types_compatible_p GNU扩展,用来判断两个类型是否相同。(PS: GCC提供了一系列的builtin函数,可以实现一些简单快捷的功能来方便程序编写,另外,很多builtin函数可用来优化编译结果。这些函数以“_builtin”作为函数名前缀。)

原型:

int __builtin_types_compatible_p(type_a, type_b);

描述:
此函数为GNU扩展,判断type_atype_b是否是相同的数据类型,相同返回1,否则返回0。该函数不区分const/volatile这样的修饰符,即int和const int被认为是相同的类型。

请注意一下情景:

  1. 两个类型的sizeof值跟它的类型判断是相互不影响的,例如:在i386架构下,sizeof(char *)sizeof(int) 的值是相同的,但是他们的类型却是不相同的。

  2. 限定符会被忽略,例如const intint他们的类型是相同的。

  3. 相同的类型不同的指针级数是不相同的例如:double*double**是不相同的。

  4. 连个用typedef定义的类型,当且进档他们定义的类型是相同的时候,=它们是相同的。

  5. enum类型是一种特殊的情况,两个enum类型是不相同的。

3. typeof()关键字

typeof() 是GUN C提供的一种特性,可参考C-Extensions,它可以取得变量或者表达式的类型,这个特性在linux内核中应用非常广泛。

具体使用方法参考:C语言中typeof()函数的用法

4. 相关宏

CMO_ARRAY_SIZE(array)计算一个数组的最大个数
CMO_PART_OF_ARRAY(array, ptr)判断是否指针是否在array内存范围内
CMO_MEMBER_SIZE(structure, member)计算结构体成员大小
CMO_MEMBER_ARRAYSIZE(structure, member)计算结构体数组类成员的最大个数
CMO_MEMBER_DELTA(structure, member1, member2)计算结构体中两个成员的偏移差值
/* Auxiliary Marco, evaluates to 0 if array is an array; compile error if not
 * array (e.g. pointer).
 */
#if (CMO__HAVE_TYPEOF && CMO__HAVE_BUILTIN_TYPES_COMPATIBLE_P)
#define __cmo_must_be_array(array)                                           \
    CMO_STATIC_ASSERT_OR_ZERO(!__builtin_types_compatible_p(                 \
        __typeof__(array), __typeof__(&(array)[0])))
#else
#define __cmo_must_be_array(array) (0)
#endif /* (CMO__HAVE_TYPEOF && CMO__HAVE_BUILTIN_TYPES_COMPATIBLE_P) */

/**
 * @def CMO_PART_OF_ARRAY
 * @brief Evaluates to 1 if ptr is part of array, 0 otherwise; compile error if
 * "array" argument is not an array (e.g. "ptr" and "array" mixed up)
 */
#define CMO_PART_OF_ARRAY(array, ptr)                                        \
    ((ptr) && ((void *)(ptr) >= (void *)&((array)[0]) &&                     \
               (void *)(ptr) < (void *)&((array)[CMO_ARRAY_SIZE(array)])))

/**
 * @def CMO_MEMBER_SIZE
 * @brief Calc structure of member size.
 *
 * Example:
 * @code
 * struct a = {
 *     uint32_t m1;
 *     uint32_t m2;
 * };
 * size_t size_m2;
 *
 * size_m2 = CMO_MEMBER_SIZE(a, m2); //size_m2 = 4
 * @endcode
 */
#define CMO_MEMBER_SIZE(structure, member) (sizeof(((structure *)0)->member))

/**
 * @def CMO_MEMBER_ARRAYSIZE
 * @brief Calc structure of member array size(Number of entries in an array).
 *
 * Example:
 * @code
 * struct a = {
 *     uint32_t m1;
 *     uint32_t m2[6];
 * };
 * size_t arraysize_m2;
 *
 * arraysize_m2 = CMO_MEMBER_ARRAYSIZE(a, m2); //arraysize_m2 = 6
 * @endcode
 */
#define CMO_MEMBER_ARRAYSIZE(structure, member)                              \
    (CMO_MEMBER_SIZE(structure, member) /                                    \
     CMO_MEMBER_SIZE(structure, member[0]))

/**
 * @def CMO_MEMBER_DELTA
 * @brief Delta from start of one member to the start of another member.
 *
 * Example:
 * @code
 * struct a = {
 *     uint32_t m1;
 *     uint32_t m2;
 * };
 * int delta_m1_m2;
 *
 * delta_m1_m2 = CMO_MEMBER_DELTA(a, m1, m2); //delta_m1_m2 = -4
 * @endcode
 */
#define CMO_MEMBER_DELTA(structure, member1, member2)                        \
    ((int)offsetof(structure, member1) - (int)offsetof(structure, member2))
<think>好的,用户现在遇到了ARRAY_SIZE未定义的问题,并且提到了STM32和huart3。我需要先理解这个问题的背景。 首先,用户之前的问题是关于将代码从huart2迁移到huart3的,现在在解决过程中遇到了ARRAY_SIZE未定义的错误。这可能是因为在代码中使用了ARRAY_SIZE这个宏,但编译器找不到它的定义。 ARRAY_SIZE通常用于计算数组的元素个数,比如在C语言中,常见的定义是: #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) 如果用户没有在代码中定义这个宏,就会导致编译错误。因此,用户需要在自己的代码中添加这个宏定义,或者检查是否包含了正确的头文件。 接下来,我需要确认用户的问题是否与之前的huart3迁移有关。可能用户在修改代码的过程中,误删除了某些宏定义,或者在新的代码模块中使用了ARRAY_SIZE而没有包含相应的定义。另外,如果用户使用的是特定的库或框架,可能需要检查该库是否有自己的ARRAY_SIZE定义,或者是否需要额外配置。 另外,用户提到STM32和huart3,所以可能是在STM32的项目中遇到了这个问题。STM32的标准库或HAL库中通常不包含ARRAY_SIZE宏,需要用户自己定义。如果用户之前在使用huart2的代码中有这个宏定义,但在迁移到huart3时没有正确移植,也可能导致问题。 需要进一步询问用户的具体代码情况,比如他们是在哪个文件中使用了ARRAY_SIZE,是否在迁移过程中修改了相关的头文件或源文件。此外,检查编译错误的具体信息,确认错误是否出现在使用ARRAY_SIZE的地方,或者是否有其他相关的问题。 可能的解决方案包括: 1. 在代码中添加ARRAY_SIZE宏定义。 2. 检查是否包含了定义该宏的头文件。 3. 确认代码中数组的使用是否正确,避免在指针上使用ARRAY_SIZE,因为这会得到错误的结果。 此外,考虑到用户之前的问题涉及CubeMX配置和代码迁移,可能需要检查用户是否在迁移过程中遗漏了某些宏定义或配置步骤,或者在重新生成代码时覆盖了原有的定义。 需要提醒用户注意代码中的宏定义位置,确保它们位于不会被CubeMX覆盖的区域,比如用户代码保护块内。同时,建议用户检查所有使用ARRAY_SIZE的地方,确保传入的是数组而不是指针,因为宏在指针情况下会失效。 最后,可能需要指导用户如何正确添加宏定义,并提供示例代码,确保他们能够解决编译错误,并理解如何避免类似问题。</think>### ARRAY_SIZE未定义问题解决指南 --- #### 一、问题根源分析 1. **ARRAY_SIZE本质**: `ARRAY_SIZE`是用于计算数组元素数量的宏,需手动定义(非C标准库内容) ```c #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) // 正确宏定义 ``` 2. **常见触发场景**: - 移植第三方代码时缺失宏定义 - CubeMX重新生成代码时覆盖用户自定义宏 - 在指针类型上误用(仅限数组类型)[^1] --- #### 二、STM32工程专用解决方案 1. **添加标准定义(推荐)** 在`main.h`或专用头文件中添加: ```c /* USER CODE BEGIN Private defines */ #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) #endif /* USER CODE END Private defines */ ``` 2. **验证使用场景**(关键步骤): ```c uint8_t uart3_tx_buffer[] = {0x01, 0x02, 0x03}; // 正确用法(数组) HAL_UART_Transmit(&huart3, uart3_tx_buffer, ARRAY_SIZE(uart3_tx_buffer), 100); // 错误示例(指针) uint8_t* p_buffer = uart3_tx_buffer; int size = ARRAY_SIZE(p_buffer); // 错误!返回sizeof指针而非数组大小 ``` 3. **CubeMX代码保护技巧**: 通过`STM32CubeMX`生成代码时,确保宏定义在用户保护区(`USER CODE BEGIN/END`块),避免被覆盖[^2] --- #### 三、硬件层关联验证(针对huart3) 1. **DMA配置检查**: 当使用`ARRAY_SIZE`计算DMA传输长度时,需确保数组定义在有效作用域: ```c /* 错误:局部数组在CubeMX生成代码中被误优化 */ static uint8_t dma_buffer[] = {0xAA, 0xBB, 0xCC}; // 需声明为全局或static ``` 2. **中断服务程序验证**: 在`USART3_IRQHandler`中使用数组时,确保数组生命周期: ```c void USART3_IRQHandler(void) { static uint8_t rx_buf[64]; // 正确:静态存储保证数组有效性 HAL_UART_Receive_IT(&huart3, rx_buf, ARRAY_SIZE(rx_buf)); } ``` --- #### 四、进阶调试方法 1. **预处理展开检查**: 在编译器选项中添加`-E`参数,观察宏展开结果: ```bash arm-none-eabi-gcc -E main.c -o main.i ``` 2. **类型安全增强方案**: 使用`_Static_assert`防止误用指针: ```c #define ARRAY_SIZE(arr) ( \ _Static_assert(!__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0])), \ "ARRAY_SIZE requires real array"), \ (sizeof(arr)/sizeof((arr)[0])) \ ) ``` --- #### 五、关联问题排查表 | 现象 | 检查点 | 工具/方法 | |------|--------|-----------| | 编译报错`undefined symbol` | 宏定义是否在包含路径中 | `grep -rn "ARRAY_SIZE"` | | 传输数据长度错误 | 数组是否退化为指针 | 逻辑分析仪抓包 | | 随机内存覆盖 | 数组越界访问 | `__attribute__((aligned))` | --- [^1]: 根据C99标准,`sizeof`运算符在编译期计算数组大小 : STM32CubeMX用户代码保护机制说明详见官方文档第4.2节 [^3]: 使用`_Static_assert`需要C11标准支持,需在编译器添加`-std=c11`参数
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值