2009-03-19 20 views
5

Comenzaré a usar C para un curso de Sistemas Operativos pronto y estoy leyendo sobre las mejores prácticas sobre el uso de C para que los dolores de cabeza se reduzcan más adelante.¿Es una mejor práctica para envolver matrices y su longitud variable en una estructura en C?

Esta siempre ha sido una de mis primeras preguntas con respecto a las matrices, ya que son fáciles de arruinar.

¿Es una práctica común para agrupar una matriz y su variable asociada que contiene su longitud en una estructura?

Nunca lo he visto en libros y generalmente siempre mantienen los dos separados o usan algo como sizeof (array []/array [1]) tipo de trato.

Pero al unir las dos en una estructura, podría pasar la estructura tanto por valor como por referencia, lo que realmente no se puede hacer con matrices a menos que se utilicen punteros, en cuyo caso se debe volver a realizar un seguimiento de la longitud de la matriz.

Estoy empezando a usar C, por lo que lo anterior podría ser terriblemente incorrecto, todavía soy un estudiante.

Cheers, Kai.

+0

Gracias por las respuestas a todos! Nota: Mi tamaño de uso fue incorrecto, gracias al desenrollado para señalarlo y también para el ejemplo del mundo real de GArray en glib. – Kai

Respuesta

3

Claro, puedes hacer eso. No estoy seguro de si lo llamaría una buena práctica, pero ciertamente es una buena idea hacer que los arreglos más bien rudimentarios de C sean un poco más manejables. Si necesita arreglos dinámicos, es casi un requisito agrupar los diversos campos necesarios para llevar a cabo la contabilidad juntos.

A veces tiene dos tamaños en ese caso: uno actual y otro asignado. Esta es una compensación en la que intercambias menos asignaciones a cierta velocidad, pagando con un poco de sobrecarga de memoria.

Muchas veces las matrices solo se usan localmente, y son de tamaño estático, por lo que el operador sizeof es muy útil para determinar el número de elementos. Su sintaxis es ligeramente fuera con eso, por cierto, así es como se ve por lo general:

int array[4711]; 
int i; 

for(i = 0; i < sizeof array/sizeof *array; i++) 
{ 
    /* Do stuff with each element. */ 
} 

Recuerde que sizeof no es una función, no siempre se necesita el paréntesis.

EDITAR: Un ejemplo del mundo real de una envoltura exactamente como el que usted describe es el tipo GArray proporcionada por glib. La parte visible para el usuario de la declaración es exactamente lo que usted describe:

typedef struct { 
    gchar *data; 
    guint len; 
} GArray; 

programas se espera que utilice la API proporcionada a acceder a la matriz siempre que sea posible, no asoman estos campos directamente.

1

No veo nada de malo en hacer eso, pero creo que la razón por la que normalmente no se hace es debido a los gastos incurridos por dicha estructura. La mayoría de C es un código bare-metal por razones de rendimiento y, como tal, a menudo se evitan las abstracciones.

+0

Hmmm ... ¿no es una generalización? –

5

Sí, esta es una gran práctica para tener en C. Es completamente lógico para ajustar los valores relacionados en una estructura contenedora.

Iría más lejos. También serviría su propósito de nunca modificar estos valores directamente. En su lugar, escriba funciones que actúen sobre estos valores como un par dentro de su estructura para cambiar la longitud y alterar los datos. De esta forma, puede agregar comprobaciones invariables y también hacer que sea muy fácil de probar.

0

Nunca lo he visto hecho de esa manera, pero no he trabajado en el nivel del sistema operativo en más de una década ... :-) Parece un enfoque razonable a primera vista. La única preocupación sería asegurarse de que el tamaño de alguna manera se mantenga exacto ... Cálculo según sea necesario no tiene esa preocupación.

1

No lo he visto en los libros tampoco, pero he estado haciendo lo mismo desde hace un tiempo. Simplemente parece tener sentido "empaquetar" esas cosas juntas. Encuentro especialmente útil si necesita devolver una matriz asignada de un método, por ejemplo.

0

