Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

yksmagalahaes

Programa de Hiperlinks

Recommended Posts

Tenho que criar um programa de hiperlinks. Por exemplo, cada nó da minha estrutura (lista duplamente encadeada) é uma página de internet. Cada pagina recebe um nome, e tem um link de avançar e um de voltar. A primeira pagina nao tem um de voltar e nem o de avançar, apenas o de criar uma nova pagina a partir do nome inserido pelo usuário. O meu problema é na hora de chamar a função que insere uma nova pagina na lista, quando digito o parametro (char nomePagina) ele buga --'

 

O código é esse:

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct pagina_da_lista
{
    struct pagina_da_lista *avancar;
    struct pagina_da_lista *voltar;
    char nomePagina;
}pagina;

typedef struct lista_paginas
{
    struct lista_paginas *inicio;
    struct lista_paginas *fim;
    int tamanho;
}listaPaginas;

void incializaNavegador(listaPaginas *L, pagina *P)
{
    P = NULL;
    L -> tamanho = 0;
}

int listaVazia (pagina *P)
{
    if (P == NULL)
    {
        return 1;
    }else {
        return 0;
    }
}

pagina *inserirNovaPagina (pagina *P, char nomepagina)
{
    pagina *novapagina = (pagina *)malloc(sizeof(pagina));
    printf("até qui ta bom");
    novapagina -> nomePagina = nomepagina;

    if (listaVazia(P))
    {
       novapagina -> avancar = NULL;
       novapagina -> voltar = NULL;
       P = novapagina;
    }
    else {
        novapagina -> voltar = P;
        novapagina -> avancar = NULL;
        P -> avancar = novapagina;
    }
    return novapagina;
}

void historico (listaPaginas *L)
{

}

void avancarPagina (pagina *P)
{
    if (P -> avancar != NULL)
    {
        printf("\t\t[AVANCAR]");
    }
    else {
        printf("\t\t\n[ABRIR PAGINA]: ");
    }
}

void voltarPagina (pagina *P)
{
    if (P -> voltar != NULL)
    {
        printf("\t\t\n[VOLTAR]");
    }
    else {
        printf ("\t\t\n[ABRIR PAGINA]: ");
    }
}

int main(void)
{

    int op, op_pagina;
    char nomePagina;
    pagina *P;
    listaPaginas *L;

        printf("\n\t\t=============== **** ===============\n");
        printf("\n\t\t 1 - Abrir Navegador??\n");
        printf("\t\t 0 - SAIR\n");
        printf("\t\t Qual Sua opcao: ");
        scanf("%d", &op);

    if (op == 1)
    {
        incializaNavegador(L, P);
    }
        else if (op == 0)
        {
            exit(0);
        }

    do
    {
        printf("\n\t\t=============== GUGLE CROME! ===============\n");
        printf("\n\t\t 1 - Abrir Pagina");
        printf("\n\t\t 0 - Sair do Navegador\n");
        printf("\t\t OPCAO: ");
        scanf("%d", &op_pagina);

        switch (op_pagina) {
        case 1:

            printf("\t\t\nNOME DA PAGINA: ");
            scanf("%s", nomePagina);
            inserirNovaPagina(P, nomePagina);
            printf("Voce esta em: %s", nomePagina);
            break;
        default:
            break;
        }

    }while (op_pagina != 0);

    return 0;
}

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

PONTEIROS:

 

Quando uma função recebe um atributo do tipo ponteiro, é feita uma cópia desse ponteiro (apenas do ponteiro, não do conteúdo).

Portanto, se você alterar a referência do ponteiro dentro da função e não retornar esta nova referência, o ponteiro original não será modificado.

Pra contornar esse tipo de situação, usamos ponteiro para ponteiro. Como o conteúdo referenciado pelo ponteiro, podendo ser outro ponteiro ou uma memória alocada, não é feita cópia, podemos assim, alcançar o valor real que precisamos.

Exemplo:

void incializaNavegador(listaPaginas *L, pagina **P)
{
    *P = NULL;
    L->tamanho = 0;
}

STRINGS:

 

Variável simples do tipo char, permite armazenar apenas um carácter. Para que possamos armazenar uma string, precisamos definir um vetor do tipo char e este terá o carácter terminador de string ('\0') em sua última posição do conteúdo armazenado.

