C указатель на указатели константного и неконстантного типов - PullRequest
0 голосов
/ 19 июня 2020

Я хотел бы объявить указатель, который будет указывать на указатели, где некоторые из этих указателей будут const, а другие будут неконстантными указателями.

Ниже приведен игрушечный пример. Имею набор columns. Каждый столбец является указателем на данные типов int или double. Пока это работает нормально. Я также хотел бы использовать указатели const.

#include<stdlib.h>
#include<stdio.h>
#define TYPE_INT 0
#define TYPE_DOUBLE 1

int main(void) {
  int ncol = 2;
  int nrow = 3;
  void **columns = malloc(ncol*sizeof(void*));
  int *types = malloc(ncol*sizeof(int));
  columns[0] = malloc(nrow*sizeof(int));
  types[0] = TYPE_INT;
  columns[1] = malloc(nrow*sizeof(double));
  types[1] = TYPE_DOUBLE;
  for (int i=0; i<ncol; ++i) {
    for (int j=0; j<nrow; ++j) {
      printf("value of column %d and row %d is: ", i+1, j+1);
      types[i]==TYPE_INT ?
        printf("%d", ((int*)columns[i])[j]) :
        printf("%.3f", ((double*)columns[i])[j]);
      printf("\n");
    }
  }
  return 0;
}
value of column 1 and row 1 is: 0
value of column 1 and row 2 is: 0
value of column 1 and row 3 is: 0
value of column 2 and row 1 is: 0.000
value of column 2 and row 2 is: 0.000
value of column 2 and row 3 is: 0.000

Если я попытаюсь изменить double * на const double*

int main(void) {
  int ncol = 2;
  int nrow = 3;
  void **columns = malloc(ncol*sizeof(void*));
  int *types = malloc(ncol*sizeof(int));
  columns[0] = malloc(nrow*sizeof(int));
  types[0] = TYPE_INT;
  columns[1] = (const double*)malloc(nrow*sizeof(double));
  types[1] = TYPE_DOUBLE;
  for (int i=0; i<ncol; ++i) {
    for (int j=0; j<nrow; ++j) {
      printf("value of column %d and row %d is: ", i+1, j+1);
      types[i]==TYPE_INT ?
        printf("%d", ((int*)columns[i])[j]) :
        printf("%.3f", ((const double*)columns[i])[j]);
      printf("\n");
    }
  }
  return 0;
}

, то gcc предупреждает

ptrs.c: In function ‘main’:
ptrs.c:13:14: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
   columns[1] = (const double*)malloc(nrow*sizeof(double));

Как сохранить вместе указатели столбцов, неважно, const они или нормальные?

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

Основываясь на том, что ваши массивы (columns и types) индексируются одним и тем же индексом [столбец], я делаю вывод, что данная строка должна быть одного типа?

У вас есть [по существу] динамический c 2D «разреженный» массив.

Вместо использования двойных звездочек (например, void **) я бы создал несколько структур для расширения.

Это может быть немного переоформления, но вот мой взгляд на это:

#include <stdlib.h>
#include <stdio.h>

typedef enum {
    TYPE_INT,
    TYPE_DOUBLE,
    TYPE_CONST_INT,
    TYPE_CONST_DOUBLE,
} type_t;

typedef union {
    void *vp;
    const void *vpc;

    int *ip;
    const int *ipc;

    double *dp;
    const double *dpc;
} rowptr_t;

typedef struct {
    int type;
    rowptr_t *rowptr;
} column_t;

typedef struct {
    int mat_ncol;
    int mat_nrow;
    column_t *mat_base;
} matrix_t;

size_t
typesize(type_t type)
{
    size_t siz;

    siz = 0;

    switch (type) {
    case TYPE_INT:
    case TYPE_CONST_INT:
        siz = 4;
        break;

    case TYPE_DOUBLE:
    case TYPE_CONST_DOUBLE:
        siz = 8;
        break;
    }

    return siz;
}

const char *
typefmt(type_t type)
{
    const char *fmt;

    fmt = NULL;

    switch (type) {
    case TYPE_INT:
    case TYPE_CONST_INT:
        fmt = "%d";
        break;

    case TYPE_DOUBLE:
    case TYPE_CONST_DOUBLE:
        fmt = "%Lf";
        break;
    }

    return fmt;
}

void
typeprt(matrix_t *mat,int colidx,int rowidx)
{
    column_t *col;
    rowptr_t *row;
    const void *vpc;

    col = &mat->mat_base[colidx];
    row = &col->rowptr[rowidx];

    vpc = row->vpc;

    switch (col->type) {
    case TYPE_INT:
    case TYPE_CONST_INT:
        printf("%d",*(const int *) vpc);
        break;

    case TYPE_DOUBLE:
    case TYPE_CONST_DOUBLE:
        printf("%.3f",*(const double *) vpc);
        break;
    }
}

void
matinit(matrix_t *mat,int ncol,int nrow)
{

    mat->mat_nrow = nrow;
    mat->mat_ncol = ncol;

    mat->mat_base = calloc(ncol,sizeof(*mat->mat_base));
}

void
allocrow(matrix_t *mat,int colidx,type_t type)
{
    column_t *col;
    size_t siz;

    siz = typesize(type);

    col = &mat->mat_base[colidx];
    col->rowptr = malloc(mat->mat_nrow * siz);
    col->type = type;
}

int
main(void)
{
    matrix_t mat;

    matinit(&mat,2,3);
    allocrow(&mat,0,TYPE_INT);
    allocrow(&mat,1,TYPE_DOUBLE);

    for (int i = 0; i < mat.mat_ncol; ++i) {
        for (int j = 0; j < mat.mat_nrow; ++j) {
            printf("value of column %d and row %d is: ", i + 1, j + 1);
            typeprt(&mat,i,j);
            printf("\n");
        }
    }

    return 0;
}
1 голос
/ 19 июня 2020

Вы можете это сделать, но во время присваивания вам нужно явно привести указатель const к неконстантному. Также добавьте дополнительные #define s для типов const, чтобы вы знали, что хранится в конкретном столбце:

#define TYPE_CONST_DOUBLE 2;

//...

//a const pointer storing an address of some buffer
const double *ptr = (const double*)malloc(nrow*sizeof(double));

//now we cast the const pointer to a non-const one and put it into the columns array
columns[1] = (void*)ptr;
types[1] = TYPE_CONST_DOUBLE;

И когда вы читаете значение из columns[n], просто верните его к const double *, если тип TYPE_CONST_DOUBLE.

...