【问题标题】:passing va_list around results in invalid data传递 va_list 会导致无效数据
【发布时间】:2020-03-12 11:15:36
【问题描述】:

我发现了几个关于此的问题,但没有一个可以帮助我完成这项工作。 我有以下。


struct general_calibration_t {
    double pressure_span;
    double pressure_offset;
    double steam_temperature_offset;
};

struct general_t {
    struct general_calibration_t calibration;
};

int generateGenericJSON(string* str, const char* json, ...) {
    char* tmp = (char*)malloc(2048);
    if (tmp == NULL)
        goto fail;

    va_list ap;
    va_start(ap, json);
    const int amount = snprintf(tmp, 2048, json, ap);
    va_end(ap);
    if (amount >= 2048 || amount < 0)
        goto release;

    string_new_value(str, tmp, amount);
    free(tmp);
    return 0;

release:
    free(tmp);
fail:
    return 1;
}

调用如下:

    struct general_t data;
    data.calibration.pressure_offset = 1.0;
    data.calibration.pressure_span = 34.8;
    data.calibration.steam_temperature_offset = 20.0;
    string calibrationStr;
    generateGenericJSON(&calibrationStr, 
        "\"Calibration\":{\"PressureSpan\":%.3f,\"PressureOffset\":%.3f,\"SteamTemperatureOffset\":%.3f}", 
        data.calibration.pressure_span, 
        data.calibration.pressure_offset, 
        data.calibration.steam_temperature_offset);

我的输出如下:

"Calibration":{"PressureSpan":0.000,"PressureOffset":0.000,"SteamTemperatureOffset":-92559631349317830736831783200707727132248687965119994463780864.000}

va_list 传递给snprintf 时出现问题。如果我使用va_arg 遍历函数generateGenericJSON 中的列表,则数据是正确的。我尝试将va_list 作为指针传递,但我得到了相同的无效数据。 将 va_list 传递给 snprintf 时我做错了什么?

【问题讨论】:

  • @dash-o 这绝对是goto的恰当用法。
  • @dash-o 这在内核、驱动程序和 rtos 代码中很常见。对 Linux 内核的快速 grep 显示其使用了 155488 次。

标签: c printf variadic-functions


【解决方案1】:

我在将这个 va_list 传递给 snprintf 时做错了什么?

您想使用vsnprintf() 而不是snprintf()


跟进 dash-ocomment:尽量避免(可能)意大利面代码。

处理不同成功/失败情况的可能方法是:

int generateGenericJSON(string* str, const char* json, ...) {
  int result = 0; /* Be optimistic. */

  do { /* One time "loop" */
    char* tmp = malloc(2048); /* No need to cast malloc() and friends in C. */
    if (tmp == NULL) {
      result = -1; /* By convention -1 indicates failure. */
      break;
    }

    va_list ap;
    va_start(ap, json);
    const int amount = vsnprintf(tmp, 2048, json, ap);
    va_end(ap);

    if (amount >= 2048 || amount < 0) {
      result = -1;
      break;
    }

    string_new_value(str, tmp, amount);
  } while (0);

  free(tmp); /* Passing NULL to free is fine. */

  return result;
}

【讨论】:

  • 这绝对是goto的恰当用法。我不明白为什么不会。我不明白你为什么需要do { ... } while(0);
  • 我的评论与tmp = malloc(...) ; if ( !tmp) goto err ; ... ; err: return 1 相关。没有理由不写tmp = malloc(...) ; if ( !tmp ) return 1。我没有看到 goto 的值。
  • 我觉得遵循每个函数只有一个出口点的模式是个好主意。
猜你喜欢
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
相关资源
最近更新 更多