0

我已经尝试过这种方式。然后在输出中得到垃圾并发现我做错了。

#include "stdlib.h"
#include "stdio.h"

/*
 * Prints array from the anonymous array pointer.
 *
 * Returns 0 on success.
 * Return -1 on failure.
 */
int print_array(const void *arr, size_t arr_len,
                size_t elem_size, const char *format) {
    for (size_t i = 0; i < arr_len; ++i)
        if (printf(format, (char *) arr + i * elem_size) < 0)
            return -1;
    printf("\n");

    return 0;
}

// Usage example
int main() {
    const int n = 3;
    int arr[n];
    for (int i = 0; i < n; ++i)
        arr[i] = i;
    print_array(arr, n, sizeof(int), "%d ");
}

我的代码应该适用于不同的可能类型。类型实际上是在执行开始的某个地方推导出来的,然后使用这个最小描述。这种技巧很好地适用,scanf因为它需要指向元素而不是元素本身的指针。

需要这种复杂性来符合开闭原则,从而最大限度地减少代码中的开关和条件句的数量。我试图只将实际类型推导集中在一个地方。

我想与 with 相同的事情scanf应该是可能的,因为printf它实际上是一个可变参数函数,它可以在运行时获取任何值并解析它们。滥用“stdarg.h”可能会有所帮助。

当然,我希望有不是特定于编译器的解决方案。

4

4 回答 4

0

与其传递格式化字符串,不如传递一个函数来打印它,允许用户做他想做/需要的任何事情来打印实际数据。

int print_array(const void *arr, size_t arr_len, size_t elem_size,
       int (*print_elem)(const void *el, void *cookie), // I would add FILE* argument
       void *cookie // allow users to pass additional context if they wanna
) {
    for (size_t i = 0; i < arr_len; ++i) {
        if (print_elem((char*)arr + i * elem_size, cookie) < 0) {
            return -1;
        }
    }
    printf("\n");
    return 0;
}

int print_int(const void *el, void *cookie) {
    return printf("%d", *(int*)el);
}

// print and allow users to pass options
int print_int_with_width(const void *el, void *cookie) {
    int width = *(int*)cookie;
    return printf("%*d", width, *(int*)el);
}

int arr[5];
int main() {
    print_array(arr, 5, sizeof(int), print_int, NULL);
    int width = 5;
    print_array(arr, 5, sizeof(int), print_int_with_width, &width);
}
于 2020-11-23T18:22:10.607 回答
0
typedef int (*print_fn)(char*,void*);
    
int print_char  (char *fmt, void *c)   { return printf(fmt, *((char*) c));    }
int print_int   (char *fmt, void *i)   { return printf(fmt, *((int*) i));     }
int print_float (char *fmt, void *f)   { return printf(fmt, *((float*) f));   }
int print_str   (char *fmt, void *str) { return printf(fmt, *((char**) str)); }
// add more types here
    
int print_array (
    void *arr, 
    size_t arr_len, 
    size_t elem_size, 
    char* fmt, 
    print_fn print_elem
){
    for (void *end = arr + arr_len * elem_size; arr < end; arr += elem_size)
        if (print_elem(fmt, arr) < 0)
            return -1;
    return 0;
}
于 2020-11-23T18:46:06.940 回答
0
int print_array(const char *format, const void *arr, size_t arr_len, size_t elem_size, int fp) 
{
    const uint8_t *uarr = arr;
    uint8_t u8;
    uint16_t u16;
    uint32_t u32;
    uint64_t u64;
    float f; double d;
    int result;

    for (size_t i = 0; i < arr_len; ++i)
    {
        switch(elem_size)
        {
            case 1:
                memcpy(&u8, uarr + i, 1); 
                result = printf(format, u8);
                break;
            case 2:
                memcpy(&u16, uarr + i * 2, 2); 
                result = printf(format, u16);
                break;
            case 4:
                if(fp)
                {
                    memcpy(&f, uarr + i * 4, 4); 
                    result = printf(format, f);
                }
                else
                {
                    memcpy(&u32, uarr + i * 4, 4); 
                    result = printf(format, u32);
                }
                break;
            case 8:
                if(fp)
                {
                    memcpy(&d, uarr + i * 8, 8); 
                    result = printf(format, d);
                }
                else
                {
                    memcpy(&u64, uarr + i * 8, 8); 
                    result = printf(format, u64);
                }
                break;
            default:
                result = -1;
                break;
        }
    }
    return result;
}

// Usage example
int main()
 {
    const int n = 3;
    int arr[n];
    double arrd[3]; 
    for (int i = 0; i < n; ++i)
    {
        arr[i] = i;
        arrd[i] = 1.1 * i;
    }
    print_array("%d\n", arr, n, sizeof(*arr), 0);
    print_array("%f\n", arrd, n, sizeof(*arrd), 1);
}
于 2020-11-23T19:01:01.927 回答
0

另一种使用宏的方法,您不需要传递每个元素的大小:

#include <stdio.h>

#define print_array(format, array, elems) \
do {                                      \
    for (int ___ = 0; ___ < elems; ___++) \
        printf(format, array[___]);       \
    printf("\n");                         \
} while (0)

int main (void)
{
    char *str[] = {"one", "two", "three"};
    print_array("%s ", str, 3);

    int arr[] = {1, 2, 3};
    print_array("%d ", arr, 3);

    return 0;
}

___用作迭代器来防止遮蔽/污染变量名。

void *与附加到回调的优势相比,编译器可以检测到错误的格式说明符,即传递s 返回"%s "的数组:int

demo.c: In function ‘main’:
demo.c:16:22: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
   16 |     print_array(arr, "%s ", 3);
      |                      ^~~~~
demo.c:6:16: note: in definition of macro ‘print_array’
    6 |         printf(format, array[___]);       \
      |                ^~~~~~
demo.c:16:24: note: format string is defined here
   16 |     print_array(arr, "%s ", 3);
于 2020-11-23T19:30:45.197 回答