teniendo en cuenta que puede calcular la longitud de la matriz (en bytes, es decir, no # de elementos) y el compilador reemplazará las llamadas sizeof() por el valor real (no es una función, las llamadas se reemplazan por el compilador con el valor que 'devuelve'), entonces la única razón por la que querrá envolverlo en una estructura es para la legibilidad.

No es una práctica común, si lo hiciera, alguien que mirara su código supondría que el campo de longitud tenía algún valor especial, y no solo el tamaño de la matriz. Ese es un peligro que acecha en su propuesta, por lo que debe tener cuidado de comentarlo correctamente.

+0

El problema con depender de sizeof es que no ayudará con las matrices asignadas dinámicamente. –

+0

... o cuando pasa la matriz a una función con un puntero. –

3

Hay tres formas.

  1. Para matriz estática (no asignada dinámicamente y no pasa como puntero) tamaño es conoce en tiempo de compilación para que pueda utilizado sizeof operador, así: sizeof(array)/sizeof(array[0])
  2. Uso terminador (valor especial para el último elemento de la matriz que no puede se utilizará como valor de matriz regular), como cadenas terminadas en nulo
  3. Utilice un valor por separado, ya sea como miembro de estructura o como variable independiente. Realmente no importa porque todas las funciones estándar que funcionan con matrices toman una variable de tamaño separada, sin embargo, unir el puntero y el tamaño de la matriz a una estructura aumentará la legibilidad del código. Sugiero usar para tener una interfaz más limpia para sus propias funciones. Tenga en cuenta que si pasa su estructura por valor, la función llamada podrá cambiar el conjunto, pero no la variable de tamaño, por lo que pasar el puntero struct sería una mejor opción.
+0

en el último punto acerca de cambiar la matriz si pasa por valor. ¿Quiere decir que puede cambiar la matriz en sí o los elementos señalados? (Si se trata de una matriz de puntero). De lo contrario, no veo cómo puede cambiar uno, pero no el otro. – mmccoo

+0

Id struct contiene puntero a matriz y tamaño de matriz (como he mencionado), cambiar la matriz no será un problema, ya que el puntero de matriz se pasará por valor, no la matriz en sí – qrdl

1

Si utiliza matrices estáticas, tiene acceso al tamaño de la matriz mediante el operador sizeof. Si lo va a poner en struct, puede pasarlo a la función por valor, referencia y puntero. Pasar el argumento por referencia y por puntero es el mismo en el nivel de ensamblaje (estoy casi seguro).

Pero si utiliza matrices dinámicas, no conoce el tamaño de la matriz en tiempo de compilación. Para que pueda almacenar este valor en la estructura, pero también puede almacenar solamente un puntero a la matriz de la estructura:

struct Foo { 
    int *myarray; 
    int size; 
}; 

para que pueda pasar esta estructura por valor, pero lo que realmente hace es pasar el puntero a int (puntero a matriz) e int (tamaño de matriz).

En mi opinión, no te ayudará mucho. Lo único que está en positivo es que almacena el tamaño y la matriz en un solo lugar y es fácil obtener el tamaño de la matriz. Si va a utilizar una gran cantidad de matrices dinámicas, puede hacerlo de esta manera. Pero si usa pocas matrices, será más fácil no usar estructuras.

+0

ANSI C no admite pasar variables por referencia . Quizás estás pensando en C++? –

+0

Estaba pensando en C, pero fue hace mucho tiempo cuando escribía en C, así que es un error mío. – klew

+0

+1 para "Lo único que está en positivo es que almacena el tamaño y la matriz en un solo lugar ...". Le ahorrará un nombre de variable y un parámetro de función, pero no memoria ni velocidad. – potrzebie

2

Para API pública Me gustaría ir con la matriz y el valor de tamaño separados. Así es como se maneja en la mayoría de la biblioteca (si no en todas) c que conozco. Cómo lo manejas internamente, depende completamente de ti. Entonces, es una buena idea usar una estructura más algunas funciones auxiliares/macros que hacen las partes difíciles para ti.Siempre me duele la cabeza volver a pensar cómo insertar un elemento o eliminar uno, por lo que es una buena fuente de problemas. Resolver eso una vez y genérico, le ayuda a obtener errores desde el principio bajo. Una buena implementación para matrices dinámicas y genéricas es kvec.

+0

No usaría 'struct {void * ptr; size_t len;}; 'como un parámetro para la API * any * functions, porque usted pierde la capacidad de, p. ordene solo parte de una lista pasando una dirección diferente a la del primer elemento y/o pasando una longitud diferente a la longitud total de la matriz. – potrzebie

+0

... así que si quieres la abstracción pero aún necesitas acceder a partes de matrices, una estructura arraySlice probablemente sea mejor. Puede tener una variable arraySlice que apunte y abarque toda la matriz y otras variables/valores de arraySlice que señalen y expandan las partes en las que está trabajando. – potrzebie

2

Yo diría que es una buena práctica. De hecho, es suficientemente bueno que en C++ lo hayan incluido en la biblioteca estándar y lo hayan llamado vector. Siempre que hable de matrices en un foro de C++, se verá inundado con respuestas que dicen que debe usar vector.

0

creo que esta parte de su pregunta es al revés:

"Pero con envolver a los dos en una estructura, que sería capaz de pasar de la estructura, tanto por su valor y por referencia, que realmente no se puede hacer con matrices, a menos que se utilicen punteros, en cuyo caso hay que volver a hacer un seguimiento de la longitud de la matriz ".

El uso de punteros al pasar matrices es el comportamiento predeterminado; eso no te compra nada en términos de pasar toda la matriz por valor. Si desea copiar la matriz completa en lugar de tener que decaer a un puntero NECESITA envolver la matriz en una estructura. Ver esta respuesta para más información:

Pass array by value to recursive function possible?

Y aquí es más en el comportamiento especial de matrices:

http://denniskubes.com/2012/08/20/is-c-pass-by-value-or-reference/

Cuestiones relacionadas