Exemplo:

  char Palavra_1[30] = "Teste";
  char Palavra_2[30];

  strcpy(Palavra_2, Palavra_1);

As funções que manipulam strings, elas já fazem o tratamento do terminador de string. No exemplo acima, permitiu copiar o conteúdo da string "Palavra_1" para "Palavra_2".

Vale lembrar que, toda variável vetor de char, é por definição um ponteiro para estrutura do vetor.

 

 

ERROS ENCONTRADOS:

 

Função "main()":

"nomePagina" é uma simples variável char, logo armazena apenas 1 (um) carácter.

    char nomePagina;
    [...]
    scanf("%s", nomePagina);

Função "inserirNovaPagina()":

Essa atribuição, fará a cópia apenas de caracter e não de strings, como demonstrado no exemplo acima.

novapagina->nomePagina = nomepagina;

 

CONCLUSÃO:

 

Acredito que, corrigindo esses erros e ajustando o código, conforme demostrados acima, deverá resolver todos, ou quase todos os seus problemas no código.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não vou pegar no pé sobre todos os detalhes.

 

Mas um deles vale a pena corrigir: qualquer vetor, ou array, não é um ponteiro. O que ocorre é que o valor de uma expressão com tipo de vetor é um ponteiro para o primeiro elemento dele. O objeto em si é um vetor, ou array:

 

 

 

#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
    char a[30], *b;
    printf("\n\tArray de 30 (char): %zu "
           "\n\tPonteiro para (char): %zu\n\n",
           sizeof a, sizeof b);
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

A variável vetor de char, é sim um ponteiro. E que referencia para a primeira área de memória, quando alocado o conteúdo.

O conteúdo em sí, realmente não é um ponteiro. É uma área de memória contendo o conteúdo que precisamos.

void mostraPalavra( char *vetor )
{
    printf("String: %s\n", vetor);
    printf("Valor do ponteiro na funcao mostraPalavra: %p\n", vetor);
}

int main(void)
{
    char palavra[] = "Teste";

    mostraPalavra(palavra);
    printf("Valor do ponteiro na funcao Main: %p\n", palavra);

    return 0;
}

Como podemos ver, aloquei um vetor de char e passei a string pra função como ponteiro, sem usar o '&', portanto, naturalmente, ela é um ponteiro.

O resultado do código, vai mostrar que o valor dos ponteiros são os mesmos, ou seja, referenciam pra mesma área de memória. Caso a variável "palavra" não fosse um ponteiro, daria erro (ou lixo) no momento de mostrar o seu valor.

Em C, a variável vetor de qualquer tipo, sempre será um ponteiro.

Compartilhar este post


Link para o post
Compartilhar em outros sites

"O compilador então aloca este número de bytes em um espaço livre de memória. O nome da variável que você declarou é na verdade um ponteiro para o tipo da variável da matriz. Este conceito é fundamental. Eis porque: Tendo alocado na memória o espaço para a matriz, ele toma o nome da variável (que é um ponteiro) e aponta para o primeiro elemento da matriz."

Fonte: http://www.mtm.ufsc.br/~azeredo/cursoC/aulas/c630.html

 

"Em C, os elementos de um vetor são sempre guardados seqüencialmente, a uma distância fixa um do outro. Com isso, é possível facilmente passar de um elemento a outro, percorrendo sempre uma mesma distância para frente ou para trás na memória. Dessa maneira, podemos usar ponteiros e a aritmética de ponteiros para percorrer vetores. Na verdade, vetores são ponteiros ― um uso particular dos ponteiros."

Fonte: http://pt.wikibooks.org/wiki/Programar_em_C/Ponteiros

 

"Outra coisa: apesar de, na maioria dos casos, não fazer muito sentido, poderíamos ter índices negativos. Estaríamos pegando posições de memória antes do vetor. Isto explica também porque o C não verifica a validade dos índices. Ele não sabe o tamanho do vetor. Ele apenas aloca a memória, ajusta o ponteiro do nome do vetor para o início do mesmo e, quando você usa os índices, encontra os elementos requisitados."

Fonte: http://www.linguagemc.xpg.com.br/ponteiro2.html

 

