Pergunta

Eu estava tentando criar uma pseudo super estrutura para imprimir a matriz de estruturas. Minhas estruturas básicas são as seguintes.

/* Type 10 Count */
typedef struct _T10CNT
{
    int _cnt[20];
} T10CNT;

...

/* Type 20 Count */
typedef struct _T20CNT
{
    long _cnt[20];
} T20CNT;
...

Criei a estrutura abaixo para imprimir a matriz de estruturas acima mencionadas. Recebi erro de ponteiro de vazio de referência ao compilar o snippet de código abaixo.

typedef struct _CMNCNT
{
    long  _cnt[3];
} CMNCNT;

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    int ii;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];
        fprintf(stout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

T10CNT struct_array[10];
...
printCommonStatistics(struct_array, NELEM(struct_array), sizeof(struct_array[0]);
...

Minha intenção é ter uma função comum para imprimir todas as matrizes. Por favor, deixe -me saber a maneira correta de usá -lo.

Aprecie a ajuda com antecedência.

Editar: o nome do parâmetro é alterado para cmncntin de cmncnt. Desculpe, foi um erro de digitação.

Obrigado, Mathew Liju

Foi útil?

Solução

Acho que seu design falhará, mas também não estou convencido de que as outras respostas que vejo lidar totalmente com as razões mais profundas.

Parece que você está tentando usar C para lidar com tipos genéricos, algo que sempre fica peludo. Você pode fazer isso, se tiver cuidado, mas não é fácil e, nesse caso, duvido que valesse a pena.

Razão mais profunda: Vamos supor que ultrapassemos os meros problemas sintáticos (ou quase mais do que sintáticos). Seu código mostra que o T10CNT contém 20 int e T20CNT contém 20 long. Em máquinas modernas de 64 bits - exceto em Win64 - sizeof(long) != sizeof(int). Portanto, o código dentro da sua função de impressão deve estar distinguindo entre a desreferência int matrizes e long matrizes. No C ++, há uma regra que você não deve tentar tratar as matrizes polimorficamente, e esse tipo de coisa é o porquê. O tipo cmncnt contém 3 long valores; Diferente das estruturas T10CNT e T20CNT em número, embora o tipo base da matriz corresponda ao T20CNT.

Recomendação de estilo: Eu recomendo fortemente evitar os principais sublinhados nos nomes. Em geral, os nomes que começam com o sublinhado são reservados para a implementação usar e usar como macros. As macros não têm respeito pelo escopo; Se a implementação define uma macro _cnt, ele destruiria seu código. Existem nuances para quais nomes são reservados; Não estou prestes a entrar nessas nuances. É muito mais simples pensar que 'os nomes começando com o sublêmio são reservados', e isso o deixará afastar problemas.

Sugestão de estilo: Sua função de impressão retorna o sucesso incondicionalmente. Isso não é sensato; Sua função não deve retornar nada, para que o chamador não precise testar sucesso ou falha (pois nunca pode falhar). Um codificador cuidadoso que observa que a função retorna um status sempre testará o status de retorno e possui código de manuseio de erros. Esse código nunca será executado, por isso está morto, mas é difícil para qualquer um (ou o compilador) determinar isso.

Correção de superfície: Temporariamente, podemos assumir que você pode tratar int e long como sinônimos; Mas você deve sair do hábito de pensar que eles são sinônimos. o void * O argumento é a maneira correta de dizer "essa função leva um ponteiro do tipo indeterminado". No entanto, dentro da função, você precisa se converter de um void * para um tipo específico antes de fazer a indexação.

typedef struct _CMNCNT
{
    long    count[3];
} CMNCNT;

static void printCommonStatistics(const void *data, size_t nelem, size_t elemsize)
{
    int i;
    for (i = 0; i < nelem; i++)
    {
        const CMNCNT *cmncnt = (const CMNCNT *)((const char *)data + (i * elemsize));
        fprintf(stdout,"STATISTICS_INP: %ld\n", cmncnt->count[0]);
        fprintf(stdout,"STATISTICS_OUT: %ld\n", cmncnt->count[1]); 
        fprintf(stdout,"STATISTICS_ERR: %ld\n", cmncnt->count[2]);
    }
}

(Eu gosto da ideia de um fluxo de arquivos chamado stout também. Sugestão: Use Cut'n'Paste no código-fonte real-é mais seguro! Eu geralmente sou uso "sed 's/^/ /' file.c"Para preparar o código para o corte de uma resposta em uma resposta.)

O que essa linha de elenco faz? Estou feliz que você perguntou ...

  • A primeira operação é converter o const void * dentro de const char *; Isso permite que você faça operações de tamanho de byte no endereço. Nos dias anteriores ao padrão C, char * foi usado no lugar de void * como o mecanismo de endereçamento universal.
  • A próxima operação adiciona o número correto de bytes para chegar ao início do io elemento da matriz de objetos de tamanho elemsize.
  • O segundo elenco então diz ao compilador "Confie em mim - eu sei o que estou fazendo" e "trate esse endereço como o endereço de uma estrutura CMNCNT".

A partir daí, o código é fácil o suficiente. Observe que, uma vez que a estrutura CMNCNT contém long valor eu usei %ld para dizer a verdade para fprintf().

Como você não está prestes a modificar os dados nesta função, não é uma má idéia usar o const qualificador como eu fiz.

Observe que se você for fiel a sizeof(long) != sizeof(int), então você precisa de dois blocos separados de código (eu sugeriria funções separadas) para lidar com a 'matriz de int'e' array de long'Tipos de estrutura.

Outras dicas

O tipo de vazio é deliberadamente deixado incompleto. A partir disso, segue -se que você não pode desreferenciar os ponteiros vazios, e nem você pode levar o tamanho. Isso significa que você não pode usar o operador subscrito usando -o como uma matriz.

No momento em que você atribui algo a um ponteiro vazio, qualquer tipo de informação do original apontado para digitar é perdido, para que você possa desreferenciar apenas se primeiro lançá -las de volta ao tipo de ponteiro original.

Primeiro e o mais importante, você passa T10CNT* para a função, mas você tenta digitar (e desreferenciar) que CMNCNT* em sua função. Isso não é um comportamento válido e indefinido.

Você precisa de uma função PrintCommonStatistics para cada tipo de elementos da matriz. Então, tenha umprintCommonStatisticsInt, printCommonStatisticsLong, printCommonStatisticsChar que todos diferem por seu primeiro argumento (um tomando int*, o outro tomando long*, e assim por diante). Você pode criá -los usando macros, para evitar o código redundante.

Passar a estrutura em si não é uma boa idéia, desde então você deve definir uma nova função para cada tamanho diferente da matriz contida dentro da estrutura (uma vez que todos são tipos diferentes). Tão melhor passar a matriz contida diretamente (struct_array[0]._cnt, chame a função para cada índice)

Altere a declaração de função para char * como assim:

static int printCommonStatistics(char *cmncnt, int cmncnt_nelem, int cmncnt_elmsize)

O tipo de vazio não assume nenhum tamanho específico, enquanto um char assumirá um tamanho de byte.

Você não pode fazer isso:

cmncnt->_cnt[0]

Se o CMNCT for um ponteiro vazio.

Você precisa especificar o tipo. Pode ser necessário repensar sua implementação.

A função

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    char *cmncntinBytes;
    int ii;

    cmncntinBytes = (char *) cmncntin;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)(cmncntinBytes + ii*cmncnt_elmsize);  /* Ptr Line */
        fprintf(stdout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stdout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stdout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

Funciona para mim.

A questão é que, na linha, comentou a "linha PTR", o código adiciona um ponteiro a um número inteiro. Como nosso ponteiro é um char * avançamos no tamanho da memória (char) * ii * cmncnt_elemsize, que é o que queremos, já que um char é um byte. Seu código tentou fazer uma coisa equivalente a avançar em tamanho (void) * ii * cmncnt_elemsize, mas o void não tem tamanho, então o compilador deu o erro.

Eu alteraria o T10CNT e o T20CNT para usar int ou longo em vez de um com cada um. Você depende do tamanhoof (int) == sizeof (longo)

Nesta linha:

CMNCNT *cmncnt = (CMNCNT *)&cmncnt[ii*cmncnt_elmsize];

Você está tentando declarar uma nova variável chamada CMNCNT, mas uma variável com esse nome já existe como um parâmetro para a função. Você pode querer usar um nome de variável diferente para resolver isso.

Além disso, você pode passar um ponteiro para um cmncnt para a função em vez de um ponteiro vazio, porque o compilador fará o ponteiro aritmético por você e você não precisa lançá -lo. Não vejo o ponto de passar um ponteiro vazio quando tudo o que você faz com ele é lançado para um cmncnt. (O que não é um nome muito descritivo para um tipo de dados, a propósito.)

Sua expressão

(CMNCNT *)&cmncntin[ii*cmncnt_elmsize]

tenta tomar o endereço de cmncntin [ii *cmncnt_elmsize] e depois lançar esse ponteiro para digitar (cmncnt *). Não pode obter o endereço de cmncntin [ii*cmncnt_elmsize] porque o cmncntin possui o tipo vazio*.

O operador do estudo C precedências e insira parênteses quando necessário.

Ponto da informação: o preenchimento interno pode realmente estragar tudo.

Considere struct {char c [6]; }; - tem sizeof () = 6. Mas se você tivesse uma matriz destes, cada elemento pode ser preso a um alinhamento de 8 bytes!

Certas operações de montagem não lidam com dados mal alinhados com graciosidade. (Por exemplo, se um int abrange duas palavras de memória.) (Sim, já fui mordido por isso antes.)

.

Segundo: No passado, usei matrizes de tamanho variável. (Eu era burro naquela época ...) Funciona se você não estiver mudando de tipo. (Ou se você tiver uma união dos tipos.)

Por exemplo:

struct T { int sizeOfArray;  int data[1]; };

Alocado como

T * t = (T *) malloc( sizeof(T) + sizeof(int)*(NUMBER-1) );
                      t->sizeOfArray = NUMBER;

(Embora o preenchimento/alinhamento ainda possa estragar tudo.)

.

Terceiro: Considere:

   struct T {
     int sizeOfArray;
     enum FOO arrayType;
     union U { short s; int i; long l; float f; double d; } data [1];
    };

Ele resolve problemas com o conhecimento de imprimir os dados.

.

Quarto: você pode simplesmente passar na matriz int/longa para sua função, em vez da estrutura. Por exemplo:

void printCommonStatistics( int * data, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << data[i] << endl;
}

Invocado via:

_T10CNT  foo;
printCommonStatistics( foo._cnt, 20 );

Ou:

 int a[10], b[20], c[30];
printCommonStatistics( a, 10 );
printCommonStatistics( b, 20 );
printCommonStatistics( c, 30 );

Isso funciona muito melhor do que ocultar dados em estruturas. À medida que você adiciona membros a uma de suas estruturas, o layout pode mudar entre as suas estruturas e não ser mais consistente. (O que significa que o endereço de _CNT em relação ao início da estrutura pode mudar para _T10CNT e não para _T20CNT. Diversão de depuração tempos lá. Uma única estrutura com uma carga útil de união _CNT evitaria isso.)

Por exemplo:

struct FOO {
  union {
         int     bar  [10];
          long biff [20];
   } u;
}

.

Quinto: se você precisar usar estruturas ... C ++, iostreams e modelos seriam muito mais limpos para implementar.

Por exemplo:

template<class TYPE> void printCommonStatistics( TYPE & mystruct, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << mystruct._cnt[i] << endl;
}      /* Assumes all mystruct's have a "_cnt" member. */

Mas provavelmente não é isso que você está procurando ...

C não é o meu copo O'Java, mas acho que seu problema é que "void *cmncnt" deve ser cmncnt *cmncnt.

Sinta -se à vontade para me corrigir agora, programadores C e me diga é por isso que os programadores Java não podem ter coisas boas.

Essa linha é meio torturada, não acha?

CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];

Que tal algo mais como

CMNCNT *cmncnt = ((CMNCNT *)(cmncntin + (ii * cmncnt_elmsize));

Ou melhor ainda, se cmncnt_elmsize = sizeof (cmncnt)

CMNCNT *cmncnt = ((CMNCNT *)cmncntin) + ii;

Isso também deve se livrar do aviso, pois você não está mais desreferenciando um vazio *.

BTW: Não tenho certeza de por que você está fazendo dessa maneira, mas se o cmncnt_elmsize às vezes não é tamanho de (cmncnt), e de fato pode variar de chamada para ligar, sugiro repensar esse design. Suponho que possa haver uma boa razão para isso, mas parece muito instável para mim. Quase posso garantir que há uma maneira melhor de projetar coisas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top