El estándar C11 dice que las matrices, tanto de tamaño variable como de longitud variable "tendrán un valor mayor que cero". ¿Cuál es la justificación para no permitir una longitud de 0?
Especialmente para matrices de longitud variable, tiene mucho sentido tener un tamaño de cero de vez en cuando. También es útil para matrices estáticas cuando su tamaño es de una macro o una opción de configuración de compilación.
Curiosamente, GCC (y clang) proporcionan extensiones que permiten matrices de longitud cero. Java también permite matrices de longitud cero.
struct { int p[1],q[1]; } foo; int *pp = p+1;
,pp
sería un puntero legítimo, pero*pp
no tendría una dirección única. ¿Por qué no podría sostenerse la misma lógica con una matriz de longitud cero? Digamos que dadoint q[0];
dentro de una estructura ,q
se referiría a una dirección cuya validez sería como la delp+1
ejemplo anterior.Respuestas:
El problema que apostaría es que las matrices en C son solo punteros al comienzo de un fragmento de memoria asignado. Tener un tamaño 0 significaría que tienes un puntero para ... ¿nada? No se puede tener nada, por lo que habría tenido que haber algo arbitrario elegido. No puede usar
null
, porque entonces sus matrices de longitud 0 se verían como punteros nulos. Y en ese punto, cada implementación diferente va a elegir diferentes comportamientos arbitrarios, lo que conducirá al caos.fuente
sizeof
0, y cómo eso causaría problemas. Todo eso puede explicarse utilizando los conceptos y la terminología adecuados sin pérdida de brevedad o claridad. Mezclar matrices y punteros solo corre el riesgo de difundir la idea errónea de matrices = punteros (que es más importante en otros contextos) sin ningún beneficio.Veamos cómo se despliega una matriz en la memoria:
Tenga en cuenta que no hay un objeto separado llamado
arr
que almacene la dirección del primer elemento; cuando aparece una matriz en una expresión, C calcula la dirección del primer elemento según sea necesario.Entonces, pensemos en esto: una matriz de 0 elementos no tendría almacenamiento aparte, lo que significa que no hay nada para calcular la dirección de la matriz (dicho de otra manera, no hay mapeo de objetos para el identificador). Es como decir: "Quiero crear una
int
variable que no ocupe memoria". Es una operación sin sentido.Editar
Las matrices Java son animales completamente diferentes de las matrices C y C ++; no son un tipo primitivo, sino un tipo de referencia derivado de
Object
.Editar 2
Un punto que aparece en los comentarios a continuación: la restricción "mayor que 0" solo se aplica a las matrices donde el tamaño se especifica a través de una expresión constante ;
Se permite que un VLA tenga una longitud 0 Ladeclaración de un VLA con una expresión no constante con valor 0 no es una violación de restricción, pero invoca un comportamiento indefinido.Está claro que los VLA son animales diferentes de las matrices regulares
, y su implementación puede permitir un tamaño 0. No se pueden declararstatic
ni al alcance del archivo, porque el tamaño de dichos objetos debe conocerse antes de que se inicie el programa.Tampoco vale nada que a partir de C11, las implementaciones no sean necesarias para admitir VLA.
fuente
T a[0]
comoT *a
, pero ¿por qué no simplemente usarT *a
?struct
hack. Nunca lo he usado personalmente; nunca trabajó en un problema que necesitaba unstruct
tipo de tamaño variable .Por lo general, querrá que su matriz de tamaño cero (de hecho variable) sepa su tamaño en tiempo de ejecución. Luego empaquete eso en
struct
y use miembros de matriz flexibles , como por ejemplo:Obviamente, el miembro de la matriz flexible tiene que ser el último
struct
y debe tener algo antes. A menudo eso sería algo relacionado con la longitud real ocupada en tiempo de ejecución de ese miembro de matriz flexible.Por supuesto que asignarías:
AFAIK, esto ya era posible en C99, y es muy útil.
Por cierto, los miembros de matriz flexible no existen en C ++ (porque sería difícil definir cuándo y cómo deberían construirse y destruirse). Ver sin embargo el futuro std :: dynarray
fuente
Si la expresión
type name[count]
está escrita en alguna función, entonces le indica al compilador de C que asigne en lossizeof(type)*count
bytes del marco de la pila y calcule la dirección del primer elemento de la matriz.Si la expresión
type name[count]
se escribe fuera de todas las definiciones de funciones y estructuras, entonces le indica al compilador de C que asigne en lossizeof(type)*count
bytes del segmento de datos y calcule la dirección del primer elemento en la matriz.name
en realidad es un objeto constante que almacena la dirección del primer elemento en la matriz y cada objeto que almacena una dirección de alguna memoria se llama puntero, por lo que esta es la razón por la que se trataname
como un puntero en lugar de una matriz. Tenga en cuenta que solo se puede acceder a las matrices en C a través de punteros.Si
count
es una expresión constante que se evalúa a cero, entonces le dice al compilador de C que asigne cero bytes en el marco de la pila o en el segmento de datos y devuelva la dirección del primer elemento de la matriz, pero el problema al hacerlo es que el primer elemento de la matriz de longitud cero no existe y no puede calcular la dirección de algo que no existe.Esto es racional ese elemento no.
count+1
no existe en lacount
matriz de longitud, por lo que esta es la razón por la que el compilador de C prohíbe definir la matriz de longitud cero como variable dentro y fuera de una función, porque ¿cuál es el contenido dename
entonces? Que direccionname
almacena exactamente?Si
p
es un puntero, entonces la expresiónp[n]
es equivalente a*(p + n)
Donde el asterisco * en la expresión derecha es la operación de desreferencia del puntero, lo que significa acceder a la memoria apuntada
p + n
o acceder a la memoria cuya dirección está almacenadap + n
, dondep + n
es la expresión del puntero, toma la direcciónp
y agrega a esta dirección el númeron
multiplica el tamaño del tipo de punterop
.¿Es posible agregar una dirección y un número?
Sí, es posible, porque la dirección es un entero sin signo comúnmente representado en notación hexadecimal.
fuente
N
tieneN+1
direcciones asociadas, la primeraN
de las cuales identifica bytes únicos y la últimaN
de las cuales cada punto acaba de pasar uno de esos bytes. Tal definición funcionaría bien incluso en el caso degenerado dondeN
es 0.Si desea un puntero a una dirección de memoria, declare uno. Una matriz en realidad apunta a un trozo de memoria que ha reservado. Las matrices decaen a punteros cuando se pasan a funciones, pero si la memoria a la que apuntan está en el montón, no hay problema. No hay razón para declarar una matriz de tamaño cero.
fuente
Desde los días del C89 original, cuando un Estándar C especificaba que algo tenía un Comportamiento Indefinido, lo que eso significaba era "Hacer lo que sea que haga que una implementación en una plataforma objetivo particular sea la más adecuada para su propósito". Los autores de la Norma no querían tratar de adivinar qué comportamientos podrían ser más adecuados para un propósito particular. Las implementaciones existentes de C89 con extensiones VLA podrían haber tenido comportamientos diferentes, pero lógicos, cuando se les dio un tamaño de cero (por ejemplo, algunos podrían haber tratado la matriz como una expresión de dirección que genera NULL, mientras que otros la trataron como una expresión de dirección que podría ser igual a la dirección de otra variable arbitraria, pero con seguridad podría agregar cero sin atrapar). Si algún código pudiera haberse basado en un comportamiento tan diferente, los autores de la Norma no
En lugar de tratar de adivinar lo que podrían hacer las implementaciones, o sugerir que cualquier comportamiento debería considerarse superior a cualquier otro, los autores de la Norma simplemente permitieron que los implementadores usaran el juicio al manejar ese caso lo mejor que creían conveniente. Las implementaciones que usan malloc () detrás de escena pueden tratar la dirección de la matriz como NULL (si malloc de tamaño cero produce un valor nulo), aquellas que usan cálculos de direcciones de pila podrían generar un puntero que coincida con la dirección de alguna otra variable, y algunas otras implementaciones podrían funcionar otras cosas. No creo que esperaran que los escritores de compiladores hicieran todo lo posible para que el caso de la esquina de tamaño cero se comportara de manera deliberadamente inútil.
fuente