"Em C, ponteiros e matrizes estão intimamente relacionados. Como você sabe, um nome de matriz sem um índice é um ponteiro para o primeiro elemento da matriz." (página 105)

Fonte: Livro C Completo e Total - 3a Edição - Autor: Herbert Schildt

 

Em que documentação diz que a variável vetor (veja vem, estou dizendo variável vetor, a que referencia para o vetor) não é ponteiro?

Compartilhar este post


Link para o post
Compartilhar em outros sites

bah, tudo questão de representação. Vetores existem , pelo comitê de padronização. Mas o compilador trata como ponteiros.

 

No final das contas o compilador vai transformar:

vetor[5] = 1; em *(vetor+5) = 1;

 

A grande sacada é não misturar conceito com prática, e também não ser totalmente xiita com um conceito ou com alguma definição ANSI, minha opinião particular é veja como um vetor, trate como um ponteiro.

Isso garante mais tranquilidade ainda mais na questão de passagem de parametros, retorno de função e etc.

Compartilhar este post


Link para o post
Compartilhar em outros sites

"O compilador então aloca este número de bytes em um espaço livre de memória. O nome da variável que você declarou é na verdade um ponteiro para o tipo da variável da matriz. Este conceito é fundamental. Eis porque: Tendo alocado na memória o espaço para a matriz, ele toma o nome da variável (que é um ponteiro) e aponta para o primeiro elemento da matriz."

Fonte: http://www.mtm.ufsc.br/~azeredo/cursoC/aulas/c630.html

 

"Em C, os elementos de um vetor são sempre guardados seqüencialmente, a uma distância fixa um do outro. Com isso, é possível facilmente passar de um elemento a outro, percorrendo sempre uma mesma distância para frente ou para trás na memória. Dessa maneira, podemos usar ponteiros e a aritmética de ponteiros para percorrer vetores. Na verdade, vetores são ponteiros ― um uso particular dos ponteiros."

Fonte: http://pt.wikibooks.org/wiki/Programar_em_C/Ponteiros

 

"Outra coisa: apesar de, na maioria dos casos, não fazer muito sentido, poderíamos ter índices negativos. Estaríamos pegando posições de memória antes do vetor. Isto explica também porque o C não verifica a validade dos índices. Ele não sabe o tamanho do vetor. Ele apenas aloca a memória, ajusta o ponteiro do nome do vetor para o início do mesmo e, quando você usa os índices, encontra os elementos requisitados."

Fonte: http://www.linguagemc.xpg.com.br/ponteiro2.html

 

"Em C, ponteiros e matrizes estão intimamente relacionados. Como você sabe, um nome de matriz sem um índice é um ponteiro para o primeiro elemento da matriz." (página 105)

Fonte: Livro C Completo e Total - 3a Edição - Autor: Herbert Schildt

 

Em que documentação diz que a variável vetor (veja vem, estou dizendo variável vetor, a que referencia para o vetor) não é ponteiro?

 

Todas estas referências estão erradas. O último autor é, inclusive, famoso por seu livro ser particularmente cheio de erros.

 

A wikipedia (tanto os artigos em português quanto em inglês) é, também, uma péssima fonte, justamente pela falta de material preciso e correto.

 

A fonte que deveria ser mais confiável é a da UFSC. Mas, infelizmente, também está cheia de erros. O pensamento confuso e impreciso do autor o leva a um erro fatal citado abaixo:

 

Vamos ver agora um dos usos mais importantes dos ponteiros: a varredura sequencial de uma matriz. Quando temos que varrer todos os elementos de uma matriz de uma forma sequencial, podemos usar um ponteiro, o qual vamos incrementando. Qual a vantagem? Considere o seguinte programa para zerar uma matriz:

 

 

int main (){	float matrx [50][50];	int i,j;	for (i=0;i<50;i++)        	for (j=0;j<50;j++)                	matrx[i][j]=0.0;	return(0);}

Podemos reescrevê-lo usando ponteiros:

 

int main (){	float matrx [50][50];	float *p;	int count;	p=matrx[0];	for (count=0;count<2500;count++)        {        	*p=0.0;        	p++;        }	return(0);}

