O padrão C11 diz que as matrizes, tamanho e tamanho variável "devem ter um valor maior que zero". Qual é a justificativa para não permitir um comprimento de 0?
Especialmente para matrizes de comprimento variável, faz todo o sentido ter um tamanho zero de vez em quando. Também é útil para matrizes estáticas quando seu tamanho é de uma opção de configuração de macro ou compilação.
Curiosamente, o GCC (e clang) fornece extensões que permitem matrizes de comprimento zero. Java também permite matrizes de comprimento zero.
struct { int p[1],q[1]; } foo; int *pp = p+1;
,pp
seria um ponteiro legítimo, mas*pp
não teria um endereço exclusivo. Por que a mesma lógica não se aplica a uma matriz de comprimento zero? Digamos que, dadoint q[0];
em uma estrutura ,q
se refira a um endereço cuja validade seria semelhante à dop+1
exemplo acima.Respostas:
O problema que eu apostaria é que as matrizes C são apenas indicadores do início de um pedaço de memória alocado. Ter um tamanho 0 significaria que você tem um ponteiro para ... nada? Você não pode ter nada, então teria que haver alguma coisa arbitrária escolhida. Você não pode usar
null
, porque suas matrizes de comprimento 0 pareceriam ponteiros nulos. E nesse ponto, cada implementação diferente escolherá comportamentos arbitrários diferentes, levando ao caos.fonte
sizeof
0 e como isso causaria problemas. Tudo isso pode ser explicado usando os conceitos e terminologia adequados, sem perda de brevidade ou clareza. Misturar matrizes e ponteiros apenas corre o risco de espalhar o equívoco matrizes = ponteiros (o que é mais importante em outros contextos) sem nenhum benefício.Vejamos como uma matriz é tipicamente organizada na memória:
Observe que não há um objeto separado nomeado
arr
que armazena o endereço do primeiro elemento; quando uma matriz aparece em uma expressão, C calcula o endereço do primeiro elemento, conforme necessário.Então, vamos pensar sobre isso: uma matriz com 0 elementos não teria armazenamento reservado para ela, o que significa que não há nada para calcular o endereço da matriz (em outras palavras , não há mapeamento de objetos para o identificador). É como dizer: "Quero criar uma
int
variável que não ocupa memória". É uma operação sem sentido.Editar
Matrizes Java são animais completamente diferentes das matrizes C e C ++; eles não são um tipo primitivo, mas um tipo de referência derivado
Object
.Editar 2
Um ponto levantado nos comentários abaixo - a restrição "maior que 0" se aplica apenas a matrizes em que o tamanho é especificado por meio de uma expressão constante ;
é permitido que um VLA tenha um comprimento 0Declarar um VLA com uma expressão não constante com valor 0 não é uma violação de restrição, mas invoca um comportamento indefinido.É claro que os VLAs são animais diferentes das matrizes regulares
e sua implementação pode permitir um tamanho 0. Eles não podem ser declaradosstatic
ou no escopo do arquivo, porque o tamanho desses objetos deve ser conhecido antes do início do programa.Também não vale nada que, a partir do C11, as implementações não sejam necessárias para dar suporte aos VLAs.
fonte
T a[0]
comoT *a
, mas por que não usarT *a
?struct
hack. Eu nunca o usei pessoalmente; nunca trabalhou em um problema que precisava de umstruct
tipo de tamanho variável .Você normalmente deseja que sua matriz de tamanho zero (de fato variável) saiba seu tamanho em tempo de execução. Em seguida, coloque-o em um
struct
e use membros de matriz flexíveis , como por exemplo:Obviamente, o membro da matriz flexível deve ser o último
struct
e você precisa ter algo antes. Frequentemente, isso seria algo relacionado ao comprimento real em tempo de execução desse membro flexível da matriz.Claro que você alocaria:
AFAIK, isso já era possível no C99 e é muito útil.
BTW, os membros flexíveis da matriz não existem em C ++ (porque seria difícil definir quando e como eles devem ser construídos e destruídos). No entanto, veja o futuro std :: dynarray
fonte
Se a expressão
type name[count]
estiver escrita em alguma função, você solicita que o compilador C aloque nossizeof(type)*count
bytes do quadro da pilha e calcule o endereço do primeiro elemento na matriz.Se a expressão
type name[count]
for gravada fora de todas as funções e definições de estruturas, você instrui o compilador C a alocar nossizeof(type)*count
bytes do segmento de dados e computa o endereço do primeiro elemento na matriz.name
na verdade, é um objeto constante que armazena o endereço do primeiro elemento na matriz e todo objeto que armazena um endereço de alguma memória é chamado ponteiro; portanto, esse é o motivo pelo qual você trataname
como ponteiro, e não como matriz. Observe que as matrizes em C podem ser acessadas apenas através de ponteiros.Se
count
é uma expressão constante avaliada como zero, você diz ao compilador C para alocar zero bytes no quadro da pilha ou no segmento de dados e retorna o endereço do primeiro elemento na matriz, mas o problema é que o primeiro elemento da matriz de comprimento zero não existe e você não pode calcular o endereço de algo que não existe.É racional esse elemento não.
count+1
não existe nacount
matriz -length, portanto, esse é o motivo pelo qual o compilador C proíbe definir a matriz de comprimento zero como variável dentro e fora de uma função, porque qual é o conteúdoname
dela? Que endereçoname
armazena exatamente?Se
p
é um ponteiro, a expressãop[n]
é equivalente a*(p + n)
Onde o asterisco * na expressão correta é uma operação de desreferência do ponteiro, o que significa acessar a memória apontada
p + n
ou acessar a memória cujo endereço está armazenadop + n
, ondep + n
está a expressão do ponteiro, ele pega o endereço dep
e adiciona a esse endereço o númeron
multiplique o tamanho do tipo do ponteirop
.É possível adicionar um endereço e um número?
Sim, é possível, porque o endereço é um número inteiro sem sinal geralmente representado na notação hexadecimal.
fonte
N
temN+1
endereços associados, o primeiroN
dos quais identifica bytes únicos e o últimoN
dos quais cada ponto após um desses bytes. Definição tal iria funcionar muito bem mesmo no caso degenerado, ondeN
é 0.Se você deseja um ponteiro para um endereço de memória, declare um. Uma matriz, na verdade, aponta para um pedaço de memória que você reservou. As matrizes se deterioram para os ponteiros quando passadas para as funções, mas se a memória apontada estiver na pilha, não há problema. Não há motivo para declarar uma matriz de tamanho zero.
fonte
Desde os dias do C89 original, quando um Padrão C especificava que algo tinha Comportamento Indefinido, o que isso significava era "Fazer o que tornaria uma implementação em uma plataforma de destino específica mais adequada para a finalidade pretendida". Os autores da Norma não quiseram adivinhar quais comportamentos poderiam ser mais adequados para qualquer finalidade específica. As implementações C89 existentes com extensões VLA podem ter comportamentos diferentes, mas lógicos, quando recebem tamanho zero (por exemplo, alguns podem ter tratado a matriz como uma expressão de endereço que produz NULL, enquanto outros a tratam como uma expressão de endereço que pode ser igual ao endereço de outra variável arbitrária, mas poderia com segurança adicionar zero a ela sem capturar). Se algum código pudesse ter se baseado em comportamentos tão diferentes, os autores da Norma não
Em vez de tentar adivinhar o que as implementações podem fazer, ou sugerir que qualquer comportamento deve ser considerado superior a qualquer outro, os autores da Norma simplesmente permitiram que os implementadores usassem julgamento ao lidar com esse caso da melhor maneira que quisessem. Implementações que usam malloc () nos bastidores podem tratar o endereço da matriz como NULL (se malloc de tamanho zero gerar nulo), aquelas que usam cálculos de endereço de pilha podem gerar um ponteiro que corresponde ao endereço de alguma outra variável, e algumas outras implementações podem fazer outras coisas. Eu não acho que eles esperavam que os escritores de compiladores se esforçassem para fazer com que a caixa de canto de tamanho zero se comporte de maneira deliberadamente inútil.
fonte