Em um projeto, alguém empurrou esta linha:
double (*e)[n+1] = malloc((n+1) * sizeof(*e));
O que supostamente cria uma matriz bidimensional de (n + 1) * (n + 1) duplas.
Supostamente , digo, porque até agora ninguém a quem eu perguntei poderia me dizer o que isso faz, exatamente, nem de onde se originou ou por que deveria funcionar (que supostamente funciona, mas ainda não estou comprando).
Talvez esteja faltando algo óbvio, mas agradeceria se alguém pudesse explicar acima da linha para mim. Porque pessoalmente, eu me sentiria muito melhor se usássemos algo que realmente entendêssemos.
c
arrays
multidimensional-array
malloc
allocation
User1291
fonte
fonte
n+1
mas em vez dissodouble (*e)[rows] = malloc(columns * sizeof *e);
Respostas:
A variável
e
é um ponteiro para uma matriz den + 1
elementos do tipodouble
.Usar o operador dereference em
e
fornece o tipo basee
que é "matriz den + 1
elementos do tipodouble
".A
malloc
chamada simplesmente pega o tipo base dee
(explicado acima) e obtém seu tamanho, multiplica-o porn + 1
e passa esse tamanho para amalloc
função. Essencialmente, alocando um array den + 1
arrays den + 1
elementos dedouble
.fonte
sizeof(*e)
é equivalente asizeof(double [n + 1])
. Multiplique isso porn + 1
e você terá o suficiente.n+1
para ambas as dimensões, o resultado será quadrado. Se você fizer issodouble (*e)[cols] = malloc(rows * sizeof(*e));
, o resultado terá qualquer número de linhas e colunas que você especificou.int rows = n+1
eint cols = n+1
. Deus nos salve do código inteligente.Essa é a maneira típica de alocar arrays 2D dinamicamente.
e
é um ponteiro de array para um array do tipodouble [n+1]
.sizeof(*e)
portanto, fornece o tipo do tipo apontado, que é o tamanho de umdouble [n+1]
array.n+1
essas matrizes.e
para apontar para o primeiro array neste array de arrays.e
comoe[i][j]
para acessar itens individuais na matriz 2D.Pessoalmente, acho que esse estilo é muito mais fácil de ler:
fonte
ptr = malloc(sizeof *ptr * count)
estilo.malloc(row*col*sizeof(double))
ocorre quandorow*col*sizeof()
estoura, massizeof()*row*col
isso não acontece. (ex .: linha, colunaint
)sizeof *e * (n+1)
é mais fácil de manter; se você decidir alterar o tipo de base (dedouble
paralong double
, por exemplo), então você só precisa alterar a declaração dee
; você não precisa modificar asizeof
expressão namalloc
chamada (o que economiza tempo e protege você de alterá-la em um lugar, mas não no outro).sizeof *e
sempre lhe dará o tamanho certo.Esse idioma sai naturalmente da alocação de array 1D. Vamos começar alocando uma matriz 1D de algum tipo arbitrário
T
:Simples, certo? A expressão
*p
tem tipoT
, entãosizeof *p
dá o mesmo resultado quesizeof (T)
, então estamos alocando espaço suficiente para umN
array -element deT
. Isso é verdade para qualquer tipoT
.Agora, vamos substituir
T
por um tipo de array comoR [10]
. Então nossa alocação se tornaA semântica aqui é exatamente a mesma do método de alocação 1D; tudo o que mudou foi o tipo de
p
. Em vez deT *
, é agoraR (*)[10]
. A expressão*p
tem tipoT
que é tipoR [10]
, entãosizeof *p
é equivalente asizeof (T)
que é equivalente asizeof (R [10])
. Portanto, estamos alocando espaço suficiente para um arrayN
por10
elemento deR
.Podemos levar isso ainda mais longe, se quisermos; suponha que
R
seja um tipo de arrayint [5]
. Substitua issoR
e teremosMesma coisa -
sizeof *p
é o mesmo quesizeof (int [10][5])
, e nós acabam alocando um pedaço contíguo de memória grande o suficiente para segurar umaN
por10
pelo5
conjunto deint
.Então esse é o lado da alocação; e o lado do acesso?
Lembre-se de que a
[]
operação subscrito é definida em termos de aritmética de ponteiro:a[i]
é definido como*(a + i)
1 . Assim, o operador subscrito desreferencia[]
implicitamente um ponteiro. Sep
for um ponteiro paraT
, você pode acessar o valor apontado desreferenciando explicitamente com o*
operador unário :ou usando o
[]
operador subscrito:Assim, se
p
apontar para o primeiro elemento de uma matriz , você pode acessar qualquer elemento dessa matriz usando um subscrito no ponteirop
:Agora, vamos fazer nossa operação de substituição novamente e substituir
T
pelo tipo de arrayR [10]
:Uma diferença imediatamente aparente; estamos desreferenciando explicitamente
p
antes de aplicar o operador subscrito. Não queremos subscrever emp
, queremos subscrever em quep
aponta (neste caso, o arrayarr[0]
). Desde unário*
tem precedência menor do que o índice[]
operador, temos que usar parênteses para explicitamente grupop
com*
. Mas lembre-se de cima que*p
é o mesmo quep[0]
, então podemos substituir isso porou apenas
Assim, se
p
apontar para uma matriz 2D, podemos indexar nessa matriz da seguintep
forma:Levando isso à mesma conclusão acima e substituindo
R
porint [5]
:Isso funciona da mesma forma se
p
apontar para um array regular ou se apontar para a memória alocadamalloc
.Esse idioma tem os seguintes benefícios:
free
. Novamente, isso não é verdade com o método de alocação fragmentada, onde você precisa desalocar cadaarr[i]
antes de poder desalocararr
.Às vezes, o método de alocação fragmentada é preferível, como quando seu heap está muito fragmentado e você não pode alocar sua memória como um bloco contíguo ou deseja alocar uma matriz "denteada" em que cada linha pode ter um comprimento diferente. Mas, em geral, esse é o melhor caminho a seguir.
1. Lembre-se de que os arrays não são ponteiros - em vez disso, as expressões de vetor são convertidas em expressões de ponteiro, conforme necessário.
fonte