我正在尝试实现打印2D数据的通用功能.我想出的是:
int mprintf(FILE* f, char* fmt, void** data, size_t cols, size_t rows)
Run Code Online (Sandbox Code Playgroud)
我们面临的挑战是确定有多少位从一次读取data的基础上fmt.
格式fmt将是stdlib特定的格式printf()和类似.
你对stdlibc(GNU GCC C)已经存在的功能有什么了解吗?我可以用它来缓解这个问题吗?
我尝试避免必须手动完成所有操作,因为我知道"我很蠢"(我不想引入愚蠢的错误).因此,重用代码将是最无错误的方式.
谢谢
附录
我看到有一个/usr/include/printf.h.我不能使用任何这些功能来做到这一点并同时轻松完成我的工作吗?
有问题的设计:
int mprintf(FILE *f, char *fmt, void **data, size_t cols, size_t rows);
Run Code Online (Sandbox Code Playgroud)
格式字符串应为a const char *.
显然,您的代码可以做printf()或多或少做的事情.它查看格式转换说明符,然后确定要收集的类型.在某些方面,您的代码会稍微复杂一些.你需要治疗的阵列unsigned char由阵列不同short,等等C99提供了修饰hh的signed char或unsigned char(格式说明符之前d,i,o,u,x,或X),并修改h为short或unsigned short.你也应该认识到这些.同样,应该处理Lfor long double和lfor long和llfor 的修饰符long long.有趣的是,printf()不必处理float(因为任何单个float值会自动提升double),但您的代码必须这样做.通过类比h和L,你应该H用作修饰符来表示一个float数组.请注意,这种情况意味着您需要将该printf()函数传递给与用户指定的格式不同的格式.您可以复制用户提供的格式,删除'H'(或者使用用户提供的格式,除非它包含'H';您不会修改用户的格式字符串 - 尤其是因为修改后的界面说这是一个恒定的字符串).
最终,您的代码必须确定数组中元素的大小.可能是您修改接口以包含该信息 - 类似于bsearch()和qsort(),或fread()等函数fwrite().或者您可以从格式说明符中确定它.
请注意,虽然GCC允许指针运算void *,但标准C不允许.
你确定你想要一个void **界面吗?我认为如果你传递数组的起始元素的地址 - 单个指针级别会更容易理解.
short s[3][4];
float f[2][5];
char c[20][30];
mprintf(fp, "%3hd", &s[0][0], 4, 3);
mprintf(fp, "%8.4Hf", &f[0][0], 5, 2);
mprintf(fp, "%hhu", &c[0][0], 30, 20);
Run Code Online (Sandbox Code Playgroud)
这会将data参数更改为a void *.也许我太不含咖啡因,但我看不出如何使双指针工作得当.
此代码假定"0是成功"约定.它假设您正在处理数字,而不是指针或字符串的矩阵.
typedef int (*PrintItem)(FILE *fp, const char *format, void *element);
static int printChar(FILE *fp, const char *format, void *element)
{
char c = *(char *)element;
return (fprintf(fp, format, c) <= 0) ? -1 : 0;
}
...and a whole lot more like this...
static int printLongDouble(FILE *fp, const char *format, void *element)
{
long double ld = *(long double *)element;
return (fprintf(fp, format, ld) <= 0) ? -1 : 0;
}
int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows)
{
char *format = strdup(fmt);
int rc = 0;
size_t size;
PrintItem print;
if ((rc = print_info(format, &size, &print)) == 0)
{
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
void *element = (char *)data + (i * cols + j) * size;
if ((rc = print(fp, format, element)) < 0)
goto exit_loop;
}
fputc('\n', fp); // Possible error ignored
}
}
exit_loop:
free(fmt);
return rc;
}
static int print_info(char *fmt, size_t *size, PrintItem *print)
{
...analyze format string...
...set *size to the correct size...
...set *print to the correct printing function...
...modify format string if need so be...
...return 0 on success, -1 on failure...
}
Run Code Online (Sandbox Code Playgroud)
作为练习留下:
size_tintmax_tptrdiff_t请注意,我通常不会在与其他作业相同的行上使用+=或*=运算符; 但是,生成测试数字很方便.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
/* mprintf() - print a matrix of size cols x rows */
extern int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows);
typedef int (*PrintItem)(FILE *fp, const char *format, void *element);
static int printChar(FILE *fp, const char *format, void *element)
{
char value = *(char *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printShort(FILE *fp, const char *format, void *element)
{
short value = *(short *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printInt(FILE *fp, const char *format, void *element)
{
int value = *(int *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printLong(FILE *fp, const char *format, void *element)
{
long value = *(long *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printLongLong(FILE *fp, const char *format, void *element)
{
long long value = *(long long *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printFloat(FILE *fp, const char *format, void *element)
{
float value = *(float *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printDouble(FILE *fp, const char *format, void *element)
{
double value = *(double *)element;
return (fprintf(fp, format, value) <= 0) ? -1 : 0;
}
static int printLongDouble(FILE *fp, const char *format, void *element)
{
long double valued = *(long double *)element;
return (fprintf(fp, format, valued) <= 0) ? -1 : 0;
}
/* analyze format string - all arguments can be modified */
static int print_info(char *format, size_t *size, PrintItem *print)
{
char *fmt = format;
char c;
bool scanning_type = false;
int hcount = 0;
int lcount = 0;
int Hcount = 0;
int Lcount = 0;
char *Hptr = 0;
while ((c = *fmt++) != '\0')
{
switch (c)
{
case '%':
if (*fmt == '%')
fmt++;
else
scanning_type = true;
break;
/* Length modifiers */
case 'h':
if (scanning_type)
hcount++;
break;
case 'l':
if (scanning_type)
lcount++;
break;
case 'L':
if (scanning_type)
Lcount++;
break;
case 'H':
if (scanning_type)
{
Hptr = fmt - 1;
Hcount++;
}
break;
/* Integer format specifiers */
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
if (scanning_type)
{
/* No floating point modifiers */
if (Hcount > 0 || Lcount > 0)
return -1;
/* Can't be both longer and shorter than int at the same time */
if (hcount > 0 && lcount > 0)
return -1;
/* Valid modifiers are h, hh, l, ll */
if (hcount > 2 || lcount > 2)
return -1;
if (hcount == 2)
{
*size = sizeof(char);
*print = printChar;
}
else if (hcount == 1)
{
*size = sizeof(short);
*print = printShort;
}
else if (lcount == 2)
{
*size = sizeof(long long);
*print = printLongLong;
}
else if (lcount == 1)
{
*size = sizeof(long);
*print = printLong;
}
else
{
*size = sizeof(int);
*print = printInt;
}
return 0;
}
break;
/* Floating point format specifiers */
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
if (scanning_type)
{
/* No integer modifiers */
if (lcount > 0 || hcount > 0)
return -1;
/* Can't be both float and long double at once */
if (Lcount > 0 && Hcount > 0)
return -1;
/* Cannot repeat L or H modifiers */
if (Lcount > 1 || Hcount > 1)
return -1;
if (Lcount > 0)
{
*size = sizeof(long double);
*print = printLongDouble;
}
else if (Hcount > 0)
{
/* modify format string, dropping the H */
assert(Hptr != 0 && strlen(Hptr+1) > 0);
memmove(Hptr, Hptr+1, strlen(Hptr)); // Copy '\0' too!
*size = sizeof(float);
*print = printFloat;
}
else
{
*size = sizeof(double);
*print = printDouble;
}
return 0;
}
break;
default:
break;
}
}
return -1;
}
int mprintf(FILE *fp, const char *fmt, void *data, size_t cols, size_t rows)
{
char *format = strdup(fmt); // strdup() is not standard C99
int rc = 0;
size_t size;
PrintItem print;
if ((rc = print_info(format, &size, &print)) == 0)
{
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
void *element = (char *)data + (i * cols + j) * size;
if ((rc = print(fp, format, element)) < 0)
{
fputc('\n', fp); // Or fputs("<<error>>\n");
goto exit_loop;
}
}
fputc('\n', fp); // Possible error ignored
}
}
exit_loop:
free(format);
return rc;
}
#ifdef TEST
int main(void)
{
short s[3][4];
float f[2][5];
char c[8][9];
FILE *fp = stdout;
int v = 0;
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 4; j++)
{
s[i][j] = (v += 13) & 0x7FFF;
printf("s[%zu,%zu] = %hd\n", i, j, s[i][j]);
}
}
v = 0;
for (size_t i = 0; i < 8; i++)
{
for (size_t j = 0; j < 9; j++)
{
c[i][j] = (v += 13) & 0x7F;
printf("c[%zu,%zu] = %hhu\n", i, j, c[i][j]);
}
}
float x = 1.234;
for (size_t i = 0; i < 2; i++)
{
for (size_t j = 0; j < 5; j++)
{
f[i][j] = x *= 13.12;
printf("f[%zu,%zu] = %g\n", i, j, f[i][j]);
}
}
mprintf(fp, " %5hd", &s[0][0], 4, 3);
mprintf(fp, "%%(%3hhu) ", &c[0][0], 8, 9);
mprintf(fp, " %11.4He", &f[0][0], 5, 2);
mprintf(fp, " %11.4He", f, 5, 2);
return 0;
}
#endif /* TEST */
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1222 次 |
| 最近记录: |