You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

排查C宏展开中与格式说明符相关的编译错误

原UCI错误处理宏的问题分析

原错误处理宏定义

最初为捕获UCI操作失败原因定义的宏如下:

#define HANDLE_UCI_ERR(_cond_, _fmt_, ...)                  
    do {                                                    
        if (_cond_) {                                       
            char *err_msg = NULL;                           
            uci_get_errorstr(g_uci_ctx, &err_msg, _fmt_);   
            ERR(err_msg, ##__VA_ARGS__);                    
            free(err_msg);                                  
            goto bail;                                      
        }                                                   
    } while (0)

编译错误信息

编译时出现如下错误:

gcc trial.c -luci -o trial
trial.c: In function ‘main’:
trial.c:37:11: error: expected ‘)’ before ‘err_msg’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \n      |           ^~~~~~~
trial.c:14:38: note: in definition of macro ‘MY_PRINT’
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \n      |                                      ^~~
trial.c:37:4: note: in expansion of macro ‘MY_ERR’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \n      |    ^~~~~~
trial.c:72:2: note: in expansion of macro ‘HANDLE_UCI_ERR’
   72 |  HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);
      |  ^~~~~~~~~~~~~~
trial.c:14:10: warning: format ‘%s’ expects a matching ‘char *’ argument [-Wformat=]
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \n      |          ^~~~~~~
trial.c:19:3: note: in expansion of macro ‘MY_PRINT’
   19 |   MY_PRINT("Error  : ", fmt, ##arg)
      |   ^~~~~~~~
trial.c:37:4: note: in expansion of macro ‘MY_ERR’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \n      |    ^~~~~~
trial.c:72:2: note: in expansion of macro ‘HANDLE_UCI_ERR’
   72 |  HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);
      |  ^~~~~~~~~~~~~~
trial.c:14:29: note: format string is defined here
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \n      |                            ~^
      |                             |
      |                             char *
trial.c:14:10: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \n      |          ^~~~~~~
trial.c:19:3: note: in expansion of macro ‘MY_PRINT’
   19 |   MY_PRINT("Error  : ", fmt, ##arg)
      |   ^~~~~~~~
trial.c:37:4: note: in expansion of macro ‘MY_ERR’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \n      |    ^~~~~~
trial.c:72:2: note: in expansion of macro ‘HANDLE_UCI_ERR’
   72 |  HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);
      |  ^~~~~~~~~~~~~~
trial.c:14:32: note: format string is defined here
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \n      |                               ~^
      |                                |
      |                                int

Compilation exited abnormally with code 1

可复现的简化代码

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <uci.h>

#define DEBUG_ERROR_LEVEL 2

int loglevel = 7;

#define MY_PRINT(prefix, fmt, arg...) do {              
        printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); 
    } while (0)

#define MY_ERR(fmt, arg...)         
    if (loglevel > DEBUG_ERROR_LEVEL)   
        MY_PRINT("Error  : " fmt, ##arg)

#define HANDLE_UCI_ERR(_cond_, _fmt_, ...)                              
    do {                                                            
        if (_cond_) {                                           
            char *err_msg = NULL;                           
            uci_get_errorstr(g_uci_ctx, &err_msg, _fmt_);   
            MY_ERR(err_msg, ##__VA_ARGS__);         
            free(err_msg);                                  
            goto bail;                                      
        }                                                       
    } while (0)

static const char *PACKAGE_STR = "package";

static struct uci_context *g_uci_ctx;

__attribute__((constructor))
void init_uci_context() {
    g_uci_ctx = uci_alloc_context();
    if (!g_uci_ctx) {
        exit(EXIT_FAILURE);
    }
    if (uci_set_confdir(g_uci_ctx, "/home/aziz.kavas@corp.airties.com/tmp/config/") != 0) {
        exit(EXIT_FAILURE);
    }
}

__attribute__((destructor))
void cleanup_uci_context() {
    if (g_uci_ctx) {
        uci_free_context(g_uci_ctx);
    }
}

int main() {
    int rc = 0;
    struct uci_package *pkg = NULL;

    rc = uci_load(g_uci_ctx, PACKAGE_STR, &pkg);
    HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);

bail:
    uci_unload(g_uci_ctx, pkg);
    return rc;
}

修改后的正确宏

通过调整HANDLE_UCI_ERR宏解决了编译问题:

#define HANDLE_UCI_ERR(cond, fmt, arg...)                
    do {                                                 
        if (cond) {                                      
            char *err_msg = NULL;                        
            uci_get_errorstr(g_uci_ctx, &err_msg, NULL); 
            MY_ERR("%s : " fmt, err_msg, ##arg);         
            free(err_msg);                               
            goto bail;                                   
        }                                                
    } while (0)

原代码的问题分析

  1. 宏展开的语法错误
    原宏中调用MY_ERR(err_msg, ##__VA_ARGS__)时,MY_ERR展开后会变成MY_PRINT("Error : " err_msg, ##__VA_ARGS__),而C语言不支持直接用空格拼接字符串指针和字符串字面量,这直接导致了编译报错expected ‘)’ before ‘err_msg’。

  2. 格式字符串与参数的逻辑混乱
    MY_ERR的第一个参数要求是格式字符串,但原代码传入的err_msg是普通字符串。当printf把err_msg当作格式字符串处理时,后续传入的PACKAGE_STR会被当作该格式的参数,但err_msg并没有对应的占位符,从而引发格式不匹配的编译警告。

  3. uci_get_errorstr用法错误
    原代码中给uci_get_errorstr传入_fmt_,相当于让UCI库用该格式字符串生成错误信息,之后又把生成好的err_msg当作格式字符串传给MY_ERR,属于重复且错误的格式处理逻辑。修改后传入NULL直接获取原始错误信息,再通过"%s : " fmt的格式字符串拼接错误信息与自定义提示,才是正确的用法。

内容的提问来源于stack exchange,提问作者Aziz Kavas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 20:25:56