paulo1205
(usa Ubuntu)
Enviado em 10/02/2014 - 02:16h
É fácil: simplesmente não tem como extrair informações de tamanho original de um array depois que ele é passado a uma função.
Assim como um array em C não é uma variável (i.e. depois de declarado um array "
a[N] ", você não poderá mais fazer algo do tipo "
a=... "), o tamanho do array não é um objeto manipulável no programa em tempo de execução. Até porque, pense bem: se você mesmo teve de declarar o array com tamanho
N , seria redundante fazer o programa guardar esse valor em algum outro lugar (lembre-se que C é uma linguagem de programação de sistemas, quase que um Assembly de nível um pouquinho mais alto).
Uma característica dos arrays em C é que, quando se declara um array, o compilador aloca uma região contígua de memória para que os elementos sucessivos fiquem justapostos. Num caso como o de "
int a[10] ", é fácil ver que memória alocada será um bloco do tamanho suficiente para guardar dez valores inteiros. No caso de um array bidimensional, como "
int a[5][10] ", o que se tem é um array
a com cinco elementos, sendo que cada um desses elementos é um array com dez elementos inteiros. Como os dez inteiros de cada array interno são contíguos, e como cada array interno é contíguo aos demais elementos do array exterior, o que se tem no momento da compilação é que o compilador aloca uma única tripa com espaço para cinquenta elementos inteiros.
Estendendo esse pensamento para N dimensões, você pode pensar que um array N-dimensional é um array unidimensional em que cada elemento é um array com N-1 dimensões idênticas.
Para usar funções genéricas para arrays com tamanhos arbitrários, o que se costuma fazer em C é passar as dimensões do(s) array(s) como parâmetro(s) adicional(is) de cada função. Com elementos contíguos, dependendo do tipo da função, podem ser passados os tamanhos de cada dimensão ou tão-somente a quantidade total de elementos.
Veja os exemplos abaixo.
#include <stdio.h>
/*
Calcula media levando em conta o tamanho total do
bloco contíguo de memória (como se fosse um array 1D).
NOTA: O "void *" é para que eu não precise me preocupar,
na hora de chamar a função, com a quantidades de
dimensões do array nem com o tipo de elemento do
array mais externo; basta que o tipo do elemento
do array mais interno seja "double" (funciona em C;
em C++ exigiria mais cuidados).
*/
double average_1d(void *array, int n_elems){
int left=n_elems;
double *p_elem=array;
double tot=0.0;
do
tot+=*p_elem++;
while(--left);
return tot/(double)n_elems;
}
double average_2d(void *array, int rows, int cols){
return average_1d(array, rows*cols);
}
double average_3d(void *array, int x, int y, int z){
return average_1d(array, x*y*z);
}
/*
Função que localiza a posição do maior elemento num array 3D.
Quando a função é chamada, os ponteiros "x", "y" e "z" devem
apontar para as dimensões do array 3D; ao final da função,
esses valores são sobrescritos (então tome cuidado!) com a
coordenada do elemento, e o valor desse elemento é devolvido
pela função.
*/
double find_biggest_3d(void *array, int *x, int *y, int *z){
int maxx, maxy, maxz;
int i, j, k;
double *p_elem;
double biggest;
maxx=*x;
maxy=*y;
maxz=*z;
p_elem=array;
i=j=k=0;
biggest=*p_elem;
*x=*y=*z=0;
while(1){
k++;
if(k>=maxz){
k=0;
j++;
if(j>=maxy){
j=0;
i++;
if(i>=maxx)
break;
}
}
++p_elem;
if(*p_elem>biggest){
biggest=*p_elem;
*x=i;
*y=j;
*z=k;
}
}
return biggest;
}
/*
É bom hábito não espalhar constantes numéricas pelo código, mas
sempre lhes dar um sentido. Admito que, aqui, até não fica muito
bonito, mas veja como isso tem valor depois, em main().
*/
#define ARR3D_1_X 2
#define ARR3D_1_Y 4
#define ARR3D_1_Z 2
double arr3d_1[ARR3D_1_X][ARR3D_1_Y][ARR3D_1_Z]={
{ {0, 1}, {1, 1}, {1, 0}, {0, 0} },
{ {6, 7}, {4, 5}, {8, 9}, {2, 3} }
};
/*
Um array 2D cujas dimensão mais externa é calculada pelo com-
pilador (infelizmente não dá para deixar todas as dimensões
internas indefinidas...). Neste caso, é fácil ver que o valor
a ser preenchido é três, mas imagine um array gigantesco, que
ocupe várias páginas ou telas. Quem vai querer contar?
Ver abaixo (em main()), como recuperar as dimensões do array.
*/
double arr2d_1[][5]={
{1, 2, 3, 4, 5},
{0, 1, 0, 1, 0},
{0, 9, 8, 7, 6}
};
int main(void){
int x, y, z;
double d;
/* arr3d_1 */
x=ARR3D_1_X;
y=ARR3D_1_Y;
z=ARR3D_1_Z;
printf("Avg 1D de arr3d_1: %lf\n", average_1d(arr3d_1, x*y*z));
printf("Avg 3D de arr3d_1: %lf\n", average_3d(arr3d_1, x, y, z));
d=find_biggest_3d(arr3d_1, &x, &y, &z);
printf("Maior elem de arr3d_1: %lf (em arr3d_1[%d][%d][%d])\n", d, x, y, z);
/* arr2d_1 */
x=(sizeof arr2d_1)/(sizeof arr2d_1[0]);
y=(sizeof arr2d_1[0])/(sizeof arr2d_1[0][0]);
/*
Note que sizeof é um operador que funciona no momento da
compilação. Desse modo, não há acesso aos "valores" de
arr2d_1, arr2d_1[0] e arr2d_1[0][0], mas tão-somente se
trabalha com os tamanhos dos tipos dos elementos.
*/
printf("\nAvg 1D de arr2d_1: %lf\n", average_1d(arr2d_1, x*y));
printf("Avg 3D de arr3d_1: %lf\n", average_2d(arr2d_1, x, y));
/*
Abusos -- CRIANÇAS, NÃO FAÇAM ISTO EM CASA (i.e. não recorram
a tais práticas, a não ser que seja absolutamente necessário)!
Aproveito que sei os tamanhos dos arrays e que eles ocupam
um espaço contíguo de memória para usar uma função 3D num
array 2D, e vice-versa.
Reiterando: isso só funciona porque se conhecem detalhes in-
ternos da disposição dos dados na memória e também que as
funções chamadas são simples. Noutras linguagens, porém, e
noutros contextos de execução, essas assunções podem falhar.
*/
printf("\nCRIANÇAS, NÃO FAÇAM ISTO EM CASA!!!\n");
printf("\nAvg 2D de arr3d_1: %lf\n", average_2d(arr3d_1, 4, 4));
x=5;
y=1;
z=3;
d=find_biggest_3d(arr2d_1, &x, &y, &z);
printf("Maior elem de arr2d_1: %lf (OK) (em arr2d_1[%d][%d][%d] (hein?\?\?!!!))\n", d, x, y, z);
return 0;
}