C 宏来检查是否正在检查函数的返回值

C 宏来检查是否正在检查函数的返回值,c,function,macros,void-pointers,c-preprocessor,C,Function,Macros,Void Pointers,C Preprocessor,我有一个函数 void *custom_get_value(ObjectPtr) 此函数传统上从不返回NULL。它可以返回以下任何值 uint32_t int32_t uint64_t int64_t uint8_t 由于该函数从未用于返回NULL,因此我有很多这样做的代码 *(uint32_t*)custom_get_value(ObjectPtr) OR *(uint64_t*)custom_get_value(

我有一个函数

void *custom_get_value(ObjectPtr) 
此函数传统上从不返回NULL。它可以返回以下任何值

uint32_t
int32_t 
uint64_t 
int64_t
uint8_t
由于该函数从未用于返回NULL,因此我有很多这样做的代码

       *(uint32_t*)custom_get_value(ObjectPtr)

                    OR

      *(uint64_t*)custom_get_value(ObjectPtr) 
最近,我们决定修改

void *custom_get_value(ObjectPtr) in such a way that it can return NULL.So all occourances of the above scenario (de-referencing to specific types without checking the return value) can result in segmentation fault.
我可以使用一些宏来标识代码中返回

void *custom_get_value(ObjectPtr)

未被检查。如果是,如何执行此操作?

您无法轻松编写宏来跟踪值返回后发生的情况,因为数据流分析在某种程度上超出了预处理器的能力。但是,有一种有用的技术可以帮助处理此类情况,即将要检查的函数定义为自引用宏:

#define custom_get_value(...) (0, custom_get_value(__VA_ARGS__))
这个例子本身并没有做任何事情,但它展示了一些有用的东西:当宏名称作为它自己的替换列表的一部分出现时,该名称的嵌套引用为“涂成蓝色”,并且不会再次展开。因此,通过将现有函数名定义为宏,除了保留原始调用外,还可以使用额外的操作“包装”(或“建议”)该函数的所有现有调用(可能分散在未知数量的代码中)

然后您有几个选项,例如:

#define custom_get_value(...) ({ \
    _Pragma("message \"custom_get_value called\""); \
    custom_get_value(__VA_ARGS__); \
})
…如果您在GCC或Clang上,应该列出编译时调用
custom\u get\u value
的每个点的文件和行号。(无论是
#pragma message
还是语句表达式表单都是非标准的GCC扩展-但是如果您只打算在清理旧调用时使用它,那么这也许无关紧要?一旦检查了所有调用,您就可以删除非标准代码。)

或者改为应用运行时消息:

#define custom_get_value(...) \
    custom_wrap(custom_get_value(__VA_ARGS__), __FILE__, __LINE__)

void * custom_wrap(void * val, char * f, int l) {
    printf("custom_get_value called in %s on line %d\n", f, l); return val;
}
…这会让您在每次调用函数时都打印消息,但至少是标准的


您甚至可以使用一个函数来包装所有调用,该函数会使生成的指针再次保证为非空,尽管这可能不是处理这种情况的最佳方法。

将新函数命名为
custom\u get\u value\u ex
,并删除旧函数。然后您的编译器将非常友好地指出旧函数的所有用法,以便您可以查看它们

通常,在更改已在使用的函数的语义时,最好检查对函数的所有调用

也许您可以将其中一些调用封装在一个更易于维护的包装器中,例如

inline uint32_t custom_get_uint32(.....) 
{  
     void *value = custom_get_value(.....);
     if ( !value )
          return 0;   // or some other error handling
     return *(uint32_t *)value;
}
然后,如果您再次更改
custom\u get\u value
,则只需将此包装设为fox即可

更为整洁的是,您可以使用以下功能获取感兴趣的项目:

inline uint32_t get_propeller_setting(.....)
{
     void *value = custom_get_value(PROPELLER_SETTING,........)
     // ....
}
然后,您可以对每个值何时返回空指针进行特定处理