No primeiro programa, cada vez que se faz matrx[j] o programa tem que calcular o deslocamento para dar ao ponteiro. Ou seja, o programa tem que calcular 2500 deslocamentos. No segundo programa o único cálculo que deve ser feito é o de um incremento de ponteiro. Fazer 2500 incrementos em um ponteiro é muito mais rápido que calcular 2500 deslocamentos completos.

 

Infelizmente, produzir um ponteiro que aponte para mais que um elemento depois do último de qualquer matriz gera comportamento indefinido, conforme descrito na especificação da linguagem, em 6.5.6p8:

 

(...) In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. (...)

 

O programa do autor, portanto, está errado. Sua afirmação sobre a performance está igualmente errada, dado que o aspecto de rapidez não é definido pela linguagem.

 

 

Você, e os autores desses textos, confundiram o tipo do objeto com o tipo do valor do objeto. Já disse e repito: o valor de um vetor é um ponteiro para seu primeiro elemento.
Outro erro comum de quem pensa dessa forma é ao tentar declarar um ponteiro para um vetor da seguinte maneira:
// Obs: código ERRADO.
 
int a[3];
int **p = &a; // tipos incompatíveis
Se o tipo de a fosse "ponteiro para int", então a inicialização de p com &a seria correta. Contudo, ela não é. O compilador pode ou não emitir um warning, mas um programa que use esta declaração para p gerará comportamento indefinido. O correto é fazer o seguinte:
int a[3];
int (*p)[3] = &a;

 

Aqui, declarei p como ponteiro para vetor de 3 ints, que tem tipo compatível com o de &a (são do mesmo tipo).

 

Pela última vez: a declaração de um vetor introduz um nome associado a um vetor.

 

Para aprender sobre estas declarações de uma fonte confiável, sugiro a seção 6.7.5.2 do n1256, draft standard da C99, ou 6.7.6.2 do n1570, draft standard da C11.

Compartilhar este post


Link para o post
Compartilhar em outros sites

caros...

 

Acho que houve aí uma clara confusão e mistura nos conceitos.

 

Um vetor não é um ponteiro, na verdade um vetor é o conjunto de dados do mesmo tipo armazenados na memória, que compartilham um mesmo identificador (nome da variácel), definição básica porém eficiente.

 

Já o identificador de um vetor (nome do vetor) é um texto que para compilador representa um endereço de memória. Este tal endereço de memória é onde o primeiro elemento do vetor está armazenado.

 

Conclusão: nomes de variáveis, nomes de vetores (e não vetores), etc, são endereços de memória (pra quem já estudou Assembly, as coisas são assim).

 

Então o colega guidjos tem toda a razão.

Sem mais.

 

Abraços à todos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entendi sobre o que o colega guidjos comentou. Pelo que vi, apenas ocorreu um equívoco na interpretação do que falei no primeiro post. Em nenhum momento falei que vetor é um ponteiro. Sempre falei que, variável vetor é um ponteiro (referência para memória).

 

Quando se especifica:

char a[3];

 

A memória alocada será estática, não permitindo sua modificação de tamanho, apenas de conteúdo, mas, o nome "a" armazena uma posição de memória, logo, faz referência para onde contem o vetor, realmente.

 

Quando se especifica:

char *a = malloc( sizeof(char) * 3 );

 

A memória alocada será dinâmica, permitindo que seja modificado o tamanho ou liberada, através da função "free()", porém, o nome "a" armazena uma posição de memória, logo, faz referência para onde contem o vetor, realmente.

 

Referente ao código comentado como "ERRADO", pelo colega guidjo, realmente está errado. Mas esse exemplo, estaria se referindo em um ponteiro para ponteiro ("**p = &a"), que não é o caso que comentei.

O seguinte código abaixo, está correto:

 

    int a[3];
    int *p = a;

O ponteiro "p" não contém a referência para "a", mas sim, referência para a memória apontada por "a".

 

Para comprovar que o código está de acordo com as normas, usei as seguintes flags na compilação: "-Wmain -pedantic-errors -pedantic -Wextra -Wall"

A flag "-pedantic" valida as normas ISO C e ISO C++, baseada no padrão C89 (ISO C90), que é mais restrito que C98/99, onde, não permite sequer comentários iniciados por "//", apenas "/* */".

