Pergunta

Olá, estou trabalhando em uma tarefa em C, onde preciso passar em um tipo de parâmetro desconhecido em uma função.

Por exemplo, suponha que eu tenha o seguinte:

int changeCount(void* element)
{
    element.Count = element.Count++;

    return 1;

}

A razão pela qual o elemento variável é nulo é porque existem três tipos de possibilidades. Todos os três, no entanto, têm uma variável de membro chamada "Count".

Quando tento compilar o código real que escrevi em eclipese, recebo o seguinte erro:

Erro: solicitação de membro 'contagem' em algo não uma estrutura ou união

Acho que isso está acontecendo porque o compilador não sabe o tipo de "elemento" antes da mão. No entanto, não vejo por que isso não está funcionando.

Obrigado pela ajuda!

Foi útil?

Solução

Você precisa lançar o ponteiro para um desses 3 tipos e depois usá -lo. Algo como:

MyType* p = (MyType*)element;
p->count++;

No entanto, você precisa ter certeza do tipo de objeto que está lançando como lançar um objeto para o tipo errado, pode ser perigoso.

Outras dicas

Primeiro, você está passando em um ponteiro para o Void, que é uma abordagem válida para tipos desconhecidos, mas precisa passar os ponteiros para seus diferentes tipos de objetos.

C não é uma linguagem dinâmica, portanto, as informações do tipo simbólico são excluídas amplamente antes do tempo de execução, então, quando você diz que todos os seus três tipos têm um membro Count Isso não ajuda no design da sua função.

A única maneira de acessar Count é lançando seu void* parâmetro para o tipo de ponteiro correto antes de desrefinar com -> ou (*element).Count (ou seja, não apenas .).

A menos que você confie em seus tipos com um layout compatível (que provavelmente depende da implementação), você também precisará passar algo que ajude sua função a determinar o elenco correto para executar. Neste ponto, você pode estar melhor com três funções separadas e melhor segurança do tipo.

Você precisa lançá -lo para o tipo real, mas pode obter resultados inesperados nas estruturas não têm a propriedade Count no mesmo local.

typedef struct _A 
{
    int count;
    int x;
}A;
int changeCount(void* element)
{
    ((A*)element)->Count++;

    return 1;

}

Lembre -se também de que, se você passar um ponteiro para uma estrutura que se parece com o seguinte, incrementará o campo X e não a contagem.

typedef struct _B 
{
    int x;      
    int count;

}B;

    B st;
    B* pst = &st;
    changeCount(pst);

Se o .Count O elemento é do mesmo tipo para cada um desses 3 tipos, você também pode usar uma macro. Supondo que seja int, você pode fazer isso:

#define changeCount(a) _changeCount((a), &(a)->Count)

int _changeCount(void* element, int *count)
{
  (*count)++;
  return 1;
}

Isso vai funcionar, porque o a.Count O endereço será resolvido quando você ligar para a função, não depois (quando você não conhece mais o tipo). Presumo que você tenha o tipo adequado quando chama a função. Então Sometype x; changeCount(x); vai funcionar, mas passar em algo que já é um (void*) não vai.

Também sua expressão original element.Count = element.Count++; é bastante bizarro. Se você deseja incrementar, use element.Count++, ou element.Count = element.Count + 1.

use um elenco.

ClassName(element).count++

também seu element.count = element.count ++; A linha é redundante, você só precisa fazer elemento.count ++ (que incrementa o valor por um)

Ao compilar, C de descarte [1] a maioria das informações do tipo, deixando apenas compensações. Portanto, sua função compilaria com algo assim, em pseudocódigo:


changeCount:
  assign *(*(stack_ptr + offset_element) + offset_Count) + 1
    to *(stack_ptr + offset_element) + offset_Count;
  assign 1 to return_value;
  pop

O Stack_Ptr é a localização do quadro da pilha que foi criado quando você chama ChangeCount, o offset_element é a localização do argumento do elemento, em relação ao Stack_PTR, mas o que está offset_count? Lembre -se de que todo o compilador sabe sobre seu código é exatamente o que você mostrou no seu exemplo; O elemento é um ponteiro genérico, não um ponteiro para qualquer coisa. Você terá que dizer ao compilador a que elemento está apontando, lançando ou atribuindo -o a uma variável [2]:


typedef struct { int Count; } counted_t;
int changeCount(void* element)
{
    counted_t* counted = element;
    counted.Count++;
    return 1;
}

Essa função gerará essencialmente o mesmo código (pseudo) acima, mas o compilador agora sabe qual deve ser o deslocamento da contagem.

Você menciona que existem três possibilidades para o tipo do elemento que está apontando. Existem algumas maneiras de lidar com isso: uma união distinta ou uma estrutura "herdada". Para um distinto uso da união, digamos, uma estrutura com um elemento sendo uma enumeração identificando quais das três possibilidades e outro elemento é uma união das três estruturas possíveis; Isso é aproximadamente o que os idiomas ML (OCAML, Haskell, etc.) chamam um tipo de dados algébricos ou o que é um sindicato em Pascal. Para "herança", você pode usar as definições de tipo:


typedef struct { counted_t counted; int i; } counted_int_t;
typedef struct { counted_t counted; double d; } counted_double_t;
typedef struct { counted_t counted; char* s; } counted_charptr_t;

Nesse caso, você pode usar a função ChanGecount acima e passar em um ponteiro para um counted_int_t, counted_double_t ou counted_charptr_t.

O que acontece é que o compilador apresentará as três estruturas com o elemento de contagem nas estruturas "descendente" no mesmo lugar Enquanto o elemento counted_t for o primeiro. (Pelo menos, em todos os compiladores que já usei e em todo o código que já vi. Acho que isso chegou ao padrão C em algum momento, mas é um idioma muito normal.)

1] Exceto para depurar informações, se você disse ao compilador para emiti -las. Seu programa não terá acesso a essas informações, por isso não ajudaria neste caso.

2] A operação X ++ (pós -concremento) incrementa a variável (bem, lvalue) a que é aplicada; A atribuição no código original é desnecessária.

Este é exatamente o problema:

Acho que isso está acontecendo porque o compilador não sabe o tipo de "elemento" antes da mão. No entanto, não vejo por que isso não está funcionando.

Chamar um método ou dados de membro pelo nome geralmente não é um recurso de idiomas estáticamente tipados.

Até a void * só pode ser lançado de forma confiável para um T *, onde T é um tipo, quando o ponteiro é realmente um ponteiro para esse tipo.

No C ++, uma possibilidade é ter todos os três tipos herdados da mesma classe de graves virtuais que possui uma contagem de métodos (ou seja, uma interface ICountable). Então lançando para X * e usando p->Count. Isso seria C ++ idiomático - todas as classes implementam a mesma interface e, portanto, todos suportam esse método. Este seria um método apoiado pela linguagem, tipo análogo de confiar no mesmo truque de compensações de estrutura mostrado na resposta de Tommy McGuire, o que torna as estruturas todas semelhantes por convenção. Se você alterasse as estruturas, ou o compilador partisse do padrão para colocar estruturas, estaria em água quente.

Não posso deixar de pensar que isso é um problema de brinquedo, já que o método é tão simples que normalmente não o envolveria em uma função - simplesmente o chamaria de inline: T t; t.Count++;.

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