/* vsprintf自定义实现 */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <math.h>
typedef struct {
size_t len;
unsigned char* data;
} gtc_str_t;
#define GTC_INT32_LEN (sizeof("-2147483648") - 1)
#define GTC_INT64_LEN (sizeof("-9223372036854775808") - 1)
#define GTC_MAX_UINT32_VALUE (unsigned int) 0xffffffff
#define gtc_min(val1, val2) ((val1 > val2) ? (val2) : (val1))
#define gtc_string(str) { sizeof(str) - 1, (unsigned char *) str }
#define gtc_cpymem(dst, src, n) (((unsigned char *) memcpy(dst, src, n)) + (n))
//int mprintf(unsigned char* buf, const char* fmt, ...) __attribute__((format(printf,2,3)));
static unsigned char* gtc_sprintf_num(unsigned char* buf, unsigned char* last, unsigned long long ui64, unsigned int hexadecimal)
{
unsigned char * p, temp[GTC_INT64_LEN + 1] = { 0 };
size_t len;
unsigned int ui32;
static unsigned char hex[] = "0123456789abcdef";
static unsigned char HEX[] = "0123456789ABCDEF";
// 1.参数校验
p = temp + GTC_INT64_LEN;
if (0 == hexadecimal)
{
// 非十六进制数值处理
if (ui64 < GTC_MAX_UINT32_VALUE)
{
/*
设计说明:
为什么无符号32位和无符号64位需要分开处理?
因为在某些平台上32位除法和64除法不一样,为了提升32位除法性能,因此需要分开处理
*/
ui32 = (unsigned int)ui64;
do {
*--p = (unsigned char)(ui32 % 10 + '0');
} while (ui32 /= 10);
}
else
{
do {
*--p = (unsigned char)(ui64 % 10 + '0');
} while (ui64 /= 10);
}
}
else if (1 == hexadecimal)
{
do {
/* the "(uint32_t)" cast disables the BCC's warning */
*--p = hex[(unsigned int)(ui64 & 0xf)];
} while (ui64 >>= 4);
}
else
{
/* hexadecimal == 2 */
do {
/* the "(uint32_t)" cast disables the BCC's warning */
*--p = HEX[(unsigned int)(ui64 & 0xf)];
} while (ui64 >>= 4);
}
// 计算拷贝字符串长度
len = temp + GTC_INT64_LEN - p;
if (buf + len > last) {
len = last - buf;
}
return gtc_cpymem(buf, p, len);
}
static unsigned char* gtc_vslprintf(unsigned char* buf, unsigned char* last, const char* fmt, va_list args)
{
double f;
int long_type, ch;
long long i64;
unsigned long long ui64, frac, scale;
gtc_str_t* v;
size_t len;
unsigned char * p;
// 1.参数校验
// 2.格式化
while (*fmt && buf < last)
{
if ('%' == *fmt)
{
fmt++;
// 初始化
long_type = 0;
i64 = 0;
ui64 = 0;
// 处理ld% lu% %lf类似于多个格式化标记
for (;;)
{
switch (*fmt)
{
case 'l':
// 长类型标记
long_type = 1;
fmt++;
break;
default:
break;
}
break;
}
switch (*fmt)
{
case 'u':
if (long_type)
{
// %lu 无符号长整型
ui64 = (unsigned long long)va_arg(args, unsigned long long);
}
else
{
// %u 无符号整型
ui64 = (unsigned long long)va_arg(args, unsigned int);
}
// 数字格式化
buf = gtc_sprintf_num(buf, last, ui64, 0);
fmt++;
continue;
case 'd':
if (long_type)
{
// %ld 有符号长整型
i64 = (long long)va_arg(args, long long);
}
else
{
// %d 有符号整型
i64 = (long long)va_arg(args, int);
}
// 负数处理
if (i64 < 0)
{
*buf++ = '-';
i64 = -i64;
}
// 数字格式化
ui64 = (unsigned long long)i64;
buf = gtc_sprintf_num(buf, last, ui64, 0);
fmt++;
continue;
case 'c':
// 字符
/*
知识补充:
va_arg不支持获取char类型,需要用int来代替
*/
ch = va_arg(args, int);
*buf++ = (unsigned char)(ch & 0xff);
fmt++;
continue;
case 'f':
f = va_arg(args, double);
if (f < 0) {
*buf++ = '-';
f = -f;
}
// 整数部分提取
ui64 = (unsigned long long)f;
// 数字转字符串
buf = gtc_sprintf_num(buf, last, ui64, 0);
// 根据实测,printf是保留小数点后面6位有效数字
scale = (unsigned long long)pow(10, 6);
// 0.5为了四舍五入
frac = (unsigned long long)((f - (double)ui64) * scale + 0.5);
if (frac)
{
// 获取真实精度
while (!frac%10)
{
frac /= 10;
}
// 字符串增加小数点
if (buf < last) {
*buf++ = '.';
}
// 数字转字符串
buf = gtc_sprintf_num(buf, last, frac, 0);
}
fmt++;
continue;
case 'V':
/*
设计说明:
注意%V的特殊用法,这个值得记录
*/
v = va_arg(args, gtc_str_t*);
len = gtc_min(((size_t)(last - buf)), v->len);
buf = gtc_cpymem(buf, v->data, len);
fmt++;
continue;
case 'x':
ui64 = (unsigned long long)va_arg(args, unsigned int);
// 数字格式化
buf = gtc_sprintf_num(buf, last, ui64, 1);
fmt++;
continue;
case 'X':
ui64 = (unsigned long long)va_arg(args, unsigned int);
// 数字格式化
buf = gtc_sprintf_num(buf, last, ui64, 2);
fmt++;
continue;
case '%':
*buf++ = '%';
fmt++;
continue;
case 'p':
ui64 = (unsigned long long)va_arg(args, void*);
*buf++ = '0';
*buf++ = 'x';
// 数字格式化
buf = gtc_sprintf_num(buf, last, ui64, 2);
fmt++;
continue;
case 's':
p = va_arg(args, unsigned char*);
while (*p && buf < last) {
*buf++ = *p++;
}
fmt++;
continue;
default:
*buf++ = *fmt++;
continue;
}
}
else
{
// 拷贝非格式化字符
*buf++ = *fmt++;
}
}
return buf;
}
/*
知识补充:
__attribute__ format
该__attribute__属性可以给被声明的函数加上类似于printf或scanf的特征,他可以使编辑器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。
format的语法格式为
format(archetype, string-index, first-to-check)
format属性告诉编译器,按照printf,scanf,strftime,strfmon的参数表格式规则对该函数的参数进行检查。
archetype指定是哪种风格
string-index指定传入函数的第几个参数是格式化字符串
first-to-check指定从函数的第几个参数开始按上述规则进行检查
注意:当该函数是C++类中成员属性时,需要考虑this指针问题。
使用方式
1.函数声明
int mprintf(unsigned char* buf, const char* fmt, ...) __attribute__((format(printf,2,3)));
2.函数定义
__attribute__((format(printf, 2, 3))) int mprintf(unsigned char* buf, const char* fmt, ...){}
以上两种方式任选其一即可
*/
__attribute__((format(printf, 2, 3))) int mprintf(unsigned char* buf, const char* fmt, ...)
{
int ret = 0;
va_list ap;
// 1.参数校验
if (NULL == buf)
{
ret = -1;
return ret;
}
// 2.初始化宏变量
va_start(ap, fmt);
// 3.格式化字符串
gtc_vslprintf(buf, (void*)-1, fmt, ap);
// 4.结束
va_end(ap);
return ret;
}
void test()
{
unsigned char buf[2024] = { 0 };
gtc_str_t str = gtc_string("world");
int a1 = 123;
int a2 = -456;
float f1 = 18.3;
double f2 = 23.456;
unsigned int u1 = 302;
unsigned long long u2 = 348;
//mprintf(buf, "number is %d | negative number is %d | float number is %f | double number is %f | unsigned number is %u | unsigned long long number is %u | hexadecimal is %x | hexadecimal is %X | pointer is %p | hello %V | character is %%", a1, a2, f1, f2, u1, u2, u1, u1, &u1, &str);
mprintf(buf, "%s with you.", "fly");
printf("%s\n", buf);
}
int main()
{
test();
return 0;
}
描述
C 库宏 void va_start(va_list ap, last_arg) 初始化 ap 变量,它与 va_arg 和 va_end 宏是一起使用的。last_arg 是最后一个传递给函数的已知的固定参数,
即省略号之前的参数。这个宏必须在使用 va_arg 和 va_end 之前被调用。
声明
void va_start(va_list ap, last_arg);
参数
ap -- 这是一个 va_list 类型的对象,它用来存储通过 va_arg 获取额外参数时所必需的信息。
last_arg -- 最后一个传递给函数的已知的固定参数。
返回值
NA
------------------------------------------------------------------------
描述
C 库宏 type va_arg(va_list ap, type) 检索函数参数列表中类型为 type 的下一个参数。它无法判断检索到的参数是否是传给函数的最后一个参数。
声明
type va_arg(va_list ap, type)
参数
ap -- 这是一个 va_list 类型的对象,存储了有关额外参数和检索状态的信息。该对象应在第一次调用 va_arg 之前通过调用 va_start 进行初始化。
type -- 这是一个类型名称。该类型名称是作为扩展自该宏的表达式的类型来使用的。
返回值
该宏返回下一个额外的参数,是一个类型为 type 的表达式。
------------------------------------------------------------------------
描述
C 库宏 void va_end(va_list ap) 允许使用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则结果为未定义。
声明
void va_end(va_list ap)
参数
ap -- 这是之前由同一函数中的 va_start 初始化的 va_list 对象。
返回值
NA