Arremates … ou melhor: Arraymates!

Elaborei esta prova de conceito para criar um modo padronizado de lidar com arrays.

Tive dificuldade com isto, ontem, ao criar um programa de sudoku, pois quase não utilizo arrays em meus projetos.

O conceito aqui testado é explicado em

Kernighan, Brian W., and Dennis M. Ritchie.The C Programming Language.
Prentice Hall, Englewood Cliffs, NJ. Second Ed. 1988.

Nas seções A 7.3.1 e em A.8.6.2

Basicamente um array é declarado na forma T D.

Onde D tem a forma:

D1 [ constante ], sendo a constante, opcional.

E T é o modificador de tipo do array.

e `[]’ são os operadores de subscritos.

Note que, se a constante estiver presente – pois sendo opcional, poderia não estar – ela necessariamente deve ser do tipo íntegro: um valor inteiro maior ou igual a zero.

Caso a constante não seja especificada, o array terá um tipo incompleto, ou seja, não possuindo uma área reservada ou alocada para ele que, de modo geral, podem causar efeitos indesejáveis durante a execução (runtime).

Os arrays podem ser construídos utilizando-se outros tipos ( int, float, etc.), ponteiros, estruturas e uniões ou … outros arrays ! Neste último caso temos os arrays multidimensionais.

Quaisquer que sejam os tipos usados na construção, eles devem ser necessáriamente completos, o que quer dizer que não poderão existir arrays criados de estruturas de tipos incompletos.

Isto implica que para um array, ** somente a primeira dimensão **, poderá estar indefinida.

Portanto não são válidas declarações do tipo:

int a[3][];

Ao tentar compilar isto o “berro” do GCC é estridente

make -k arr
cc -Werror -Wall -Wextra -Wformat-security -Wformat=2 -Wno-format-extra-args    arr.c   -o arr
arr.c:49:5: error: array type has incomplete element type
make: ** [arr] Erro 1

Compilation exited abnormally with code 2 at Fri Mar 23 11:44:58

No caso de várias dimensões, o que importa é entender o modo como o array é visto pelo “C”.

int a[3][14][16];

Declara um array de três dimensões com 3 x 14 x 16 * ( 4 bytes ), o que dá um total de 2688 bytes “alocados”.

Uma vez declarado o array, podemos usar suas referências parciais como:

a, a[2], a[3][4] ou a[1][0][7] 

entretanto somente o último caso – a referência completa – retorna um tipo inteiro, sendo que os demais retornam arrays. Na realidade o GCC, “chilica” se fizermos:

printf(""%d %d %d %d\n"", a, a[2], a[3][5], a[0][1][3]); /* aqui, só na base do cast ! */

Isto gera estes probleminhas:

make -k arr
cc -Werror -Wall -Wextra -Wformat-security -Wformat=2 -Wno-format-extra-args    arr.c   -o arr
arr.c: In function ‘main’:
arr.c:131:5: error: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int (*)[14][16]’ [-Werror=format]
arr.c:131:5: error: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘int (*)[16]’ [-Werror=format]
arr.c:131:5: error: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘int *’ [-Werror=format]
arr.c:138:6: error: conflicting types for ‘a’
arr.c:129:9: note: previous declaration of ‘a’ was here
cc1: all warnings being treated as errors
make: ** [arr] Erro 1

Compilation exited abnormally with code 2 at Fri Mar 23 14:03:15

Portanto podemos entender que a[2] é um array de 2 ponteiros para inteiros, a[3][4] é um array de 3 posições a conter ponteiros para arrays sendo que cada uma destas 3 posições é um array de 4 posições a conter ponteiros para inteiros.

Contudo a “operação de subscritar“, i. e. definir subscritos – representada pelo símbolo `[]´ – é definida de modo que, sendo `E1′ um array e `E2′ um inteiro, a operação E1[E2] é idêntica à “operação de apontar” – explicada no nosso artigo anterior. E que, a despeito de sua aparência assimétrica, é uma operação comutativa. ( Lembra da tia do primário ?… 3 + 4 = 4 + 3 )

Uma “prova” disto:

int b[6];

b[1] = 666;
printf("%d %d %d %d\n", b[1], 1[b], *( b + 1), * (1 + b) );

Sei que é estranho referir-se ao array como 1[b], mas está correto. Correto, porém, não quer dizer que seja recomendável.

Um exemplo mais ousado:

#include <stdio.h>

/* imprime na tela o array passaso via * p */
/* x -limite de linhas, y - limite de colunas */
void pra ( int x, int y, void * _p )
{	int i, j;
	int r, c;

	int * p = (int * ) _p;

	for ( i = 0; i < x; i ++ ) {
		for ( j = 0; j < y; j ++ ) {
			r =  i * x;                  /* limite de linha */
			c = (( x < y ) ? i : 0) + j; /* limite de coluna */
			printf("%d ",  * ( p + r + c ) );
		}
		puts("");
	}
}

int main( void )
{   // int a[3][14][16];

	// printf("%d %d %d %d\n", a, a[2], a[3][5], a[0][1][3]); /* aqui, só na base do cast ! */

	int b[6];

	b[1] = 666;
	printf("%d %d %d %d\n", b[1], 1[b], *( b + 1), * ( 1 + b) );

	int c[][3] = {
		{ 1, 2, 3 }, 
		{ 4, 5, 6 }, 
		{ 7, 8, 9 }
	};

	puts("\nUm array c[3][3]:\n");
	pra (3, 3, c);

	printf("\nsizeof(c)       = %2d (3 x 3 x 3 x 4) ", (int) sizeof(c));
	printf("\nsizeof(c[0])    = %2d (3 x 4 )", (int) sizeof(c[0]));
	printf("\nsizeof(c[0][0]) = %2d (1 x 4 )\n", (int) sizeof(c[0][0]));

	puts("\nc.q.d!");

	return 0;
}

Que gera a seguinte saída:


666 666 666 666

Um array a[3][3]:

1 2 3 
4 5 6 
7 8 9 

sizeof(c)       = 36 (3 x 3 x 3 x 4) 
sizeof(c[0])    = 12 (3 x 4 )
sizeof(c[0][0]) =  4 (1 x 4 )

c.q.d!
Anúncios

2 comentários sobre “Arremates … ou melhor: Arraymates!

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s