Woking с простой функцией для печати similated 2D матрицы, я просто хочу, чтобы иметь возможность передавать массив указателей на тип функции, как void**
, наряду с необходимыми размерами m x n
, в sizeof type
, формат строку а для printf
и простой флаг, чтобы отличить с плавающей запятой от integer. Проблема, с которой я сталкиваюсь, - это обработка области для разыменования массива указателей, чтобы каждый элемент мог быть правильно напечатан как исходный тип. Ниже, основная схема:Как пройти (void **) до функции и эффективно Развернуть/Использовать для любого типа?
void mtrx_prnv (size_t m, size_t n, void **matrix,
size_t sz, char *fmt, char fp)
{
if (fp) { // floating point
if (sz == 4) {
float **mtrx = (float **)matrix;
...
}
else if (sz == 8) {
double **mtrx = (double **)matrix;
...
}
}
else {
if (sz == 1) {
char **mtrx = (char **)matrix;
...
}
else if (sz == 2) {
...
}
...
}
}
подход отлично работает, но проблема заключается в том, что после тестирования для плавающей запятой флага 'fp'
и SizeOf типа 'sz'
, любое создание указателя использовать для разыменования ограничиваются объемом тестового блока и заканчивается тем, что требует базового дословного дублирования кода, необходимого для обработки массива указателей в каждом блоке. Код заканчивается длиннее, чем создание множества небольших функций для каждого типа. например .:
void mtrx_prn_float (size_t m, size_t n, float **matrix) {}
void mtrx_prn_int (size_t m, size_t n, int **matrix) {}
Есть ли лучше или стандартный способ передать массив указателей типа, как void**
, тип SIZEOF, и любые другие флаги необходимы, чтобы правильно разыменовать печатать без такого большого дублирования кода? Если так оно и должно быть, все в порядке, но я хочу убедиться, что я не пропустил простой трюк. Пока мои поиски не принесли ничего полезного (подавляющая часть информации, возвращаемая поиском, о том, как вернуть один тип, а не отделить все типы). How to write C function accepting (one) argument of any type не был здесь.
Полная функция (без дополнительной логики для разделения int/unsigned
) приведена ниже.
/* printf array of pointers to type as 2D matrix of
size 'm x n' with sizeof type 'sz', format string
'fmt' and floating point flag 'fp' (0 - int).
*/
void mtrx_prnv (size_t m, size_t n, void **matrix,
size_t sz, char *fmt, char fp)
{
register size_t i, j;
if (fp) { /* floating point */
if (sz == 4) {
float **mtrx = (float **)matrix;
for (i = 0; i < m; i++) {
char *pad = " [ ";
for (j = 0; j < n; j++) {
printf (fmt, pad, mtrx[i][j]);
pad = ", ";
}
printf ("%s", " ]\n");
}
}
else if (sz == 8) {
double **mtrx = (double **)matrix;
for (i = 0; i < m; i++) {
char *pad = " [ ";
for (j = 0; j < n; j++) {
printf (fmt, pad, mtrx[i][j]);
pad = ", ";
}
printf ("%s", " ]\n");
}
}
else
goto err;
}
else { /* integer (no unsigned yet) */
if (sz == 1) {
char **mtrx = (char **)matrix;
for (i = 0; i < m; i++) {
char *pad = " [ ";
for (j = 0; j < n; j++) {
printf (fmt, pad, mtrx[i][j]);
pad = ", ";
}
printf ("%s", " ]\n");
}
}
else if (sz == 2) {
short **mtrx = (short **)matrix;
for (i = 0; i < m; i++) {
char *pad = " [ ";
for (j = 0; j < n; j++) {
printf (fmt, pad, mtrx[i][j]);
pad = ", ";
}
printf ("%s", " ]\n");
}
}
else if (sz == 4) {
int **mtrx = (int **)matrix;
for (i = 0; i < m; i++) {
char *pad = " [ ";
for (j = 0; j < n; j++) {
printf (fmt, pad, mtrx[i][j]);
pad = ", ";
}
printf ("%s", " ]\n");
}
}
else if (sz == 8) {
long **mtrx = (long **)matrix;
for (i = 0; i < m; i++) {
char *pad = " [ ";
for (j = 0; j < n; j++) {
printf (fmt, pad, mtrx[i][j]);
pad = ", ";
}
printf ("%s", " ]\n");
}
}
else
goto err;
}
return;
err:;
fprintf (stderr, "%s() error: invalid size for fp_flag '%zu'.\n",
__func__, sz);
}
Как указывается в комментариях, декларация/определение обновлена:
void mtrx_prnv (size_t m, size_t n, void *matrix,
size_t sz, char *fmt, char fp);
Macro Предложенного решение
Существовало несколько превосходных решений предложили, в первую очередь устранить дословное дублирование создание макроса для избыточного текста, а во-вторых, использование обратных вызовов для печати значений элементов. Макрос предусмотрен вблизи капли в растворе:
#define PRINT_MATRIX(type) do { \
type **mtrx = (type **)matrix; \
for (i = 0; i < m; i++) { \
char *pad = " [ "; \
for (j = 0; j < n; j++) { \
printf (fmt, pad, mtrx[i][j]); \
pad = ", "; \
} \
printf ("%s", " ]\n"); \
} \
} while (0)
...
void mtrx_prnvm (size_t m, size_t n, void *matrix,
size_t sz, char *fmt, char fp)
{
register size_t i, j;
if (fp) { /* floating point */
if (sz == 4) {
PRINT_MATRIX(float);
}
else if (sz == 8) {
PRINT_MATRIX(double);
}
else
goto err;
}
else { /* integer (no unsigned yet) */
if (sz == 1) {
PRINT_MATRIX(char);
}
else if (sz == 2) {
PRINT_MATRIX(short);
}
else if (sz == 4) {
PRINT_MATRIX(int);
}
else if (sz == 8) {
PRINT_MATRIX(long);
}
else
goto err;
}
return;
err:;
fprintf (stderr, "%s() error: invalid size for fp_flag '%zu'.\n",
__func__, sz);
}
Тип аргумента 'matrix' должен действительно быть просто' void * ', а не' void ** ', если вы хотите, чтобы этот код был правильно переносимым. Изменение этого не должно иметь какого-либо заметного эффекта, так что вы тоже можете это сделать. – davmac
Как сказал @davmac. Любое значение указателя объекта можно безопасно округлить с помощью преобразования в void *, но это не обязательно означает, что 'void *' имеет то же * представление *, что и любой другой тип указателя. В результате, хотя листинг 'void *' to 'other_type **' является безопасным (-ish), литье 'void **' в 'other_type **' не является * безопасным. –
Это имеет смысл. Я всегда могу получить свой 'other_type **' обратно, если матрица 'void *' –