[英]C99 compound literal passed to function parameter and returned by the same function
我想在 C99 中將 uuid 轉換為十六進制字符串,並將其傳遞給在后台使用 printf 格式的日志函數。 我想避免局部變量的單獨分配,因為如果日志被禁用,那么預處理器會刪除函數調用並且變量變得未使用,因此會發出警告。
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
typedef struct {
uint8_t data[8];
} uuid_64_t;
#define UID_64_STR_MAX_SIZE 24
#define UUID_64_TO_STRING(uuid_64, separator, uppercase) \
uuid_64_to_string((char[UID_64_STR_MAX_SIZE]){ 0 }, \
sizeof((char[UID_64_STR_MAX_SIZE]){ 0 }), \
uuid_64, \
separator, \
uppercase)
const char *bytes_to_hex(char *buffer,
uint32_t buffer_size,
const uint8_t *bytes,
uint32_t bytes_size,
char separator,
bool uppercase)
{
const char hex_char_uppercase[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const char hex_char_lowercase[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
const char *hex_char = uppercase ? hex_char_uppercase : hex_char_lowercase;
uint32_t total_size, total_separator_size;
// If the separator is set the null character then no separator is used so
// the multiplication by zero results in zero total separator size.
// There is a separator after each two hex characters except for the last two.
total_separator_size = (bytes_size - 1) * (separator != '\0');
// One character shall be reserved for the terminating null character
total_size = 2 * bytes_size + total_separator_size + 1;
if ((buffer == NULL) || (bytes == NULL) || (buffer_size < total_size)) {
return "INVALID";
}
uint32_t out_idx = 0;
for (uint32_t in_idx = 0; in_idx < bytes_size; in_idx++) {
buffer[out_idx++] = hex_char[(bytes[in_idx] >> 4) & 0xF];
buffer[out_idx++] = hex_char[(bytes[in_idx] >> 0) & 0xF];
if (separator != '\0' && (in_idx + 1) < bytes_size) {
buffer[out_idx++] = separator;
}
}
buffer[out_idx] = '\0';
return buffer;
}
const char *uuid_64_to_string(char *buffer,
uint32_t buffer_size,
const uuid_64_t *uuid_64,
char separator,
bool uppercase)
{
return bytes_to_hex(buffer,
buffer_size,
uuid_64->data,
sizeof(uuid_64->data),
separator,
uppercase);
}
int main(void)
{
printf("uuid=%s\r\n", UUID_64_TO_STRING(&uuid_64, ':', true));
}
這個想法是通過一個宏調用一個函數,該宏將復合文字作為緩沖區參數傳遞。 復合文字分配一個局部變量,uuid_64_to_string 函數通過 bytes_to_hex 函數寫入十六進制字符。 uuid_64_to_string 返回這個傳遞的復合文字,以便在類似 printf 的日志調用中直接使用它。 唯一的問題可能是復合文字的生命周期,我對此有點不確定。 根據C99標准:
復合文字的值是由初始化列表初始化的未命名對象的值。 如果復合文字出現在函數體之外,則該對象具有靜態存儲持續時間; 否則,它具有與封閉塊關聯的自動存儲持續時間
因此,當我解釋標准時,這應該是明確定義的行為,因為 printf 調用和 uuid_64_to_string 調用在同一個塊中。 你有什么意見?
宏UUID_64_TO_STRING
擴展為對uuid_64_to_string
的函數調用,傳遞一個指向復合文字(char[UID_64_STR_MAX_SIZE]){ 0 }
的指針,其范圍是main()
函數中的封閉塊。
函數uuid_64_to_string
返回它的第一個參數,即指向本地數組的指針。 可以將它作為參數傳遞給printf
,因為它指向的對象是 C 字符串,並且具有涵蓋printf
調用執行的生命周期。
相反,將 this 指針返回給調用函數或將其存儲到當前范圍之外使用的指針中是錯誤的:
int main() {
printf("uuid=%s\r\n", UUID_64_TO_STRING(&uuid_64, ':', true)); // OK
return 0;
}
此用法無效:
char *hexify(uuid_64_t *id) {
return UUID_64_TO_STRING(id, ':', true); // NOT OK
}
int main() {
printf("uuid=%s\r\n", hexify(&uuid_64)); // NOT OK
return 0;
}
請注意,范圍問題可能很微妙:
int main() {
const char *p = "invalid id";
if (isValidID(uuid_64))
p = UUID_64_TO_STRING(&uuid_64, ':', true);
printf("uuid=%s\r\n", p); // OK
return 0;
}
int main() {
const char *p = "invalid id";
if (isValidID(uuid_64)) {
p = UUID_64_TO_STRING(&uuid_64, ':', true);
}
printf("uuid=%s\r\n", p); // NOT OK
return 0;
}
雖然這個宏看起來很有用,但應該小心使用它,可能只用作函數參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.