tutorial de ponteiros e arrays em c

Upload: rafael-goncalves

Post on 10-Oct-2015

17 views

Category:

Documents


1 download

TRANSCRIPT

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    1/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 1/39

    Tutorial de Ponteiros e Arrays em C

    Ted Jensen (Traduo Csar A. K. Grossmann)

    PrefcioAgradecimentosSobre o AutorUso deste MaterialOutras Verses Deste Documento

    Introduo1. O Que Um Ponteiro?

    Referncias2. Tipos Ponteiros e Arrays3. Ponteiros e Strings4. Mais Sobre Strings5. Ponteiros e Estruturas6. Um Pouco Mais Sobre Strings, e Arrays de Strings7. Mais Sobre Arrays Multi-Dimensionais8. Ponteiros Para Arrays9. Ponteiros e Alocao Dinmica de Memria

    Mtodo 1Mtodo 2Mtodo 3Mtodo 4

    10.Ponteiros Para FunesReferncias

    Eplogo

    PrefcioEste documento foi feito como uma introduo aos ponteiros para programadores iniciantes na

    linguagem C. Depois de vrios anos de leituras e contribuies a vrias conferncias sobre C, incluindoas da FidoNet e UseNet, eu notei que um grande nmero de novatos no C parecem ter uma dificuldadeem entender os fundamentos do ponteiros. Eu resolvi ento assumir a tarefa de tentar explicar a eles emuma linguagem simples e com muitos exemplos.

    A primeira verso deste documento foi colocada no domnio pblico, assim como esta. Ela foi escolhidapor Bob Stout, que incluiu o mesmo como o arquivo PTR-HELP.TXT, em sua coleo largamentedistribuda de SNIPPETS. Desde o lanamento original em 1995, eu acrescentei uma quantiasignificativa de material e fiz algumas correes menores ao trabalho original.

    Na verso HTML 1.1, eu fiz um pequeno nmero de mudanas na apresentao como resultado decomentrios que recebi por email de todo o mundo. Na verso 1.2 eu atualizei os dois primeiroscaptulos para refletir a mudana de compiladores 16 bits para compiladores 32 bits nos PCs.

    Agradecimentos

    http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-http://-/?-
  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    2/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 2/39

    Existem tantas pessoas que contribuiram sem saber a este trabalho, por causa das questes que elescolocaram no FidoNet C Echo, ou no Newsgroup UseNet comp.lang.c, ou vrias outras confernciasem outras redes, que seria impossvel listar a todas. Agradecimentos em especial a Bob Stout, que foigentil em incluir a primeira verso deste material em seu arquivo SNIPPETS.

    Sobre o Autor

    Ted Jensen um Engenheiro Eletrnico aposentado que trabalhou como projetista de hardware ougerente de projetistas de hardware no campo de gravao magntica. Programao tem sido um hobbyseu desde 1968, quando ele aprendeu a perfurar cartes para serem executados em um mainframe (omainframe tinha 64K de memria de ncleo magntico!).

    Uso deste Material

    Tudo que est contido aqui est liberado para o Domnio Pblico. Qualquer pessoa pode copiar oudistribuir este material da maneira que desejar. A nica coisa que eu peo que se este material forusado como auxlio em uma class, eu apreciaria se ele fosse distribudo completo, ou seja, incluindotodos os captulos, prefcio e introduo. Eu tambm apreciaria se, nestas circunstncias, o instrutordesta classe me enviasse uma nota em um dos endereos abaixo para me informar disto. Eu escrevi estematerial na esperana que o mesmo fosse til para outros e como eu no estou pedindo nenhumaremunerao financeira, a nica forma de eu saber que eu atingi pelo menos parcialmente meu objetivo atravs deste feedback dos que acharam este material til.

    A propsito, voc no precisa ser um instrutor ou professor para contactar-me. Eu apreciarei uma notade qualquer um que achar este material til, ou que tiver alguma crtica construtiva a oferecer. Tambmestou disposto a responder a perguntas enviadas via email para o endereo abaixo.

    Outras Verses Deste DocumentoAlm da verso hipertexto deste documento, eu disponibilizei outras verses mais apropriadas paraimpresso ou para download do documento completo. Se voc est interesssado em manter-seatualizado com meu progresso nesta rea, ou quiser verificar se h verses mais recentes destedocumento, veja meu Wev Site em http://www.netcom.com/~tiensen/ptr/cpoint.hm

    Ted JensenRedwood City, [email protected]

    Feb. 2000

    IntroduoSe voc quer ter proficincia em escrever cdigo na linguagem C, tem que ter um bom conhecimentosobre como usar ponteiros. Infelizmente, os ponteiros C parecem representar um obstculo para novatos,particularmente para os que vem de outras linguagens de computador, como FORTRAN, Pascal ouBASIC.

    Para ajudar estes novatos a entender ponteiros eu escrevi o material presente. Para obter o mximobenefcio deste material, eu acho importante que o usurio consiga executar o cdigo das vrias listagenscontidas no artigo. Eu tentei, portanto, fazer com que todo o cdigo atendesse s normas ANSI, deforma que ir funcionar com qualquer compilador que tambm atenda as mesmas normas ANSI. Eutambm tentei colocar o cdigo em blocos no texto. Desta forma, com a ajuda de um editor de textos

    mailto:[email protected]://www.netcom.com/~tiensen/ptr/cpoint.hm
  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    3/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 3/39

    ASCII, voc pode copiar um dado bloco de cdigo para um novo arquivo e compilar o mesmo em seusistema. Eu recomendo que os leitores faam isto j que vai ajudar a entender o material.

    1. O Que Um Ponteiro?

    Uma das coisas que os novatos em C tem dificuldade o conceito de ponteiros. O objetivo deste tutorial

    fornecer uma introduo aos ponteiroes e o seu uso para estes novatos.Eu descobri que uma das razes principais para os problemas que os novatos tem com ponteiros queeles tem um conhecimento fraco ou mnimo sobre variveis (da forma que so usadas em C). Assim,vamos comear a discusso com as variveis C em geral.

    Uma varivel em um programa algo que tem um nome, e cujo valor pode variar. A forma que ocompilador e o linker tratam-nas que eles atribuem um bloco especfico da memria do computadorpara guardar o valor da varivel. O tamanho do bloco depende do intervalo de valores permitido varivel. Por exemplo, em um PC de 32 bits, o tamanho de uma varivel integer 4 bytes. Em velhosPCs de 16 bits, era 2 bytes. Em C o tamanho de um tipo de varivel como o inteiro no precisa ser o

    mesmo em todos os tipos de mquinas. Alm disto, existe mais de um tipo de varivel inteira em C.Temos os integers, os long integers, e os short integersque voc vai encontrar em qualquer textobsico sobre C. Este documento assume o uso de um sistema de 32 bits com integersde 4 bytes.

    Se voc quiser saber o tamanho dos vrios tipos de inteiros no seu sisetma, execute o cdigo abaixo queele lhe dar esta informao.

    #include

    int main() {

    printf("size of a short is %d\n", sizeof(short)); printf("size of a int is %d\n", sizeof(int)); printf("size of a long is %d\n", sizeof(long)); }

    Quando declaramos uma varivel, informamos ao compilador duas coisas, o nome da varivel e o tipoda mesma. Por exemplo, declaramos uma varivel do tipo integer com o nome kescrevendo:

    int k;

    Ao ver a parte "int" nesta declarao o compilador reserva 4 bytes de memria (em um PC) paraguardar o valor do integer. Ele tambm monta uma tabela de smbolos. Nesta tabela ele acrescenta osmbolo ke o endereo relativo na memria em que estes 4 bytes foram separados.

    Desta forma, se mais tarde escrevemos:

    k = 2;

    esperamos que, na execuo, quando esta declarao for executada, o valor 2 seja colocado na porode memria reservada para guardar o valor de k. Em C nos referimos a uma varivel como o integer kcomo sendo um "objeto".

    Em um certo sentido existem dois "valores" associados ao objeto k. Um o valor do inteiro armazenadoali (2 no exemplo acima) e o outro o "valor" da localizao de memria, isto , o endereo de k.Alguns textos se referem a estes dois valoers com a nomenclatura rvalue(right value, valor direita,pronunciado "are value"), e lvalue(left value, valor esquerda, pronunciado "el value")respectivamente.

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    4/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 4/39

    Em algumas linguagens, o lvalue o valor que pode aparecer do lado esquerdo de um operador deatribuio '=' (isto , o endero para onde o resultado da expresso do lado direito vai). O rvalue o queest no lado direito da declarao de atribuio, o 2acima. Rvalues no podem ser usados no ladoesquerdo da declarao de atribuio. Desta forma, 2 = k; ilegal.

    Na verdade, a definio acima de "lvalue" foi um pouco modificada para o C. De acordo com K&R II(pgina 197):[1]

    "An objectis a named region of storage an lvalueis an expression referring to an object."

    ("Um objeto uma regio de armazenamento que tem nome um lvalue uma expresso que refere umobjeto.")

    Entretanto, neste ponto, a definio originalmente citada acima suficiente. Conforme nos tornamosmais familiares com ponteiros iremos entrar em mais detalhes sobre isto.

    Vamos considerar agora:

    int j, k;

    k = 2;j = 7;

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    5/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 5/39

    acima. Neste caso, novamente se a declarao estiver fora de qualquer funo, ela inicializada para umvalor que garantidamente no aponta para qualquer objeto C ou funo. Um ponteiro iniciado destaforma chamado de ponteiro "null".

    O padro de bits usado para um ponteiro null pode ou no resultar em zero j que depende do sistemaespecfico no qual o cdigo est sendo desenvolvido. Para fazer com que o cdigo fonte seja compatvelentre vrios compiladores em vrios sistemas, uma macro usada para representar um ponteiro null. Amacro recebe o nome NULL. Assim, colocar o valor NULL em um ponteiro, como em uma declarao

    de atribuio, como ptr = NULL;, garante que o ponteiro um ponteiro null. De forma similar, damesma forma que algum testa um valor inteiro para ver se zero, como em if(k == 0), podemostestar se um ponteiro null usando if(ptr == NULL).

    Voltemos ao uso de nossa nova varivel ptr. Suponha agora que queremos armazenar em ptroendereo de nossa varivel inteira k. Para isto, usamos o operador unrio &e escrevemos:

    ptr = &k;

    O que o operador &faz recuperar o lvalue (endereo) de k, mesmo que kesteja no lado direito dooperador '=' de atribuio, e copia o mesmo para o contedo de nosso ponteiro ptr. Agora, diz-se que ptr"aponta para" k. Continue conosco agora, h somente mais um operador que precisamos discutir.

    O "operador de dereferenciamento" o asterisco, e usado conforme o exemplo abaixo:

    *ptr = 7;

    Esta linha ir copiar o 7 para o endereo apontado por ptr. Assim, se ptr"aponta para" (contm oendereo de) k, a declarao acima ir colocar em ko valor 7. Ou seja, quando usamos o '*' destaforma, estamos nos referindo ao valor para o qual ptr est apontando, no ao valor do ponteiro em si.

    De forma semelhante, podemos escrever:

    printf("%d\n", *ptr);

    para escrever na tela o valor inteiro armazenado no endereo apontado por ptr.

    Uma forma de ver tudo isto junto executar o seguinte programa e ento ervisar o cdigo e a sadacuidadosamente.

    Programa 1.1

    /* Program 1.1 from PTRTUT10.TXT 6/10/97 */

    #include

    int j, k;

    int *ptr;

    int main(void) { j = 1; k = 2; ptr = &k; printf("\n");

    printf("j has the value %d and is stored at %p\n", j, (void *)&j); printf("k has the value %d and is stored at %p\n", k, (void *)&k); printf("ptr has the value %p and is stored at %p\n", ptr, (void *)&ptr); printf("The value of the integer pointed to by ptr is %d\n", *ptr);

    return 0;

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    6/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 6/39

    }

    Nota: Ainda temos que discutir os aspectos de C que requerem o uso da expresso (void *)usadaacima. Por enquanto, inclua a mesma no seu cdigo de teste. Iremos explicar as razes por trs destaexpresso mais tarde.

    Para revisar:

    Uma varivel declarada dando a ela um tipo e um nome (p.ex., int k)Uma varivel ponteiro declarada dando a ela um tipo e um nome (p.ex., int *ptr) onde oasterisco informa ao compilador que a varivel de nome ptr uma varivel ponteiro e o tipoinforma ao compilador que tipo o ponteiro aponta (inteiro neste caso).Uma vez que uma varivel seja declarada, podemos obter seu endereo precedendo o seu nomecom o operador unrio &, como em &k.Podemos "dereferenciar" um ponteiro, isto , referirmos o valor para o qual ele aponta, usando ooperador unrio '*' como em *ptr.O "lvalue" de uma varivel o valor de seu endereo, isto , onde ela est armazenada namemria. O "rvalue" de uma varivel o valor armazenado naquela varivel (naquele endereo).

    Referncias

    [1] "The C Programming Language" 2nd EditionB. Kernighan and D. RitchiePrentice HallISBN 0-13-110362-8

    2. Tipos Ponteiros e ArraysPodemos avanar agora. Consideremos que precisamos identificar o tipoda varivel para o qual oponteiro aponta, como em:

    int *ptr;

    Uma razo para fazer isto que mais tarde, uma vez que ptr "aponte para" alguma coisa, seescrevermos:

    *ptr = 2;

    o compilador saber quantos bytes copiar para aquela localizao de memria apontada por ptr. Se ptrfoi declarado como apontando para um inteiro, 4 bytes sero copiados. De forma semelhante para floatse doubles o nmero apropriado ser copiado. Mas, definir o tipo para o qual o ponteiro aponta permiteum grande nmero de outras formas que o compilador pode interpretar o cdigo. Por exemplo,considere um bloco de memria consistindo em dez inteiros em linha. Ou seja, 40 bytes de memria soseparados para armazenar 10 inteiros.

    Agora, digamos que apontamos nosso ponteiro ptrpara o primeiro destes inteiros. Mais, vamos dizerque aquele inteiro est localizado na posio de memria 100 (decimal). O que acontece quando

    escrevemos: ptr + 1;

    Como o compilador "sabe" que se trata de um ponteiro (isto , seu valor um endereo), e que estapontando para um inteiro (seu endereo atual, 100, o endereo de um inteiro), ele acrescenta 4 a ptr,

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    7/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 7/39

    em vez de 1, assim o ponteiro "aponta" para o prximo inteiro, na posio de memria 104. De formasemelhante, onde o ptr declarado como um ponteiro para um short, ele ser acrescido de 2 em vez de1. O mesmo vale para outros tipos de dados, como floats, doubles, ou mesmo tipos de dados definidospelo usurio, como estruturas. Isto obviamente no o mesmo tipo de "adio" que normalmentepensamos. Em C, ela referida como adio usando "aritmtica de ponteiros", um termo que iremosretornar mais tarde.

    De forma similar, j que ++ptre ptr++so equivalentes a ptr + 1(apesar que o ponto no programa

    em que ptrseja incrementado pode ser diferente), incrementar um ponteiro usando o operador unrio++, tanto pr ou ps, incrementa o endereo que ele armazena pela quantidade sizeof(type)onde"type" o tipo de objeto apontado (isto , 4 para um inteiro).

    Como um bloco de 10 inteiros localizado de forma contgua na memria , por definio, um arraydeinteiros, temos a um relacionamento interessante entre arrays e ponteiros.

    Considere o seguinte:

    int my_array[] = {1, 23, 17, 4, -5, 100};

    Temos a um array contendo 6 inteiros. Nos referimos a cada um destes inteiros via um subscrito amy_array, isto , usando my_array[0]a my_array[5]. Mas podemos alternativamente acess-losusando um ponteiro, como no exemplo abaixo:

    int *ptr; ptr = &my_array[0]; /* aponta nosso ponteiro para o primeiro inteiro em nosso array */

    E assim podemos escrever nosso array usando ou a notao de array, ou dereferenciando nossoponteiro. O seguinte cdigo ilustra isto:

    Programa 2.1

    /* Program 2.1 from PTRTUT10.HTM 6/13/97 */

    #include

    int my_array[] = {1,23,17,4,-5,100}; int *ptr;

    int main(void) { int i;

    ptr = &my_array[0]; /* point our pointer to the first element of the array */ printf("\n\n"); for (i = 0; i < 6; i++) { printf("my_array[%d] = %d ",i,my_array[i]); /*

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    8/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 8/39

    e execute o programa novamente... ento mude para:

    printf("ptr + %d = %d\n", i, *(++ptr));

    e tente novamente. Cada vez tente prever o resultado e examine com cuidado o resultado real.

    Em C, o padro declara que onde precisamos usar &var_name[0], podemos trocar por var_name,assim, no nosso cdigo, onde escrevemos:

    ptr = &my_array[0];

    podemos escrever:

    ptr = my_array;

    para obter o mesmo resultado.

    Por causa disto que muitos textos se referem ao nome de um array como um ponteiro. Eu prefiro pensarque "o nome do array o endereo do primeiro elemento do array". Muitos iniciantes (inclusive eu

    quando estava aprendendo) tem a tendncia de ficar confuso ao pensar nisto como um ponteiro. Porexemplo, enquanto podemos escrever

    ptr = my_array;

    no podemos escrever

    my_array = ptr;

    A razo que enquanto ptr uma varivel, my_array uma constante. Ou seja, o local onde oprimeiro elemento de my_arrayser armazenado no pode ser alterado uma vez que my_array[]tenhasido declarado.

    Um pouco antes, quando discutimos o termo "lvalue", eu citei K&R-2 onde est declarado:

    "An objectis a named region of storage an lvalueis an expression referring to an object".

    ("Um objeto uma regio de armazenamento com nome um lvalue uma expresso que se refere aum objeto").

    Isto leva a um problema interessante. Como my_array uma regio de armazenamento nomeada, porqu my_arrayna declarao acima no um lvalue? Para resolver este problema, alguns se referem a

    my_arraycomo sendo um "lvalue no modificvel".Modifique o programa exemplo acima trocando

    ptr = &my_array[0];

    para

    ptr = my_array;

    e execute novamente para ver se os resultados so idnticos.

    Agora, vamos nos aprofundar um pouco mais na diferena entre os nomes ptre my_arraycomo foramusados acima. Alguns autores se referem ao nome de um array como sendo um ponteiro constante. Oque isto quer dizer? Bem, para entender o termo "constante" neste sentido, vamos retornar nossadefinio do termo "varivel". Quando declaramos uma varivel ns separamos um ponto na memriapara armazenar o valor do tipo apropriado. Uma vez que isto foi feito, o nome da varivel pode ser

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    9/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 9/39

    interpretado em uma de duas formas. Quando usado no lado esquerdo do operador de atribuio, ocompilador interpreta como o local de memria para o qual mover o valor resultante da avaliao dotermo do lado direito do operador de atribuio. Mas, quando usado no lado direito do operador deatribuio, o nome da varivel interpretado como significando o contedo armazenado naqueleendereo de memria, separado para armazenar o valor daquela varivel.

    Com isto em mente, vamos agora considerar a mais simples das constantes, como em:

    int i, k; i = 2;

    Aqui, enquanto i uma varivel e ento ocupa espao na poro dedicada aos dados na memria, 2uma constante e, como tal, em vez de receber memria no segmento de dados, ela est inseridadiretamente no segmento de cdigo da memria. Ou seja, enquanto escrever algo como k = i;diz aocompilador para criar cdigo que na execuo ir procurar a localizao de memria &ipara determinaro valor a ser movido para k, o cdigo criado por i = 2;simplemente coloca 2no cdigo e no hreferncias ao segmento de dados. Ou seja, tanto kquanto iso objetos, mas 2no um objeto.

    De forma similar, no exemplo acima, como my_array uma constante, uma vez que o compilador

    estabelece onde o array ser armazenado, ele "sabe" o endereo de my_array[0]e ao ver:

    ptr = my_array;

    ele simplesmente usa este endereo como uma constante no segmento de cdigo e, portanto, no estfazendo referncia ao segmento de dados.

    Este pode ser um bom momento para explicar um pouco mais o uso da expresso (void *)usada noPrograma 1.1 no Captulo 1. Como j vimos, podemos ter ponteiros de vrios tipos. At agoradiscutimos ponteiros para inteiros e ponteiros para caracteres. Nos captulos seguintes aprenderemossobre ponteiros a estruturas e mesmo ponteiros a ponteiros.

    Tambm aprendemos que em diferentes sistemas o tamanho de um ponteiro pode variar. Da mesmaforma, possvel que o tamanho de um ponteiro possa variar dependendo do tipo de objeto apontado.Assim, da mesma forma que com os inteiros voc pode ter problemas se tentar atribuir um inteiro long auma varivel do tipo short, voc pode ter problemas se tentar atribuir valores de ponetiros de vrios tipospara variveis ponteiro de outros tipos.

    Para minimizar este problema, o C permite ponteiros do tipo void. Podemos declarar este tipo deponteiro escrevendo:

    void *vptr;

    Um ponteiro void um tipo de ponteiro genrico. Por exemplo, enquanto o C no permite acomparao de um ponteiro do tipo inteiro com um ponteiro do tipo caracter, por exemplo, qualquer umdeles pode ser comparado a um ponteiro void. Obviamente, como com outras variveis, castspodem serusados para converter de um tipo de ponteiro para outro, nas circunstncias apropriadas. No Programa1.1 do Captulo 1 eu fiz um castdos ponteiros para inteiros para ponteiros void para que ficassemcompatveis com a especificaod e converso de %p. Nos captulos seguintes, outros castssero feitospor razes a serem apresentadas.

    Bem, foi bastante coisa tcnica para digerir, e eu no espero que um iniciante tentenda tudo isto na

    primeira leitura. Com o tempo e experincia voc vai voltar e reler os dois primeiros captulos. Por ora,vamos avanar para o relacionamento entre os ponteiros, arrays de caracter, e strings.

    3. Ponteiros e Strings

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    10/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 10/39

    O estudo de strings til para entender mais o relacionamento entre ponteiros e arrays. Ele tambmtorna fcil ilustrar como algumas das funes string padro do C podem ser implementadas. Finalmente,ele ilustra como e quando os ponteiros podem e devem ser passados funes.

    Em C, strings so arrays de caracteres. Isto no necessariamente verdadeiro em outras linguagens. EmBASIC, Pascal, FORTRAN e vrias outras linguagens, uma string tem seu prprio tipo de dados. Masem C, no. Em C uma string qualquer array de caracteres terminado com um caracter binrio zero(tambm chamado de nul ou nulo, escrito como '\0'). Para comear nossa discusso vamos escrever

    algum cdigo que, enquanto serve como ilustrao, voc provavelmente nunca escrever algo parecidoem um programa real. Considere, por exemplo:

    char my_string[40];

    my_string[0] = 'T'; my_string[1] = 'e'; my_string[2] = 'd': my_string[3] = '\0';

    Apesar de ningum nunca escrever uma string desta forma, o resultado final um array de caracteres

    terminado com um caracter nul. Por definio, em C, uma string um array de caracteres terminadacom o caracter nul. Perceba, entretanto, que "nul" **no * o mesmo que "NULL". 'nul' refere-se aozero como definido pela seqncia de escape '\0'. Ela ocupa um byte de memria. NULL, por outrolado, o nome da macro usada para iniciar ponteiros nulos. NULL definido por #defineem umarquivo de cabealho em seu compilador C, nul pode no ser definido em lugar nenhum.

    Como escrever o cdigo acima pode demorar bastante, o C permite duas formas alternativas de chegarao mesmo fim. Primeiro, pode-se escrever:

    char my_string[40] = ('T', 'e', 'd', '\0'};

    Mas isto tambm usa mais digitao do que conveniente. Assim, o C permite: char my_string[40] = "Ted";

    Quando as aspas duplas so usadas, em vez das aspas simples (ou plicas), como nos exemplosanteriores, o caracter nul ('\0') automaticamente acrescentado no fim da string.

    Em todos os casos acima, a mesma coisa acontece. O compilador separa um bloco contguo de memriade tamanho 40 bytes para guardar os caracteres e inicia o mesmo de forma que os 4 primeiros caracteresso Ted\0.

    Agora, considere o seguinte programa:

    Programa 3.1

    /* Program 3.1 from PTRTUT10.HTM 6/13/97 */

    #include

    char strA[80] = "A string to be used for demonstration purposes"; char strB[80];

    int main(void)

    {

    char *pA; /* a pointer to type character */ char *pB; /* another pointer to type character */ puts(strA); /* show string A */ pA = strA; /* point pA at string A */

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    11/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 11/39

    puts(pA); /* show what pA is pointing to */ pB = strB; /* point pB at string B */ putchar('\n'); /* move down one line on the screen */ while(*pA != '\0') /* line A (see text) */ { *pB++ = *pA++; /* line B (see text) */ } *pB = '\0'; /* line C (see text) */ puts(strB); /* show strB on screen */

    return 0; }

    No exemplo acima comeamos definindo dois arrays de caracteres de 80 caracteres cada. Como eles sodefinidos globalmente, eles so inicializados com '\0's primeiro. Ento strAtem os 42 primeiroscaracteres inicializados para a string entre aspas.

    Agora, avanando no cdigo, declaramos dois ponteiros de caracter e mostramos a string na tela. Ento"apontamos" o ponteiro pApara strA. Isto , via a declarao de atribuio ns copiamos o endereode strA[0]em nossa varivel pA. Agora usamos puts()para mostrar o que est apontado por pAnatela. Considere aqui que o prottipo da funo puts():

    int puts(const char *s);

    Por enquato, ignore o const. O parmetro passado a puts() um ponteiro, ou seja, o valorde umponteiro (j que todos os parmetros em C so passados por valor), e o valor de um ponteiro oendereo para o qual ele est apontando, ou, simplesmente, um endereo. Assim, quando escrevemosputs(strA)como vimos antes, estamos passando o endereo de strA[0].

    De forma similar, quando escrevemos puts(pA)estamos passando o mesmo endereo, j que fizemospA = strA;

    Isto posto, seguimos o cdigo at a declarao while()na linha A. A linha A declara:

    Enquanto o caracter apontado por pA(isto , *pA) no for um caracter nul (isto , o terminador '\0'),fazemos o seguinte:

    A linha B declara: copie o caracter apontado por pApara o espao apontado por pB, ento incrementepA, de forma que aponte para o prximo caracter e pBaponte para o prximo espao.

    Quando copiamos o ltimo caracter, pAaponta agora para o caracter de terminao nul e o laotermina. Entretanto, no copiamos o caracter nul. E, por definio uma string em C deveser terminadapor um nul. Assim, ns acrescentamos o caracter nul com a linha C.

    bastante educacional executar este programa com seu depurador ao mesmo tempo que examina strA,strB, pAe pBe andando passo a passo pelo programa. E at mais educacional se em vez desimplesmente definir strB[]como foi feito acima, colocarmos um valor inicial como:

    strB[80] = "1234567890123456789012345678901234567890";

    onde o nmero de dgitos usado seja maior que o comprimento de strAe ento repetir o procedimentode seguir passo a passo o programa ao mesmo tempo que examina as variveis acima. Faa estaexperincia!

    Voltando ao prottipo de puts()por um momento, o "const" usado como um modificador deparmetros que informa ao usurio que a funo no ir modificar a string apontada por s, isto , a stringser tratada como uma constante.

    Obviamente, o que o programa acima ilustra uma forma simples de copiar uma string. Aps brincar

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    12/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 12/39

    com o cdigo acima at ter um bom entendimento do que est acontecendo, podemos seguir com acriao de nosso prprio substituto para o strcpy()que vem com o C. Ele pode ficar assim:

    char *my_strcpy(char *destination, char *source) { char *p = destination; while (*source != '\0') { *p++ = *source++;

    } *p = '\0'; return destination; }

    Neste caso, eu segui a prtica usada na rotina padro que retornar um ponteiro para o destino.

    Novamente, a funo projetada para aceitar os valores de dois ponteiros de caracteres, isto ,endereos, e assim no programa anterior podemos escrever:

    int main(void)

    { my_strcpy(strB, strA); puts(strB); }

    Eu mudei um pouco da forma usada no C padro que teria o prottipo:

    char *my_strcpy(char *destination, const char *source);

    Aqui, o modificador "const" usado para garantir ao usurio que a funo no modificar o contedoapontado pelo ponteiro origem. Voc pode provar isto modificando a funo acima, e seu prottipo,para incluir o modificador "const" como mostrado. Ento, dentor da funo voc pode acerscentar uma

    declarao que tente mudar o contedo do que apontado pela fonte, como em:

    *source = 'X';

    que normalmente mudaria o primeiro caracter da string para um X. O modificador const deve fazer queseu compilador aponte isto como um erro. Tente e veja.

    Agora, vamos considerar algumas das coisas que os exemplos acima nos mostraram. Primeiro, considereo fato que *ptr++deve ser interpretado como retornando o valor apontado por ptre em seguidaincrementando o valor do ponteiro. Isto tem a ver com a precedncia de operadores. Onde escrevemos(*ptr)++ns incrementaremos, no o ponteiro, mas o que o ponteiro aponta! Isto , se usado noprimeiro caracter da string exemplo o 'T' seria incrementado para um 'U'. Voc pode escrever um cdigoexemplo simples para ilustrar isto.

    Lembre novamente que uma string nada mais que um array de caracteres, com o ltimo caracter sendoum '\0'. O que fizemos acima tratar com a cpia de um array. Aconteceu ser um array de caracteres,mas a tcnica pode ser aplicada a um array de inteiros, doubles, etc. Naqueles casos, entretanto, noestaremos tratando com strings e portanto o fim do array no ser marcado com um valor especial comoo caracter nulo. Podemos implementar uma verso que dependa de um valor especial para identificar ofim. Por exemplo, podemos copiar um aray de inteiros positivos marcando o fim com um inteironegativo. Por outro lado, mais usual que quando escrevermos uma funo para copiar um array de

    itens que no strings, passemos para a funo o nmero de itens a serem copiados bem como o endereodo array, por exemplo, algo como o indicado pelo prottipo abaixo:

    void int_copy(int *ptrA, int *ptrB, int nbr);

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    13/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 13/39

    onde nbr o nmero de inteiros a serem copiados. Voc pode querer brincar com esta idia e criar umarray de inteiros e ver se consegue escrever a funo int_copy()e faz-la funcionar.

    Isto permite usar funes para tratar arrays grandes. Por exemplo, se temos um array de 5000 inteirosque queremos manipular com uma funo, precisamos passar para a funo s o endereo do array (equalquer informao auxiliar como o nbr acima, dependendo do que estamos fazendo). O array em sino passado, isto , o array inteiro no copiado e colocado na pilha antes do chamado funo, sseu endereo enviado.

    Isto diferente de passar, por exemplo, um inteiro a uma funo. Quando passamos um inteiro fazemosuma cpia do inteiro, isto , o seu valor obtido e colocado na pilha. Dentro da funo qualquermanipulao do valor passado no pode afetar de forma alguma o valor original. Mas, com arrays eponteiros podemos passar o endereo da varivel e, portanto, manipular os valores das variveisoriginais.

    4. Mais Sobre Strings

    Bem, progredimos um bom tanto em to pouco tempo! Vamos olhar para trs um pouco e ver o que foifeito no Captulo 3, sobre cpias de Strings, mas em uma luz diferente. Considere a seguinte funo:

    char *my_strcpy(char dest[], char source[]) { int i = 0; while (source[i] != '\0') { dest[i] = source[i]; i++; } dest[i] = '\0';

    return dest; }

    Lembre que strings so arrays de caracteres. Aqui escolhemos usar a notao de array em vez denotao de ponteiros para fazer a cpia. O resultado o mesmo, isto , a string copiada usando estanotao to correto quanto antes. Isto levanta alguns pontos interessantes para discusso.

    Como os parmetros so passados por valor, tanto no passar o ponteiro caracter ou o nome do array,como no exemplo acima, o que realmente passado o endereo do primeiro elemento de cada array.Assim, o valor numrico do parmetro o mesmo, caso usemos um ponteiro de caracter ou um nome dearray como parmetro. Isto parece implicar que de alguma forma source[i] o mesmo que *(p+i).

    De fato, isto verdadeiro, isto , onde est escrito a[i]pode-se substituir com *(a+i)sem qualquerproblema. De fato, o compilador ir criar o mesmo cdigo em qualquer dos casos. Assim nos vemos quea aritmtica de ponteiro a mesma coisa que a indexao de array. Qualquer uma das duas sintaxesproduz o mesmo resultado.

    Isto NO quer dizer que ponteiros e arrays so a mesma coisa, eles no so. Estamos apenas dizendoque para identificar um dado elemento em um array temos a escolha de duas sintaxes, uma usandoindexao de array e outra usando aritmtica de ponteiros, o que d resultados idnticos.

    Agora, olhando para a ltima expresso, parte dela, (a+i), uma simples adio usando o operador +eas regras do C dizem que esta expresso comutativa. Ou seja, (a+i) idntico a (i+a). Assim, podemosescrever *(i+a)com a mesma facilidade que escrevemos *(a+i).

    Mas *(i+a)poderia vir de i[a]! Disto decorre a curiosa verdade que, se:

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    14/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 14/39

    char a[20]; int i;

    escrever

    a[3] = 'x';

    o mesmo que escrever

    3[a] = 'x';

    Tente! Crie um array de caracteres, inteiros ou longs, etc. e atribua ao terceiro ou quarto elemento umvalor usando a abordagem convencional e ento escreva o valor para ter certeza que est funcionando.Ento reverta a notao de array como foi feito acima. Um bom compilador no vai reclamar e osresultados sero idnticos. Uma curiosidade... nada mais!

    Agora, olhando para nossa funo acima, quando escrevemos:

    dest[i] = source[i];

    defido ao fato que a indexao de array e a aritmtica de ponteiro d os mesmos resultados, podemosescrever isto como:

    *(dest + i) = *(source + i);

    Mas isto toma 2 adies para cada valor de i. Adies, falando em termos gerais, tomam mais tempo queincrementos (como os feitos usando o operador ++, como em i++). Isto pode no ser verdade emcompiladores modernos com otimizao, mas no se pode ter certeza. Assim, a verso de ponteiro podeser um pouco mais rpida que a verso de array.

    Outra forma de tornar mais rpida a verso de ponteiro seria mudar:

    while (*source != '\0')

    para simplesmente

    while (*source)

    j que o valor entre parntesis ir resultar em zero (FALSE) no mesmo momento em cada um dos casos.

    Neste ponto voc pode querer experimentar um pouco escrevendo seus prprios programas usandoponteiros. A manipulao de strings um bom lugar para experimentar. Voc pode querer escrever sua

    prpria verso de funes padro como:

    strlen(); strcat(); strchr();

    e quaisquer outras que voc possa ter em seu sistema.

    Voltaremos as strings e sua manipulao via ponteiros em um outro captulo. Por enquanto, vamosmudar e discutir um pouco de estruturas.

    5. Ponteiros e EstruturasComo voc sabe, podemos declarar a forma de um bloco de dados contendo diferentes tipos de dados

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    15/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 15/39

    via uma declarao de estrutura. Por exemplo, um arquivo de pessoal pode conter estruturas que separeam com algo assim:

    struct tag { char lname[20]; /* ltimo nome */ char fname[20]; /* primeiro nome */ int age; /* idade */ float rate; /* p. ex. 12.75 por hora */ };

    Digamos que temos um punhado destas estruturas em um arquivo em disco e queremos ler cada uma eescrever o primeiro e ltimo nomes de cada um de forma que tenhamos uma lista das pessoas em nossosarquivos. As informaes restantes no devem ser escritas. Faremos isto escrevendo com uma chamadade funo e passando para aquela umo um ponteiro para a estrutura mo. Para demonstrao eu ireiusar somente uma estrutura, mas perceba que o objetivo aqui escrever a funo, no a leitura doarquivo que, presumivelmente, ns sabemos como fazer.

    Para reviso, lembre que podemos acessar membros de estruturas com o operador ponto, como em:

    Programa 5.1

    /* Program 5.1 from PTRTUT10.HTM 6/13/97 */

    #include #include

    struct tag { char lname[20]; /* last name */ char fname[20]; /* first name */ int age; /* age */ float rate; /* e.g. 12.75 per hour */

    };

    struct tag my_struct; /* declare the structure my_struct */

    int main(void) { strcpy(my_struct.lname,"Jensen"); strcpy(my_struct.fname,"Ted"); printf("\n%s ",my_struct.fname); printf("%s\n",my_struct.lname); return 0; }

    Agora, esta estrutura em particular bem pequena comparada com muitas usadas em programas C. Aosdados acima queremos acrescentar:

    date_of_hire; (data types not shown) date_of_last_raise; last_percent_increase; emergency_phone; medical_plan; Social_S_Nbr; etc.....

    Se temos um grande nmero de empregados, o que querermos manipular os dados nestas estruturasatravs de funes. Por exemplo, podemos querer que uma funo escreva o nome do empregadolistado em qualquer estrutura passada ao mesmo. Entretanto, no C original (Kernighan & Ritchie, 1stEdition) no era possvel passar uma estrutura, somente um ponteiro para uma estrutura podia serpassado. Em ANSI C, permitido passar a estrutura completa. Mas, como nosso objetivo aqui

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    16/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 16/39

    aprender mais sobre ponteiros, no iremos fazer isto.

    De qualquer forma, se passamos a estrutura toda isto significa que teremos de copiar todo o contedo daestrutura da funo que est fazendo a chamada para a funo chamada. Em sistemas que usam pilha,isto feito empurrando o contedo da estrutura para a pilha. Com estruturas enormes isto pode ser umproblema. Entretanto, passar um ponteiro usa um mnimo do espao da pilha.

    De qualquer forma, como esta uma discusso sobre ponteiros, iremos discutir como passar um

    ponteiro de de estrutura e como us-lo dentro de uma funo.

    Considere o caso descrito, ou seja, queremos uma funo que ir aceitar como parmetro um ponteiropara uma estrutura e que dentro daquela funo queremos acessar os membros da estrutura. Porexemplo, queremos escrever o nome do empregado em nossa estrutura exemplo.

    Certo, agora sabemos que nosso ponteiro ir apontar para uma estrutura declarada usando struct tag.Declaramos um ponteiro destes com a declarao:

    struct tag *str_ptr;

    e apontamos para a nossa estrutura exemplo com:

    st_ptr = &my_struct;

    Agora, podemos acessar um dado membro dereferenciando o ponteiro. Mas, como ns dereferenciamoso ponteiro a uma estrutura? Bem, considere o fato que podemos querer usar o ponteiro para mudar aidade do empregado. Poderamos escrever:

    (*str_ptr).age = 63;

    Olhe esta linha com cuidado. Ela diz, substitua o que est entre parntesis pelo que st_ptrest

    apontando, que a estrutura my_struct. Assim, isto faz o mesmo que my_struct.age.

    Entretanto, esta expresso usada com uma certa freqncia, e os projetistas de C criaram uma sintaxealternativa com o mesmo significado que :

    st_ptr->age = 63;

    Com isto em mente, olhe para o seguinte programa:

    Programa 5.2

    /* Program 5.2 from PTRTUT10.HTM 6/13/97 */

    #include #include

    struct tag{ /* the structure type */ char lname[20]; /* last name */ char fname[20]; /* first name */ int age; /* age */ float rate; /* e.g. 12.75 per hour */ };

    struct tag my_struct; /* define the structure */ void show_name(struct tag *p); /* function prototype */

    int main(void) { struct tag *st_ptr; /* a pointer to a structure */

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    17/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 17/39

    st_ptr = &my_struct; /* point the pointer to my_struct */ strcpy(my_struct.lname,"Jensen"); strcpy(my_struct.fname,"Ted"); printf("\n%s ",my_struct.fname); printf("%s\n",my_struct.lname); my_struct.age = 63; show_name(st_ptr); /* pass the pointer */ return 0; }

    void show_name(struct tag *p) { printf("\n%s ", p->fname); /* p points to a structure */ printf("%s ", p->lname); printf("%d\n", p->age); }

    Novamente, bastante informao para ser absorvida de uma s vez. O leitor deve compilar e executaros vrios trechos de cdigo e usando um depurador monitar coisas como my_structe penquantoexecuta passo a passo maine seguindo o cdigo at para dentro do cdigo da funo para ver o queest acontecendo.

    6. Um Pouco Mais Sobre Strings, e Arrays deStrings

    Bem, vamos voltar um pouco s strings. Na discusso que se segue, todas as atribuies devem serentendidas como sendo globais, ou seja, feitas fora de qualquer funo, incluindo main().

    Ns apontamos em um captulo anterior que podemos escrever:

    char my_string[40] = "Ted";

    o que ir alocar espao para um array de 40 bytes e colocar a string nos primeiros 4 bytes (trs para oscaracteres entre aspas e o quarto para guardar o terminador '\0').

    Na verdade, se tudo o que quisssemos era armazenar o nome "Ted" poderamos ter escrito:

    char my_name[] = "Ted";

    e o compilador contaria os caracteres, deixaria espao para o caracter nul e armazenar o todal de quatro

    caracteres na posio de memria que seria retornada pelo nome do array, neste caso, my_name

    Em alguns cdigos, em vez do acima, voc pode ver:

    char *my_name = "Ted";

    que a abordagem alternativa. Existe uma diferena entre elas? A resposta ... sim. Usando a notaode array 4 bytes de armazenamento no bloco de memria esttica separado, um para cada caracter eum para o caracter nul terminador. Mas na notao de ponteiro os mesmos 4 bytes so requeridos, maisN bytes para armazenar a varivel ponteiro my_name(onde N depende do sistema, mas usualmente um mnimo de 2 bytes e pode ser 4 ou mais).

    Na notao arary, my_name uma abreviao para &my_name[0]que o endereo do primeiroelemento do array. Como a localizao do array fixo durante o tempo de execuo, esta umaconstante (e no uma varivel). Na notao de ponteiro, my_name uma varivel. Sobre qual o melhormtodo, isto depende do que voc vai fazer no resto do programa.

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    18/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 18/39

    Avancemos um passo adiante e consideremos o que acontece se cada uma destas declaraes feitadentro de uma funo em vez de ser feita globalmente, fora dos limites de qualquer funo.

    void my_function_A(char *ptr) { char a[] = "ABCDE" . . }

    void my_function_B(char *ptr) { char *cp = "FGHIJ" . . }

    No caso de my_function_A, o contedo, ou valor(es) do array a[] considerado como sendo os dados.O array dito como tendo inicialmente os valores ABCDE. NO caso de my_function_Bo valor doponteiro cp considerado como sendo o dado. O ponteiro inicialmente aponta para a string FGHIJ.Tanto em my_function_Ae my_function_Bas definies so variveis locais e assim a stringABCDE armazenada na pilha, bem como o valor do ponteiro cp. A string FGHIJpode serarmazenada em qualquer lugar. Em meu sistema ela armazenada no segmento de dados.

    A propsito, a inicializao de variveis automticas de array como eu fiz em my_function_Aseriailegal no velho K&R C e somente "amadureceu" no mais recente ANSI C. Um fato que pode serimportante quando se considera portabilidade e compatibilidade retroativa.

    Como estamos discutindo o relacionamento/diferenas entre ponteiros e arrays, vamos dar uma olhadaem arrays multi-dimensionais. Considere, por exemplo, o array:

    char multi[5][10];

    O que isto significa? Bem, vamos considerar da seguinte forma.

    char multi[5][10];

    Vamos pegar a parte sublinhada como se fosse o "nome" de um array. Ento colocando antes a partechare aps acrescentarmos a parte [10], ns etmos um array de 10 caracteres. Mas o nome multi[5]por si s um array indicando que existem 5 elementos, cada um deles sendo um array de 10 caracteres.Assim ns temos um array de 5 arrays de 10 caracteres cada.

    Assumindo que tenhamos preenchido este array bidimensional com dados de algum tipo. Na memria,ele pode parecer como tendo sido formado pela inicializao de 5 arrays separados, usando algo como:

    multi[0] = {'0','1','2','3','4','5','6','7','8','9'} multi[1] = {'a','b','c','d','e','f','g','h','i','j'} multi[2] = {'A','B','C','D','E','F','G','H','I','J'} multi[3] = {'9','8','7','6','5','4','3','2','1','0'} multi[4] = {'J','I','H','G','F','E','D','C','B','A'}

    Ao mesmo tempo, elementos individuais podem ser endereados usando a sintaxe:

    multi[0][3] = '3'; multi[1][7] = 'h'; multi[4][0] = 'J';

    Como os arrays so contguos na memria, nosso bloco de memria para a matriz acima pode ser algo

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    19/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 19/39

    do tipo:

    0123456789abcdefghijABCDEFGHIJ9876543210JIHGFEDCBA ^ |_____ starting at the address &multi[0][0]

    Note que eu noescrevi multi[0]="0123456789". Se eu tivesse feito isto, um terminador '\0'seriaacrescentado aos caracters contidos entre aspas. Seria este o caso se eu tivesse separado espao para 11caracteres por linha em vez de 10.

    Meu objetivo acima ilustrar como a memria arranjada para arrays de duas dimenses. Ou seja, este um array bidimensional de caracteres, NO um array de "strings".

    Agora, o compilador sabe quantas colunas esto presentes no array de forma que ele pode interpretarmulti + 1como o endereo do 'a' na segunda linha acima. Ou seja, ele acrescenta 10, o nmero decolunas, para obter sua posio. Se estivermos lidando com inteiros e um array com a mesma dimensoo compilador acrescentaria 10*sizeof(int)que, em minha mquina, seria 20. Assim, o endereo de 9 naquarta linha acima seria &multi[3][0]ou (multi + 3)em notao de ponteiro. Para obter o contedo dosegundo elemento na quarta linha ns acrescentaramos 1 a seu endereo e dereferenciaramos o

    resultado como em

    *(*(multi + 3) + 1)

    Com um pouco de raciocnio, podemos ver que:

    *(*(multi + row) + col) e multi[row][col] do o mesmo resultado.

    O programa seguinte ilustra isto usando arrays de inteiros em vez de arrays de caracteres.

    Programa 6.1 /* Program 6.1 from PTRTUT10.HTM 6/13/97*/

    #include #define ROWS 5 #define COLS 10

    int multi[ROWS][COLS];

    int main(void) { int row, col; for (row = 0; row < ROWS; row++) { for (col = 0; col < COLS; col++) { multi[row][col] = row*col; } }

    for (row = 0; row < ROWS; row++) { for (col = 0; col < COLS; col++) {

    printf("\n%d ",multi[row][col]); printf("%d ",*(*(multi + row) + col)); } }

    return 0;

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    20/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 20/39

    }

    Por causa da dupla de-referncia exigida na verso com ponteiros, o nome de um array bi-dimensional geralmente dito como sendo o equivalente a um ponteiro para ponteiro. Com um array tridimensionalns estaramos lidando com um array de arrays de arrays e alguns diriam que seu nome seria oequivaletne a um ponteiro para ponteiro para ponteiro. Entretanto, ns aqui separamos um bloco dememria inicialmente para o array usando a notao de array. Portanto, estamos lidando com umaconstante, no uma varivel. Ou seja, estamos falando sobre um endereo fixo, e no uma varivel

    ponteiro. A funo de dereferncia usada acima permite que acessemos qualquer elemento no array dearrays sem precisar mudar o valor daquele endereo (o endereo de multi[0][0]como dado pelo smbolomulti).

    7. Mais Sobre Arrays Multi-Dimensionais

    No captulo anterior ns notamos que, dado

    #define ROWS 5

    #define COLS 10int multi[ROWS][COLS];

    ns podemos acessar os elementos individuais do array multiusando ou

    multi[row][col]

    ou

    *(*(multi + row) + col)

    Para entender melhor o que est acontecendo, vamos trocar

    *(multi + row)

    por Xcomo em

    *(X + col)

    Agora, a partir disto ns vemos que o Xfunciona como um ponteiro, j que a expresso de-referenciada e sabemos que col um inteiro. Aqui a aritmtica usada um tipo especial, chamada"aritmtica de ponteiros". Isto significa que, como estamos falando de um array de inteiros, o endereo

    apontado por (ou seja, pelo valor de) X + col + 1deve ser maior que o endereo X + colpor umaquantia igual a sizeof(int).

    Como conhecemos o leiaute da memria para arrays de duas dimenses, podemos determinar que naexpresso multi + rowcomo a usada acima, multi + row + 1deve incrementar de valor uma quantiaigual necessria para "apontar para" a prxima linha, que neste caso deve ser uma quantia igual aCOLS * sizeof(int).

    Isto diz que se a expresso *(*(multi + row) + col)deve ser avaliada corretamente na execuo, ocompilador deve gerar o cdigo que leve em considerao o valor de COLS, isto , a segunda

    dimenso. Devido equivalncia das duas formas de expresso, isto verdadeiro quer estejamos usandoa expresso com ponteiros como aqui, ou a expresso de array multi[row][col].

    Assim, para calcular qualquer das expresses, um total de 5 valores devem ser conhecidos:

    1. O endereo do primeiro elemento do array, que retornado pela expresso multi, isto , o nome

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    21/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 21/39

    do array.2. O tamanho do tipo de elementos do array, neste caso, sizeof(int).3. A segunda dimenso do array.4. O valor do ndice da primeira dimenso, rowneste caso.5. O valor do ndice da segunda dimenso, colneste caso.

    Dado tudo isto, considere o problema de designar uma funo para manipular os valores de elementosde um array previamente declarado. Por exemplo, pode-se colocar em todos os elementos do array multi

    o valor 1.

    void set_value(int m_array[][COLS]) { int row, col; for (row = 0; row < ROWS; row++) { for (col = 0; col < COLS; col++) { m_array[row][col] = 1; }

    } }

    E para chamar esta funo usaramos:

    set_value(multi);

    Agora, a partir da funo ns usamos os valores que foram definidos via #definecomo ROWS eCOLS que estabelecem os limites para os laos. Mas estes #defineso apenas constantes no que tangeao compilador, isto , no h nada que conecte estes nmeros ao tamanho do array dentro da funo.rowe colso variveis locais, obviamente. A definio formal de parmetros permite que o compilador

    determine as caractersticas associadas com o valor do ponteiro que ser passado em tempo de execuo.Ns realmente no precisamos a primeira dimenso e, como veremos mais tarde, existem ocasies ondeiremos perferir no defin-los dentro da definio de parmetro, fora o hbito ou consistncia, eu nousei aqui. Mas a segunda dimenso deve ser usada e foi vista na expresso para o parmetro. A razopor que precisamos dela para resolver m_array[row][col]j foi descrita. Enquanto o parmetro define otipo de dados (intneste caso) e as variveis automticas para linha e coluna so definidas nos laos,somente um valor podoe ser passado usando um nico parmetro. Neste caso, o valor de multicomonotado na declarao de chamada, isto , o endereo do primeiro elemento, geralmente referido comosendo um ponteiro para o array. Assim, a nica forma que temos de informar o compilador da segundadimenso incluindo-a explicitamente na definio do parmetro.

    De fato, em geral todas as dimenses de ordem maior que um so necessrias quando se trata de arraysmulti-dimensionais. Ou seja, se estamos falando de arrays de 3 dimenses, a segunda e terceiradimenses devem ser especificadas na definio do parmetro.

    8. Ponteiros Para Arrays

    Ponteiros, obviamente, podem ser "apontados" para qualquer tipo de objeto de dado, incluindo arrays.Enquanto era evidente quando discutamos o programa 3.1, importante expandir sobre como fazer isto

    quando se trata de arrays multi-dimensionais.Para revisar, no Captulo 2 ns declaramos que dado um array de inteiros ns podemos apontar a umponteiro de inteiro daquele array usando:

    int *ptr;

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    22/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 22/39

    ptr = &my_array[0]; /* aponta nosso ponteiro para o primeiro inteiro em nosso array */

    Como declaramos l, o tipo da varivel ponteiro deve ser o mesmo tipo do primeiro elemento do array.

    Alm disto, podemos usar um ponteiro como parmetro formal de uma funo que projetada paramanipular um array, por exemplo.

    Dado: int array[3] = {'1', '5', '7'}/ void a_func(int *p);

    Alguns programadores podem preferir escrever o prottipo da funo como:

    void a_func(int p[]);

    o que informa a outros que podero usar esta funo que a mesma feita para tratar os elementos de umarray. Obviamente, em qualquer dos casos, o que realmente passado o valor de um ponteiro para oprimeiro elemento do array, independente de que notao usada no prottipo da funo ou definio.Note que se a notao de array usada, no h necessidade de passar a dimenso do array j que noestamos passando o array inteiro, apenas o endereo do primeiro elemento.

    Voltemos agora ao problema do array de 2 dimenses. Como foi dito no captulo anterior, o C interpretaum array bidimensional como um array de arrays de uma s dimenso. Sendo este o caso, o primeiroelemento de um array bidimensional de inteiros um array de uma dimenso de inteiros. E um ponteiropara um array bidimensional de inteiros deve ser um ponteiro para aquele tipo de dados. Uma forma deconseguir isto atravs do uso da palavra-chave "typedef". O typedefatribui um novo nome a umtipo de dados especificado. Por exemplo:

    typedef unsigned char byte;

    faz com que o nome bytesignifique o tipo unsigned char. Portanto

    byte b[10];

    ser um array de unsigned char.

    Note que na declarao typedef, a palavra bytefoi substituda pelo que normalmente seria o nome denosso unsigned char. Ou seja, a regra para usar typedef que o novo nome para o tipo de dados onome usado na definio do tipo de dado. Assim, em:

    typedef int Array[10];

    Array torna-se um tipo de dados para um array de 10 inteiros, isto , Array my_arrdeclara my_arrcomo sendo um array de 10 inteiros e Array arr2d[5]cria arr2dcomo um array de 5 arrays de 10inteiros cada.

    Note que Array *pldfaz de pldum ponteiro para um array de 10 inteiros. Como *pldaponta para omesmo tipo de arr2d, atribuir o endereo do array bidimensional arr2dpara **pld*, o ponteiro aoarray de uma dimenso de 10 inteiros, aceitvel. Isto , pld = &arr2d[0];ou pld = arr2d;estocorretos.

    Como o tipo de dados que usamos para nosso ponteiro um array de 10 inteiros, deveremos esperar queincrementar pldpor 1 ir mudar seu valor de 10*sizeof(int), que o que acontece. Ou seja, sizeof(*pld) 20. Voc pode provar isto para voc mesmo escrevendo e rodando um pequeno e simples programa.

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    23/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 23/39

    Agora, enquanto usar typedef torna as coisas mais claras para o leitor e mais fceis para o programador,isto realmente no necessrio. O que precisamos uma forma de declarar um ponteiro como pldsem anecessidade da palavra-chave typedef. Isto pode ser feito, e

    int (*p1d)[10];

    a declarao correta, isto , pldaqui um ponteiro para um array de 10 inteiros da mesma forma queele seria se fosse declarado usando o tipo Array. Note que isto diferente de

    int *p1d[10];

    que faria que pldfosse o nome de um array de 10 ponteiros para o tipo int.

    9. Ponteiros e Alocao Dinmica de Memria

    Existem vezes em que conveniente alocar memria durante a execuo do programa usando malloc(),calloc(), ou outras funes de alocao. Usar esta abordagem permite protelar a deciso sobre o

    tamanho do bloco de memria necessrio para armazenar um array, por exemplo, para o momento deexecuo do programa. Ou permite usar uma seo da memria para armazenar um array de inteiros emcerto momento no tempo, e quando aquela memria no mais necessria, ela pode ser liberada paraoutros usos, como o armazenamento de um array de estruturas.

    Quando a memria alocada, a funo de alocao (como malloc(), calloc(), etc.) retorna um ponteiro.O tipo deste ponteiro depende do tipo do compilador usado, se um velho compilador K&R ou umcompilador ANSI novo. Com o compilador antigo o tipo do ponteiro retornado char, com ocompilador ANSI void.

    Se voc est usando um compilador mais antigo, e quer alocar memria para um array de inteiros, voc

    ter que fazer um cast do ponteiro char retornado para um ponteiro inteiro. Por exemplo, para alocalespao para 10 inteiros, poderamos escrever:

    inf *iptr; iptr = (int *)malloc(10 * sizeof(int)); if (iptr == NULL)

    { ... Rotina de erro vem aqui ... }

    Se voc est usando um compilador ANSI, malloc()retorna um ponteiro voide como um ponteiro voidpode ser atribudo a uma varivel ponteiro de qualquer tipo, o cast (int *)mostrado acima no

    necessrio. A dimenso do array pode ser determinada durante a execuo e no necessria durante acompilao. Ou seja, o 10acima poderia ser uma varivel lida de um arquivo de dados ou teclado, oucalculada baseada em alguma necessidade, durante a execuo.

    Devido equivalncia entre a notao de array e ponteiros, uma vez que iptrtenha sido criado comomostrado acima, pode-se usar a notao de array. Por exemplo, pode-se escrever:

    int k; for (k = 0; k < 10; k++) iptr[k] = 2;

    para colocar em todos os elementos o valor 2.Mesmo com um entendimento razoavelmente bom de ponteiros e arrays, se existe um lugar em que onovato em C provavelmente ir tropear na primeira vez que trabalhar, a alocao dinmica de arraysmultidimensionais. Em geral, gostaramos de poder acessar elementos destes arrays usando notao dearray, e no notao de ponteiro, sempre que possvel. Dependendo da aplicao podemos ou no

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    24/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 24/39

    conhecer as duas dimenses durante a compilao. Isto nos leva a vrias formas de executar a tarefa.

    Como j vimos, quando alocando dinamicamente um array de uma dimenso, sua dimenso pode serdeterminada durante a execuo. Agora, quando usando alocao dinmica de arrays de ordem maior,ns no precisamos saber a primeira dimenso durante a compilao. Se precisamos ou no conhecer asdimenses maiores depende de como iremos escrever o cdigo. Aqui eu irei discutir vrios mtodospara alocar dinamicamente espao para arrays bidimensionais de inteiros.

    Primeiro, iremos considerar casos em que a segunda dimenso conhecida durante a compilao.

    Mtodo 1

    Uma forma de lidar com o problema atravs do uso da palavra chave typedef. Para alocar um arraybidimensional de inteiros, lembre que as duas notaes seguintes resultam na gerao do mesmo cdigoobjeto:

    multi[row][col] = 1; *(*(multi + row) + col) = 1;

    Tambm verdade que as duas notaes a seguir geram o mesmo cdigo:

    multi[row] *(multi + row)

    Como o da direita deve resultar em um ponteiro, a notao de array na esquerda tambm deve resultarem um ponteiro. De fato, multi[0]ir retornar um ponteiro para o primeiro inteiro na primeira linha,multi[1]um ponteiro para o primeiro inteiro na segunda linha, etc. Na verdade, multi[n]retorna umponteiro para o n-simo array deste array de arrays. Aqui a palavra ponteiroest sendo usada pararepresentar um valor de endereo. Apesar deste uso ser comum na literatura, quando estiver lendo estetipo de declarao deve-se ser cuidados para distinguir entre o endereo constante de um array e umavarivel ponteiro que um objeto de dados em si.

    Considere agora:

    Programa 9.1

    /* Program 9.1 from PTRTUT10.HTM 6/13/97 */

    #include #include

    #define COLS 5

    typedef int RowArray[COLS]; RowArray *rptr;

    int main(void) { int nrows = 10; int row, col; rptr = malloc(nrows * COLS * sizeof(int)); for (row = 0; row < nrows; row++) { for (col = 0; col < COLS; col++) {

    rptr[row][col] = 17; } }

    return 0; }

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    25/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 25/39

    Neste exemplo eu assumi que se estava usando um compilador ANSI, assim um cast no ponteiro voidretornado por malloc()no necessrio. Se voc estiver usando um compilador K&R antigo voc terde fazer o cast usando:

    rptr = (RowArray *)malloc(... etc.

    Usando esta abordagem, rptrtem todas as caractersticas de um nome de array (exceto que rptr modificvel), e a notao de array pode ser usada pelo resto do programa. Isto tambm significa que se

    voc pretende escrever uma funo para modificar o contedo do array, deve usar COLS como partedos parmetros formais da funo, como ns fizemos quando discutimos a passagem de arraysbidimensionais para uma funo.

    Mtodo 2

    No Mtodo 1 acima, rptr acabou sendo um ponteiro para o tipo "um array de uma dimenso de COLSinteiros". Acontece que existe uma sintaxe que pode ser usada para este tipo sem a necessidade dotypedef. Se escrevermos:

    int (*xptr)[COLS];

    a varivel xptrter todas as mesmas caractersticas da varivel rptrno Mtodo 1 acima, e noprecisamos usar a palavra-chave typedef. Aqui, xptr um ponteiro para um array de inteiros e otamanho do array dado pelo #define COLS. A colocao dos parntesis torna a notao de ponteirospredominante, mesmo que a notao de array tenha uma maior precedncia. Isto , se tivssemos escrito

    int *xptr[COLS];

    teramos definido xptrcomo um array de ponteiros mantendo um nmero de ponteiros igual ao definidopor COLS. No a mesma coisa, de forma nenhuma. Entretanto, arrays de ponteiros tem seu uso na

    alocao dinmica de arrays bidimensionais, como veremos nos dois mtodos a seguir.

    Mtodo 3

    Considere o caso onde no conhecemos o nmero de elementos de cada linha durante a compilao, isto, tanto o nmero de linhas e o nmero de colunas devem ser determinados durante a execuo. Umaforma de fazer isto seria criar um array de ponteiros do tipo inte ento alocar espao para cada linha eapontar cada um destes ponteiros a cada uma destas linhas. Considere o exemplo:

    Programa 9.2 /* Program 9.2 from PTRTUT10.HTM 6/13/97 */

    #include #include

    int main(void) { int nrows = 5; /* Both nrows and ncols could be evaluated */ int ncols = 10; /* or read in at run time */ int row; int **rowptr;

    rowptr = malloc(nrows * sizeof(int *)); if (rowptr == NULL) { puts("\nFailure to allocate room for row pointers.\n"); exit(0); }

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    26/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 26/39

    printf("\n\n\nIndex Pointer(hex) Pointer(dec) Diff.(dec)");

    for (row = 0; row < nrows; row++)

    { rowptr[row] = malloc(ncols * sizeof(int)); if (rowptr[row] == NULL) { printf("\nFailure to allocate for row[%d]\n",row);

    exit(0); } printf("\n%d %p %d", row, rowptr[row], rowptr[row]); if (row > 0) printf(" %d",(int)(rowptr[row] - rowptr[row-1])); }

    return 0; }

    No cdigo acima, rowptr um ponteiro a um ponteiro do tipo int. Neste caso ele aponta para o

    primeiro elemento de um array de ponteiros para o tipo int. Considere o nmero de chamadas amalloc():

    Para obter espao para o array de ponteiros 1 chamada Para obter espao para as linhas 5 chamadas ----- Total 6 chamadas

    Se voc escolher usar esta abordagem, note que mesmo que voc possa usar a notao de array paraacessar elementos individuais do array, como por exemplo, rowptr[row][col] = 17;, isto nosignifica que os dados no "array bidimensional" estejam contguos na memria.

    Voc~e pode, entretanto, usar notao de array como se ele fosse um bloco contnuo de memria. Porexemplo, voc pode escrever:

    rowptr[row][col] = 176;

    exatametne como se rowptr fosse o nome de um array bidimensional criado durante a compilao.Obviamente rowe coldevem estar nos limites do array que foi criado, exatamente como em um arraycriado durante a compilao.

    Se voc quer ter um bloco contguo de memria dedicado ao armazenamento de elementos no array

    voc pode fazer como no exemplo abaixo:

    Mtodo 4

    Neste mtodo ns alocamos um bloco de memria para guardar o array inteiro primeiro. Ento criamosum array de ponteiros para apontar para cada linha. Assim, mesmo que o array de ponteiros seja usado,o array em si est contguo na memria. O cdigo como segue:

    Programa 9.3

    /* Program 9.3 from PTRTUT10.HTM 6/13/97 */#include

    #include

    int main(void)

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    27/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 27/39

    { int **rptr; int *aptr; int *testptr; int k; int nrows = 5; /* Both nrows and ncols could be evaluated */ int ncols = 8; /* or read in at run time */ int row, col;

    /* we now allocate the memory for the array */aptr = malloc(nrows * ncols * sizeof(int));

    if (aptr == NULL) { puts("\nFailure to allocate room for the array"); exit(0); }

    /* next we allocate room for the pointers to the rows */

    rptr = malloc(nrows * sizeof(int *)); if (rptr == NULL)

    { puts("\nFailure to allocate room for pointers"); exit(0); }

    /* and now we 'point' the pointers */

    for (k = 0; k < nrows; k++) { rptr[k] = aptr + (k * ncols); }

    /* Now we illustrate how the row pointers are incremented */ printf("\n\nIllustrating how row pointers are incremented"); printf("\n\nIndex Pointer(hex) Diff.(dec)");

    for (row = 0; row < nrows; row++) { printf("\n%d %p", row, rptr[row]); if (row > 0) printf(" %d",(rptr[row] - rptr[row-1])); } printf("\n\nAnd now we print out the array\n"); for (row = 0; row < nrows; row++)

    { for (col = 0; col < ncols; col++) { rptr[row][col] = row + col; printf("%d ", rptr[row][col]); } putchar('\n'); }

    puts("\n");

    /* and here we illustrate that we are, in fact, dealing with a 2 dimensional array in a contiguous block of memory. */

    printf("And now we demonstrate that they are contiguous in memory\n");

    testptr = aptr; for (row = 0; row < nrows; row++) { for (col = 0; col < ncols; col++)

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    28/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 28/39

    { printf("%d ", *(testptr++)); } putchar('\n'); }

    return 0; }

    Considere, novamente, o nmero de chamadas a malloc() Para obter espao para o array de ponteiros 1 chamada Para obter espao para o array de ptrs 1 chamada ----- Total 2 chamadas

    Agora, cada chamada a malloc()cria um overhead adicional de espao j que malloc() geralmenteimplementado pelo sistema operacional formando uma lista ligada que contm dados sobre o tamanhodo bloco. Mas, mais importante, com grandes arrays (vrias centenas de linhas) manter o controle doque precisa ser liberado quando chega a hora pode ser problemtico. Isto, combinado com a

    contiguidade do bloco de dados que permite a inicializao de tudo para zero usando o memset(),parece que torna a segunda alternativa a preferida.

    Como um exemplo final de arrays multidimensionais iremos ilustrar a alocao dinmica de um array detrs dimenses. Este exemplo ir ilustrar uma coisa mais a ser verificada quando se faz este tipo dealocao. Por razes citadas acima, iremos utilizar a abordagem apresentada na segunda alternativa.Considere o seguinte cdigo:

    Programa 9.4

    /* Program 9.4 from PTRTUT10.HTM 6/13/97 */

    #include #include #include

    int X_DIM=16; int Y_DIM=5; int Z_DIM=3;

    int main(void) { char *space; char ***Arr3D; int y, z; ptrdiff_t diff;

    /* first we set aside space for the array itself */

    space = malloc(X_DIM * Y_DIM * Z_DIM * sizeof(char));

    /* next we allocate space of an array of pointers, each to eventually point to the first element of a 2 dimensional array of pointers to pointers */

    Arr3D = malloc(Z_DIM * sizeof(char **));

    /* and for each of these we assign a pointer to a newly

    allocated array of pointers to a row */

    for (z = 0; z < Z_DIM; z++) {

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    29/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 29/39

    Arr3D[z] = malloc(Y_DIM * sizeof(char *));

    /* and for each space in this array we put a pointer to the first element of each row in the array space originally allocated */

    for (y = 0; y < Y_DIM; y++) { Arr3D[z][y] = space + (z*(X_DIM * Y_DIM) + y*X_DIM);

    } }

    /* And, now we check each address in our 3D array to see if the indexing of the Arr3d pointer leads through in a continuous manner */

    for (z = 0; z < Z_DIM; z++) { printf("Location of array %d is %p\n", z, *Arr3D[z]); for ( y = 0; y < Y_DIM; y++) { printf(" Array %d and Row %d starts at %p", z, y, Arr3D[z][y]);

    diff = Arr3D[z][y] - space; printf(" diff = %d ",diff); printf(" z = %d y = %d\n", z, y); } } return 0; }

    Se voc seguiu este tutorial at este ponto, no deve ter problemas decifrando o cdigo acima com baseapenas nos comentrios. H alguns pontos que eu gostaria de destacar. Comecemos com a linha quetem:

    Arr3D[z][y] = space + (z*(X_DIM * Y_DIM) + y*X_DIM);

    Note que aqui space um ponteiro para caracter, que tem o mesmo tipo de Arr3D[z][y]. importanteque quando se est somando um inteiro, como o resultante da expresso (z*(X_DIM * Y_DIM) +y*X_DIM)a um ponteiro, o resultado um novo valor de ponteiro. E quando se est atribuindo valoersde ponteiro a variveis de ponteiro os tipos dos dados do valor e da varivel devem combinar.

    10. Ponteiros Para Funes

    At agora discutimos ponteiros para objetos de dados. C tambm permite a declarao de ponteiros parafunes. Ponteiros para funes tem uma variedade de usos e alguns deles sero discutidos aqui.

    Considere o seguinte problema real. Voc quer escrever uma funo que capaz de ordenarvirtualmente qualquer coleo de dados que podem ser armazenados em um array. Este pode ser umarray de strings, ou inteiros, ou floats, ou mesmo estruturas. O algoritmo de ordenamento pode ser omesmo para todos. Por exemplo, ele pode ser um simples algoritmo bubble sort, ou o algoritmo maiscomplexo shell sortou quick sort. Iremos utilizar um simples bubble sortpara nossa demonstrao.

    Sedgewick[1] descreveu o bubble sortusando cdigo C configurando uma funo que quando recebe

    um ponteiro para um array ir ordenar o mesmo. Se chamarmos esta funo bubble(), um programa deordenamento descrito por bubble_1.c, que segue:

    Programa bubble_1.c

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    30/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 30/39

    /*-------------------- bubble_1.c --------------------*/

    /* Program bubble_1.c from PTRTUT10.HTM 6/13/97 */

    #include

    int arr[10] = { 3,6,1,2,3,8,4,1,7,2};

    void bubble(int a[], int N);

    int main(void) { int i; putchar('\n'); for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } bubble(arr,10); putchar('\n');

    for (i = 0; i < 10; i++)

    { printf("%d ", arr[i]); } return 0; }

    void bubble(int a[], int N) { int i, j, t; for (i = N-1; i >= 0; i--) { for (j = 1; j a[j]) { t = a[j-1]; a[j-1] = a[j]; a[j] = t; } } } }

    /*---------------------- end bubble_1.c -----------------------*/

    O bubble sort um dos ordenamentos mais simples. O algoritmo examina o array do segundo at oltimo elemento, comparando cada elemento com o que o precede. Se um dos elementos que precede maior que o elemento atual, os dois so trocados de forma que o maior vai ficando prximo do fim doarray. No primeiro passo, isto resulta no maior elemento indo para o fim do array. O array agoralimitado a todos os elementos exceto o ltimo e o processo repetido. Isto coloca o prximo maiorelemento na posio anterior do maior elemento. O processo repetido por um nmero de vezes igualao nmero de elementos menos 1. O resultado final um array ordenado.

    Aqui nossas funes foram feitas para ordenar um array de inteiros. Assim, na linha 1 estamoscomparando inteiros e nas linhas 2 a 4 estamos usando um inteiro temporrio para guardar inteiros. O

    que queremos fazer agora ver se podemos converter este cdigo de forma que possamos usar qualquertipo de dado, isto , no ficarmos restritos a inteiros.

    Ao mesmo tempo no queremos ter que analisar nosso algoritmo e o cdigo associado com ele toda vezque usarmos o mesmo. Comeamos removendo a comparao de dentro da funo bubble()de forma a

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    31/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 31/39

    tornar relativamente fcil modificar a funo de comparao sem ter que reescrever pores relacionadasao algoritmo em sim. O resultado est em bubble_2.c:

    Programa bubble_2.c

    /*---------------------- bubble_2.c -------------------------*/

    /* Program bubble_2.c from PTRTUT10.HTM 6/13/97 */

    /* Separating the comparison function */

    #include

    int arr[10] = { 3,6,1,2,3,8,4,1,7,2};

    void bubble(int a[], int N); int compare(int m, int n);

    int main(void) { int i;

    putchar('\n'); for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } bubble(arr,10); putchar('\n');

    for (i = 0; i < 10; i++) { printf("%d ", arr[i]); }

    return 0; }

    void bubble(int a[], int N)

    { int i, j, t; for (i = N-1; i >= 0; i--) { for (j = 1; j n); }

    /*--------------------- end of bubble_2.c -----------------------*/

    Se nosso objetivo fazer nossa rotina de ordenamento independente do tipo de dados, uma forma defazer isto usar ponteiros para o tipo void para apontar para os dados em vez de usar o tipo inteiro.Como partida nesta direo, vamos modificar algumas coisas no programa acima de forma que ponteiros

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    32/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 32/39

    possam ser utilizados. Para comear, vamos ficar com ponteiros para o tipo inteiro.

    Programa bubble_3.c

    /*----------------------- bubble_3.c -------------------------*/

    /* Program bubble_3.c from PTRTUT10.HTM 6/13/97 */

    #include

    int arr[10] = { 3,6,1,2,3,8,4,1,7,2};

    void bubble(int *p, int N);

    int compare(int *m, int *n);

    int main(void) { int i; putchar('\n');

    for (i = 0; i < 10; i++)

    { printf("%d ", arr[i]); } bubble(arr,10); putchar('\n');

    for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }

    void bubble(int *p, int N) { int i, j, t; for (i = N-1; i >= 0; i--) { for (j = 1; j *n); }

    /*------------------ end of bubble3.c -------------------------*/

    Note as alteraes. Estamos agora passando um ponteiro para um inteiro (ou array de inteiros) parabubble(). E de dentro de bubble estamos passando ponteiros para os elementos do array que queremoscomparar para a nossa funo de comparao. E, obviamente, estamos dereferenciando estes ponteirosem nossa funo compare()para fazer a comparao em si. Nosso prximo passo ser converter osponteiros em bubble()para ponteiros do tipo void de forma que a funo fique mais insensvel ao tipo.

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    33/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 33/39

    Isto est mostrado no bubble_4.

    Programa bubble_4.c

    /*------------------ bubble_4.c ----------------------------*/

    /* Program bubble_4.c from PTRTUT10,HTM 6/13/97 */

    #include

    int arr[10] = { 3,6,1,2,3,8,4,1,7,2};

    void bubble(int *p, int N);

    int compare(void *m, void *n);

    int main(void) { int i; putchar('\n');

    for (i = 0; i < 10; i++)

    { printf("%d ", arr[i]); } bubble(arr,10); putchar('\n');

    for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }

    void bubble(int *p, int N) { int i, j, t; for (i = N-1; i >= 0; i--) { for (j = 1; j *n1); }

    /*------------------ end of bubble_4.c ---------------------*/

    Note que, ao fazer isto, em compare()temos que introduzir o casting do ponteiro tipo void para o tiporeal sendo ordenado. Mas, como veremos mais tarde, isto no problema. E como o que est sendopassado para bubble()ainda um ponteiro para um array de inteiros, temos que fazer o cast destes

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    34/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 34/39

    ponteiros para ponteiros para inteiros quando passamos os mesmos como parmetros em nossa chamadaa compare().

    Agora vamos tratar do prolema do que passamos para bubble(). Queremos que o primeiro parmetropara aquela funo seja tambm um ponteiro para void. Mas, isto significa que dentro de bubble()precisamos fazer algo sober a varivel t, que atualmente um inteiro. Alm disso, onde usamos t =p[j-1];o tipo de p[j-1]precisa ser conhecido para saber quantos bytes copiar para a varivel t(ou oque quer que coloquemos no lugar de t).

    Atualmente, no bubble_4.c, o conhecimento dentro de bubble()sobre o tipo do dado sendo ordenado(e, portanto, o tamanho de cada elemento individual) obtido do fato que o primeiro parmetro umponteiro para o tipo inteiro. Se queremos que bubble()ordente qualquer tipo de dado, precisamos fazerque aquele ponteiro aponte para o tipo void. Mas, ao fazer isto, iremos perder a informao sobre otamanho dos elementos individuais dentro do array. Assim, em bubble_5.c, iremos acrescentar umparmetro separado para tratar esta informao de tamanho.

    Estas alteraes, de bubble_4.c para bubble_5.c so, talvez, um pouco mais extensivas que as quefizemos nos exemplos anteriores. Assim, compare cuidadosamente os dois mdulos para ver as

    diferenas.Programa bubble_5.c

    /*---------------------- bubble5.c ---------------------------*/

    /* Program bubble_5.c from PTRTUT10.HTM 6/13/97 */

    #include #include

    long arr[10] = { 3,6,1,2,3,8,4,1,7,2};

    void bubble(void *p, size_t width, int N); int compare(void *m, void *n);

    int main(void) { int i; putchar('\n');

    for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } bubble(arr, sizeof(long), 10); putchar('\n');

    for (i = 0; i < 10; i++) { printf("%ld ", arr[i]); }

    return 0; }

    void bubble(void *p, size_t width, int N)

    { int i, j; unsigned char buf[4]; unsigned char *bp = p;

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    35/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 35/39

    for (i = N-1; i >= 0; i--)

    { for (j = 1; j *n1); }

    /*--------------------- end of bubble5.c ---------------------*/

    Note que eu mudei o tipo dos dados do array de intpara longpara ilustrar as mudanas necessrias nafuno compare(). Dentro de bubble()eu me descartei da varivel t(que teramos de ter mudado dotipo intpara o tipo long). Eu acrescentei um buffer de tamanho 4 de unsigned char, que o tamanhonecessrio para armazenar um long (isto ser novamente alterado em futuras modificaes a este

    cdigo). O ponteiro unsigned char *bp usado para apontar para a base do array a ser ordenado, isto ,o primeiro elemento daquele array.

    Temos tambm que modificar o que passado a compare(), e como faremos a troca dos elementos quea comparao indica precisarem ser trocados. O uso de memcpy()e a notao de ponteiros em vez danotao de array ajuda na reduo da sensibilidade ao tipo.

    Novamente, fazer uma comparao cuidadosa de bubble_5.c com bubble_4.c pode resultar em umentendimento maior sobre o que est acontecendo e por qu.

    Agora vamos para bubble_6.v, onde iremos usar a mesma funo bubble() que usamos em bubble_5

    para ordenar strings em vez de inteiros longos. Obviamente temos que alterar a funo de comparao jque a forma que strings so comparadas diferente da forma que inteiros longos so comparados. E embubble_6.c ns exclumos as linhas de bubble()que estavam comentadas em bubble_5.c

    Programa bubble_6.c

    /*--------------------- bubble6.c ---------------------*/ /* Program bubble_6.c from PTRTUT10.HTM 6/13/97 */

    #include #include

    #define MAX_BUF 256

    char arr2[5][20] = { "Mickey Mouse",

    "Donald Duck",

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    36/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 36/39

    "Minnie Mouse",

    "Goofy",

    "Ted Jensen" };

    void bubble(void *p, int width, int N);

    int compare(void *m, void *n);

    int main(void) { int i; putchar('\n');

    for (i = 0; i < 5; i++) { printf("%s\n", arr2[i]); } bubble(arr2, 20, 5); putchar('\n\n');

    for (i = 0; i < 5; i++) { printf("%s\n", arr2[i]); } return 0; }

    void bubble(void *p, int width, int N) { int i, j, k; unsigned char buf[MAX_BUF]; unsigned char *bp = p;

    for (i = N-1; i >= 0; i--)

    { for (j = 1; j 0) { memcpy(buf, bp + width*(j-1), width); memcpy(bp + width*(j-1), bp + j*width , width); memcpy(bp + j*width, buf, width); }

    } } }

    int compare(void *m, void *n) { char *m1 = m; char *n1 = n; return (strcmp(m1,n1)); }

    /*------------------- end of bubble6.c ---------------------*/

    Mas, o fato que bubble()tenha ficado inalterado daquilo que foi usado em bubble_5.c indica qeu afuno capaz de ordenar uma grande variedade de tipos de dados. O que falta fazer passar parabubble()o nome da funo de comparao que queremos utilizar, de forma que a funo seja realmenteuniversal. Da mesma forma que um array o endereo do primeiro elemento do array no segmento dedados, o nome de uma funo traduz-se para o endereo daquela funo no segmento de cdigo. Assim,

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    37/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 37/39

    precisamos usar um ponteiro para uma funo. Neste caso, a funo de comparao.

    Ponteiros para funes devem combinar com as funes apontadas no nmero e tipo dos parmetros e otipo do valor de retorno. Em nosso caso, declaramos nosso ponteiro para funo como:

    int (*fptr)(const void *p1, const void *p2);

    Noet que se tivssemos escrito:

    int *fptr(const void *p1, const void *p2);

    teramos feito o prottipo para uma funo que retorna um ponteiro do tipo int. Isto por que em C ooperador parntesis () tem uma precedncia maior que o operador * ponteiro. Colocando o parntesisem torno da string (*fptr) ns indicamos que estamos declarando um ponteiro para uma funo.

    Abora ns modificamos nossa declarao de bubble()pelo acrscimo, como seu quarto parmetro, umponteiro de funo do tipo apropriado. assim que o prottipo da funo fica:

    void bubble(void *p, int width, int N,

    int(*fptr)(const void *, const void *));

    Quando chamarmos bubble(), ns inserimos o nome da funo de comparao que queremos utilizar. Oprograma bubble_7.c ilustra como esta abordagem permite o uso da mesma funo bubble()paraordenar diferentes tipos de dados.

    Programa bubble_7.c

    /*------------------- bubble7.c ------------------*/

    /* Program bubble_7.c from PTRTUT10.HTM 6/10/97 */

    #include #include

    #define MAX_BUF 256

    long arr[10] = { 3,6,1,2,3,8,4,1,7,2}; char arr2[5][20] = { "Mickey Mouse", "Donald Duck", "Minnie Mouse", "Goofy", "Ted Jensen" };

    void bubble(void *p, int width, int N, int(*fptr)(const void *, const void *)); int compare_string(const void *m, const void *n); int compare_long(const void *m, const void *n);

    int main(void) { int i; puts("\nBefore Sorting:\n");

    for (i = 0; i < 10; i++) /* show the long ints */ { printf("%ld ",arr[i]); } puts("\n");

    for (i = 0; i < 5; i++) /* show the strings */ { printf("%s\n", arr2[i]);

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    38/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 38/39

    } bubble(arr, 4, 10, compare_long); /* sort the longs */ bubble(arr2, 20, 5, compare_string); /* sort the strings */ puts("\n\nAfter Sorting:\n");

    for (i = 0; i < 10; i++) /* show the sorted longs */ { printf("%d ",arr[i]); }

    puts("\n");for (i = 0; i < 5; i++) /* show the sorted strings */

    { printf("%s\n", arr2[i]); } return 0; }

    void bubble(void *p, int width, int N, int(*fptr)(const void *, const void *)) { int i, j, k;

    unsigned char buf[MAX_BUF]; unsigned char *bp = p;

    for (i = N-1; i >= 0; i--) { for (j = 1; j 0) { memcpy(buf, bp + width*(j-1), width); memcpy(bp + width*(j-1), bp + j*width , width);

    memcpy(bp + j*width, buf, width); } } } }

    int compare_string(const void *m, const void *n) { char *m1 = (char *)m; char *n1 = (char *)n; return (strcmp(m1,n1)); }

    int compare_long(const void *m, const void *n) { long *m1, *n1; m1 = (long *)m; n1 = (long *)n; return (*m1 > *n1); }

    /*----------------- end of bubble7.c -----------------*/

    Referncias[1] "Algorithms in C"Robert SedgewickAddison-Wesley

  • 5/20/2018 Tutorial de Ponteiros e Arrays Em C

    39/39

    3/7/2014 Tutorial de Ponteiros e Arrays em C

    http://cesarakg.freeshell.org/pointers.html 39/39

    ISBN 0-201-51425-7

    Eplogo

    Eu escrevi o material que o leitor tem em mos agora para dar uma introduo a ponteiros para novatosno C. Em C, quanto mais se entende sobre ponteiros, maior a flexibilidade que se tem no cdigo escrito.

    O texto acima foi expandido do meu primeiro trabalho neste tpico que recebeu o nome de ptr_help.txte era encontrado nas primeiras verses da coleo de cdigo C SNIPPETS de Bob Stout. O contedonesta verso foi atualizado a partir do cdigo em PTRTUTOT.ZIP includo em SNIP9510.ZIP.

    Eu estou sempre pronto a aceitar crticas construtivas sobre este material, ou solicitaes de reviso paraa adio de outro material relevante. Assim sendo, se voc tiver questes, comentrios, crticas, etc.sobre esta apresentao, eu apreciaria imensamente seu contato via email para minha conta [email protected].

    mailto:[email protected]