No momento da compilação, não retornou nem mesmo um warning, comprovando que "a" é um ponteiro também.

 

Sobre o outro código apresentado:

int a[3];
int (*p)[3] = &a;

 

Estamos tratando de ponteiro para ponteiro, onde, a primeira referência indica o início da área de memória alocada e, a segunda referência, indica o deslocamento a partir do início da memória. O deslocamento será feito a partir do tamanho de cada tipo especificado.

 

Nome que armazena posição de memória, é chamado de ponteiro, correto?

 

Não vejo onde minha explicação pode estar errada.

 

OBS: Estou gostando desse tópico. Está me fazendo aprofundar mais ainda nas definições do código C.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Caro screenblack (que coincidência mano :)), aprecio muito tópicos como esses, à tanto que sentia a falta de um debate (e não embate) saudável assim. Apesar de todas concordâncias e desconcordâncias, cada elemento acabará por ser beneficiado por mais conhecimentos.

 

A primeira estrela que dou à esse fórum, parabéns.

 

Abraços.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Caros,

 

É importante diferenciar o conceito de "ponteiro" da ligação entre nomes e objetos/funções.

 

Compiladores usam sim algo chamado de "symbol table", uma entidade abstrata onde mantém estas ligações e que usam para fazer a resolução de cada nome em cada expressão. Mas isto são detalhes de implementação e não pertencem à linguagem.

 

Outra coisa importante a entender é que não existe "assembly rodando por trás de C". Programas escritos na linguagem podem ser compilados para português, inglês, C++, JavaScript, etc. Um compilador nada mais é que um tradutor. O que ocorre ao gerarmos um "executável" é que o programa em C é traduzido para um programa em código "nativo" (que a arquitetura em questão entende e pode executar). É irrelevante o fato de assembly (ou qualquer outra linguagem) ter sido usada como intermediária para otimização.

 

Sugiro o "dragon book" ("Compilers: Principles, Techniques, and Tools") como referência para técnicas de construção de compiladores, onde estes assuntos são abordados com (muito) mais detalhe. É um clássico da computação e obrigatório para quem quer entender este aspecto dela.

 

A linguagem C é uma máquina abstrata. Programas compilados são traduções de execuções desta máquina para outras linguagens, mas mantendo a semântica por ela definida. Portanto tentar aprender C tentando fingir que há alguma outra coisa acontecendo é infrutífero, além de, às vezes, perigoso. Exemplo:

 

 

// ...
int a = 10, b = 20;
printf("%d %d", a, *(&a + 1));
// ...

 

Quem tentar explicar C através de assembly se sentirá tentado a entender o espaço de memória de um programa como um grande "vetor" em que posições são alocadas conforme objetos são criados. Esta noção é falsa, e dizer que o programa imprimirá "10 20" é errado. O que ocorre é comportamento indefinido, pois não necessariamente a e b residem em posições subsequentes de memória. Mesmo que seja este o caso, qualquer coisa pode acontecer ao tentar usar * sobre &a + 1 (sim, ainda que haja um objeto válido ali). Isto faz parte da semântica da linguagem, e ponto final. Pouco importa se uma ou outra implementação geram consistentemente código nativo que garante a execução que imprime "10 20". Ainda que todos as versões de compiladores do mundo gerassem código equivalente, o programa seria inválido, pois na máquina abstrata definida na linguagem C determina que há comportamento indefinido. Uma implementação futura não precisa obedecer este padrão.

 

De volta ao assunto original, é preciso diferenciar arrays de ponteiros. Quando um array é operando de * ou + (equivalentemente, de []), ele é avaliado (seu valor é produzido), e seu valor é um ponteiro. O mesmo ocorre em chamadas a funções, em que cada argumento tem seu valor passado na invocação. Mesmo no caso de ponteiros, como em foo(&a);, o valor da expressão &a é passado como argumento. Não vou entrar em detalhes maiores, pois fugiria muito deste tópico, mas (surpresa...) em C não existe passagem por referência.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Sei que o valor de qualquer elemento, passado pra outra função, é feita por argumento.

Tanto que, no meu post anterior (#5), falei que os valores dos ponteiros eram iguais, mas não falei que eram os mesmos ponteiros.

 

Obrigado pela indicação do livro.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.