2009-11-21 14 views
8

Hola, estoy trabajando en una tarea en C donde necesito pasar un tipo desconocido de parámetro a una función.Pasando el parámetro de tipo Void en C

Por ejemplo supongamos que tengo los siguientes:

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

    return 1; 

} 

La razón por elemento variable es nula se debe a que hay 3 tipos de posibilidades. Sin embargo, los 3 tienen una variable miembro llamada "Count".

Cuando intento compilar el código real que escribí en Eclipese, me sale el siguiente error:

error: request for member ‘Count’ in something not a structure or union

que supongo que esto está sucediendo porque el compilador no sabe el tipo de "elemento" antes mano. Sin embargo, no veo por qué esto no está funcionando.

¡Gracias por la ayuda!

+1

¿Por qué no simplemente escribe 'element.Count ++' en lugar de llamar a la función? – LnxPrgr3

+4

No es directamente relevante para la pregunta, pero el comportamiento de 'element.Count = element.Count ++;' no está definido. En aras de la simplicidad, declararé la regla así: no puede modificar un valor más de una vez en la misma expresión. Para detalles completos, vea "6.5 Expressions" en el estándar C. –

Respuesta

12

Debe convertir el puntero a uno de esos 3 tipos y luego usarlo. Algo así como:

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

Sin embargo, usted necesita estar seguro del tipo de objeto que se está lanzando como emitan un objeto al tipo equivocado puede ser peligroso.

+1

O asigne una variable local del tipo apropiado: por ejemplo struct foo * f = element; (no se requiere molde) –

+0

Gracias por su respuesta. Sin embargo, esto no funcionaría si no sé qué es MyType de antemano ¿no? – b1gtuna

+2

Tienes que saber ... No hay salida – Naveen

9

En primer lugar, está pasando un puntero a void, que es un enfoque válido para tipos desconocidos, pero debe pasar punteros a sus diferentes tipos de objetos.

C no es un lenguaje dinámico, por lo que la información de tipo simbólico se elimina antes del tiempo de ejecución, por lo que cuando dice que sus tres tipos tienen un miembro Count, esto no ayuda con el diseño de su función.

La única manera de acceder Count es mediante la emisión de su parámetro void* al tipo de puntero correcta antes de derefencing con -> o (*element).Count (es decir, no sólo .).

A menos que confíe en que sus tipos tengan un diseño compatible (que probablemente dependa de la implementación), también deberá aprobar algo que ayude a su función a determinar la conversión correcta. En este punto, es posible que esté mejor con tres funciones separadas y mejor tipo de seguridad.

+0

su explicación clarificó muchas cosas. ¡¡Gracias!! – b1gtuna

6

Tiene que convertirlo en el tipo real, pero puede obtener resultados inesperados en las estructuras no tienen la propiedad de conteo en el mismo lugar.

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

    return 1; 

} 

Asimismo, recuerda que si se pasa un puntero a una estructura que se parece a la siguiente que se incrementará el campo x no es el recuento.

typedef struct _B 
{ 
    int x;  
    int count; 

}B; 

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

Si está encasillando a la estructura conocida dentro de la función, ¿cuál es el propósito de void *? ¿No podemos poner directamente el elemento A *? Y todavía no entiendo cómo nulo * se usa como parámetro:/ –

+0

si quiere usar una función para manipular muchos tipos diferentes de estructuras. Es un tipo de polimorfismo. – rerun

+0

Pero en el ejemplo anterior estás encasillando a A *, en ese caso ¿por qué usar void *? –

1

use a cast.

ClassName(element).count++ 

también su element.Count = element.Count ++; la línea es redundante, solo necesitas hacer un elemento.cuentan ++ (que incrementa el valor por uno)

+0

Los moldes de estilo de función de C++ no son válidos C y, en general, no es probable que funcionen un tipo de puntero ('void *') a un objeto. –

0

Este es exactamente el problema:

I am guessing this is happening because the compiler doesn't know the type of "element" before hand. However I don't see why this isn't working.

llamar a un método o miembros de datos de nombre no es generalmente una característica de lenguajes de tipo staticly.

Incluso un void * solo se puede convertir de manera confiable en un T *, donde T es un tipo, cuando el puntero es realmente un puntero a ese tipo.

En C++, una posibilidad es que los tres tipos hereden de la misma clase de bajo virtual X que tiene un método Count (es decir, una interfaz ICountable). Luego lanzando a X * y usando p->Count. Esto sería C++ idiomático: todas las clases implementan la misma interfaz y, por lo tanto, todas admiten este método. Este sería un método compatible con el lenguaje similar a confiar en el mismo truco de compensación estructural que se muestra en la respuesta de Tommy McGuire, que hace que las estructuras sean todas similares según la convención. Si tuviera que alterar las estructuras, o si el compilador se apartara del estándar para colocar las estructuras, estaría en agua caliente.

No puedo evitar pensar que esto es más bien un problema de juguete, ya que el método es muy simple, uno normalmente no lo envolvería en una función; uno simplemente lo llamaría en línea: T t; t.Count++;.

+0

Supongo que esto no funcionará: "heredar de la misma clase virtual de bajo X": la pregunta es sobre C, no sobre C++. – viraptor

+0

A menos que las etiquetas sean engañosas, esto es C, no C++. – LnxPrgr3

+0

Editaré para aclarar que la última oración es una expresión de diseño C++. –

3

Si el elemento .Count es del mismo tipo para cada uno de esos 3 tipos, también puede usar una macro. Asumiendo que es int, usted puede hacer esto:

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

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

Esto va a funcionar, porque la dirección a.Count se resolverá cuando se llama a la función, no después (cuando no se conoce el tipo más). Aunque asumo que tiene el tipo correcto cuando llama a la función. Así que Sometype x; changeCount(x); funcionará, pero pasar algo que ya es (void*) no lo hará.

También su expresión original element.Count = element.Count++; es bastante extraña. Si desea incrementar, use element.Count++ o element.Count = element.Count + 1.

+0

+1: & (a) -> El recuento debe ser & (a-> Count) - también, ¿por qué incluso puso el elemento en _changeCount –

+1

@Cade? Dejé el elemento ahí, en caso de que OP quisiera hacer algo más en ese función, pero nos dio una versión simplificada. Además, creo que '& (a) -> Count' es correcto -' -> 'tiene mayor prioridad que' & ', por lo que la resolución de la dirección es correcta, pero tu versión no funcionará correctamente para' changeCount (some_ptr + 4) ' – viraptor

1

Al compilar, C descarta [1] la mayoría de tipos de información, dejando solo las compensaciones. Por lo tanto, su función sería compilar a algo como esto, en pseudocódigo:


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

El stack_ptr es la ubicación del marco de pila que se creó cuando se llama a ChangeCount, la offset_element es la ubicación del argumento elemento, en relación con el stack_ptr, pero ¿qué es offset_Count? Tenga en cuenta que todo lo que sabe el compilador sobre su código es exactamente lo que ha mostrado en su ejemplo; elemento es un puntero genérico, no realmente un puntero a nada. Tendrá que indicar al compilador qué elemento que hace referencia, mediante colada o asignarlo a una variable [2]:


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

Esta función generará esencialmente el mismo código (pseudo) que el anterior, pero el compilador ahora sabe cuál debe ser el desplazamiento del conteo.

Menciona que hay tres posibilidades para el tipo de elemento que apunta. Hay un par de formas de manejar eso: ya sea una unión distinguida o una estructura "heredada". Para una unión distinguida, utilice, por ejemplo, una estructura con un elemento que sea una enumeración que identifique cuál de las tres posibilidades y otro elemento sea una unión de las tres estructuras posibles; esto es más o menos lo que los lenguajes ML (OCaml, Haskell, etc.) llamar a un tipo de datos algebraico o qué unión hay en Pascal. Por "herencia", podría utilizar las definiciones 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; 

En este caso, se puede utilizar la función ChangeCount arriba y pasar un puntero a una counted_int_t, counted_double_t o counted_charptr_t.

Lo que sucede es que el compilador establecerá las tres estructuras con el elemento Count en las estructuras "descendientes" en el mismo lugar , siempre que el elemento counted_t sea el primero. (Al menos, en cada compilador que he usado y en cada bit de código que he visto. Creo que esto lo convirtió en el estándar C en algún momento, pero es una expresión muy normal.)

[1] Excepto por la información de depuración, si le ha dicho al compilador que lo emita. Sin embargo, su programa no tendrá acceso a esa información, por lo que no sería útil en este caso.

[2] La operación x ++ (postincremento) incrementa la variable (bien, lvalue) a la que se aplica; la asignación en el código original es innecesaria.

Cuestiones relacionadas