Dúvida Básica sobre ponteiro! [RESOLVIDO]

1. Dúvida Básica sobre ponteiro! [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 28/01/2022 - 16:13h

Boa Tarde a Todos

Minha dificuldade com ponteiros é porque só encontro exemplos de int nunca tem exemplos de array de char!

1) Entendi que um ponteiro, representa um endereço de memória. Todas as variáveis têm um endereço na memória.

2) Entendi que o asterisco do ponteiro indica que a variável irá armazenar endereço e não dado.
? char *Nome = "Viva o Linux"; logo entendo que Nome está armazenando o endereço de memória onde está Viva o Linux

3) Entendi que nos processadores 64 bits um ponteiro ocupa 8 bytes.
? No Caso acima Viva o Linux tem mais de 8 Bytes, logo entendi que 8 Bytes se refere a variável ponteiro e não aos dados armazenados pela variável

4) Entendi que um ponteiro é uma variável! Que contém o endereço de um objecto de dados, em geral, outra variável. Essa é a razão para o seu nome (ponteiro): ele aponta para outra variável.

5) Entendi que um ponteiro nada mais é que um atalho a um endereço na memória

6) Quando definimos um ponteiro char *Nome = "Viva o Linux";, estamos alocando o espaço para o endereço de memória apontado e não para o valor.
? Então onde está o espaço para o valor? Ou essa alocação está abrangendo tudo? Essa alocação é dinâmica?

X) Não entendi quando declaro char *Texto;
1) Qual o tamanho máximo de caracteres que posso atribuir a ele após a declaração? Isso é infinito? Ou tem um limite?
2) Imagino que se eu enviar por exemplo pra ele depois: Viva o Linux e então depois enviar Viva O Linux no Brasil que é um texto maior vou estourar a variável, pois parece que ele fica no tamanho do primeiro texto atribuído a ele, é isso?



  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 29/01/2022 - 22:16h

ApprenticeX escreveu:

Boa Tarde a Todos

Minha dificuldade com ponteiros é porque só encontro exemplos de int nunca tem exemplos de array de char!

1) Entendi que um ponteiro, representa um endereço de memória. Todas as variáveis têm um endereço na memória.


OK.

2) Entendi que o asterisco do ponteiro indica que a variável irá armazenar endereço e não dado.
char *Nome = "Viva o Linux"; logo entendo que Nome está armazenando o endereço de memória onde está Viva o Linux


OK.

Lembre-se, porém, que texto entre aspas produz uma string constante (cujo tipo é array de caracteres com tantos elementos quantos contidos entre as aspas mais um elemento para um byte nulo, após o último elemento explícito). O C permite que você use um ponteiro para caracteres não-constantes para apontar para essa área de dados constantes, mas isso é uma anomalia no sistema de tipos, que existe em função de compatibilidade com código obsoleto, que existia antes do padrão do C que introduziu a palavra-chave “const”. O C++, por exemplo, não permite uma declaração como a que você fez, e mesmo em C seria melhor que você tivesse qualificado o dado apontado como constante, como mostrado abaixo.
const char *Nome="Viva o Linux"; 


3) Entendi que nos processadores 64 bits um ponteiro ocupa 8 bytes.
• No Caso acima Viva o Linux tem mais de 8 Bytes, logo entendi que 8 Bytes se refere a variável ponteiro e não aos dados armazenados pela variável


OK.

4) Entendi que um ponteiro é uma variável! Que contém o endereço de um objecto de dados, em geral, outra variável. Essa é a razão para o seu nome (ponteiro): ele aponta para outra variável.


Errado.

Alguns fatos sobre ponteiros:

  • Um ponteiro é um valor que representa, simultaneamente, um endereço de memória e o tipo de dado contido nesse endereço.

  • Você pode ter variáveis destinadas a armazenar valores que sejam ponteiros. Por exemplo:
int *pi;  // Um ponteiro para dado cujo tipo é int (abreviadamente: ponteiro para int).
char *pc; // Um ponteiro para dado cujo tipo é char (abreviadamente: ponteiro para char).
const char *my_argv[25]; // Um array com 25 elementos que são ponteiros para dados que são do tipo caráter constante (abreviadamente: array com 25 ponteiros para const char).
double (*pv)[3]; // Um ponteiro para dado que é tipo array com 3 elementos do tipo double (abreviadamente: ponteiro para array (com 3 elementos) de double).


  • Você pode obter valores que são ponteiros sem ter uma variável de tipo ponteiro associada. Eis alguns exemplos.
&c  /* Se c tiver sido declarado com o tipo char, produz um ponteiro com o endereço de c e o tipo de dado associado char. */
&i /* Se i tiver sido declarado com o tipo int, produz um ponteiro com o endereço de i e o tipo de dado associado int. */
some_array /* Se some_array tiver sido declarado como array para elementos de um determinado tipo, produz o endereço do primeiro elemento e o tipo de dado associado é o mesmo tipo de cada elemento do array. */
(long *)12345678900 /* Ponteiro para dado do tipo long int residente no endereço de memória 12345678900. */
NULL /* Essa constante geralmente é definida em C (em <stddef.h>) como “(void *)0” (“void *” é algo como “ponteiro para qualquer coisa”: você terá de converter para outra coisa antes de poder usar; também pode ser considerado “ponteiro para dado de tipo indefinido”). */
(void *)addr /* Se addr tiver sido declarado com um tipo de dados inteiro, converte esse valor para um endereço para dado de tipo indefinido. */
sbrk(0) /* Chama uma função (declarada em sistemas UNIX-like em <unistd.h>) que aloca memória e retorna o ponteiro para o fim da região de memória alocada (no caso, aloca 0 bytes, retornando apenas o fim da área de dados). O tipo do dado retornado é void *. */


  • O valor do ponteiro não necessariamente indica uma posição de memória válida para o programa. O ponteiro nulo, por exemplo (NULL em C, nullptr em C++) é um endereço inválido, e é muitas vezs útil para indicar que um dado não está disponível ou ainda não foi definido. Outras conversões de valores inteiros para ponteiros podem produzir valores que não correspondem a endereços válidos na memória do programa, e isso não necessariamente será um problema, desde que o programa não tente usar tais valores para obter algum dado guardado num tal endereço inválido.

5) Entendi que um ponteiro nada mais é que um atalho a um endereço na memória


Ponteiros funcionam como referências ou indireções, isto é, formas indiretas de chegar a um dado ou de referir-se a eles.

Como acessos indiretos frequentemente são mais lentos do que acessos diretos, provavelmente não convém chamar de atalho.

6) Quando definimos um ponteiro char *Nome = "Viva o Linux";, estamos alocando o espaço para o endereço de memória apontado e não para o valor.
• Então onde está o espaço para o valor? Ou essa alocação está abrangendo tudo? Essa alocação é dinâmica?


De novo, note que o tipo de Nome, no caso acima, deveria mais propriamente ser const char *.

Como já dito acima (e em outros tópicos, no passado), uma constante literal de string entre aspas provoca a criação de um array de caracteres na região de dados constantes do programa, com espeaço suficiente para armazenar todos os caracteres entre as aspas e mais um byte nulo, e dispõe esses caracteres e o byte nulo nessa região criada. Quando você declara o ponteiro e diz que ele “recebe a string”, na verdade está fazendo com ele receba o endereço do primeiro caráter dessa região de dados constantes que foi reservada para a constante literal string (e por isso a ênfase em que o tipo de dados apontado deveria receber o qualificador const).

Quanto ao tipo de alocação, não há alocação dinâmica de modo nenhum: tanto a constante literal string quanto o ponteiro têm sua alocação é providenciada durante a compilação.

X) Não entendi quando declaro char *Texto;
1) Qual o tamanho máximo de caracteres que posso atribuir a ele após a declaração? Isso é infinito? Ou tem um limite?


Você não pode atribuir caracteres diretamente a uma variável do tipo ponteiro. O que você pode fazer é fazer com que ele aponte para uma região contendo (ou que passará a conter) caracteres, e então usar o endereço nele armazenado para chegar indiretamente a essa região de memória.

2) Imagino que se eu enviar por exemplo pra ele depois: Viva o Linux e então depois enviar Viva O Linux no Brasil que é um texto maior vou estourar a variável, pois parece que ele fica no tamanho do primeiro texto atribuído a ele, é isso?


Guarde que o ponteiro é uma forma indireta de chegar a um dado. Tendo isso em mente, veja os seguintes meios de tentar fazer essa operação que você descreveu.
// Caso 1: sucessão de valores independentes no tempo: OK.
int main(void){
const char *Nome;
Nome="Viva o Linux"; // Aponta para uma constante literal string.
/* ... */
Nome="Viva o Linux no Brasil"; // Aponta para outra constante literal string em outro momento, posterior.
/* ... */
}
// Caso 2: alteração de conteúdo de constante literal string: ERRO JÁ EM TEMPO DE COMPILAÇÃO.
int main(void){
const char *Nome; // O fato de ter o const já deve servir de alerta...
Nome="Viva o Linux"; // Aponta para uma constante literal string.
strcat(Nome, " no Brasil"); // O compilador vai dar erro, porque o primeiro argumento não pode ser do tipo const char *.
}
// Caso 3: abusando da anomalia no sistema de tipos: ERRO EM TEMPO DE EXECUÇÃO.
int main(void){
char *Nome;
Nome="Viva o Linux"; // Aponta para uma constante literal string usando um ponteiro para dados não-constantes: PROBLEMA, mas permitido em C (inválido em C++).
strcat(Nome, " no Brasil"); // PROBLEMA: o compilador vai deixar passar, porque os tipos dos argumentos são compatíveis, mas o primeiro argumento aponta para memória que não pode ser modificada; o programa provavelmente vai capotar com SIGSEGV.
}
// Caso 4: Usando vetor de caracteres com tamanho suficiente em vez de constante literal para a string: OK.
int main(void){
char Nome[40]="Viva o Linux"; // Coloca a string dentro de um array, que reside em região de memória que pode ser alterada.
strcat(Nome, " no Brasil"); // Lembrando que o nome de um array sozinho numa expressão se torna um ponteiro para o primeiro elemento, o array apontado tem espaço de sobra para acomodar os caracteres adicionais.
}
// Caso 5: Usando vetor de caracteres com tamanho limitado em vez de constante literal para a string: ERRO EM TEMPO DE EXECUÇÃO.
int main(void){
char Nome[]="Viva o Linux"; // Coloca a string dentro de um array, que reside em região de memória que pode ser alterada, mas que tem o espaço limitado apenas ao conteúdo original.
strcat(Nome, " no Brasil"); // O compilador deixa passar porque os tipos dos argumentos estão certos, mas a função strcat() não considera o tamanho reservado para o destino, então a concatenação é feita em uma memória que já não pertence ao array, com resultado imprevisível (mas seguramente errôneo!).
}



... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)

3. Re: Dúvida Básica sobre ponteiro! [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 30/01/2022 - 00:28h

paulo1205 escreveu: seria melhor que você tivesse qualificado o dado apontado como constante, como mostrado abaixo.
const char *Nome="Viva o Linux"; 
Compreendi!
paulo1205 escreveu: Como já dito acima (e em outros tópicos, no passado), uma constante literal de string entre aspas provoca a criação de um array de caracteres na região de dados constantes do programa, com espeaço suficiente para armazenar todos os caracteres entre as aspas e mais um byte nulo, e dispõe esses caracteres e o byte nulo nessa região criada. Quando você declara o ponteiro e diz que ele “recebe a string”, na verdade está fazendo com ele receba o endereço do primeiro caráter dessa região de dados constantes que foi reservada para a constante literal string (e por isso a ênfase em que o tipo de dados apontado deveria receber o qualificador const).

Quanto ao tipo de alocação, não há alocação dinâmica de modo nenhum: tanto a constante literal string quanto o ponteiro têm sua alocação é providenciada durante a compilação.

Você não pode atribuir caracteres diretamente a uma variável do tipo ponteiro. O que você pode fazer é fazer com que ele aponte para uma região contendo (ou que passará a conter) caracteres, e então usar o endereço nele armazenado para chegar indiretamente a essa região de memória.

Guarde que o ponteiro é uma forma indireta de chegar a um dado. Tendo isso em mente, veja os seguintes meios de tentar fazer essa operação que você descreveu.
// Caso 1: sucessão de valores independentes no tempo: OK.
int main(void){
const char *Nome;
Nome="Viva o Linux"; // Aponta para uma constante literal string.
/* ... */
Nome="Viva o Linux no Brasil"; // Aponta para outra constante literal string em outro momento, posterior.
/* ... */
}

Até aqui entendi então que quando eu atribuo uma string const literal, ao meu ponteiro, o sistema está colocando ela em um endereço de memória, cujo o qual eu não terei controle se eu desassociar meu ponteiro! Essa string por exemplo ela é liberada da memória qdo aponto meu Ponteiro pra outra string?
const char *text;
text = "1";
text = "2345"; // Neste caso o 1 acima, foi liberado da memória? Ou continua ocupando espaço? | Entendi que aqui 2345 está alocado corretamente com seu espaço necessário em um NOVO endereço de memória certo?

char Text[] = "Linux-Text"; // Então char Text[] seu valor é imutável usando um ponteiro certo? Visto que ponteiros não modificam valores, apenas apontam para valores existentes ou novos valores certo?
const char *Pointer = Text; // Entendi que Aponto Pointer para Text, assim Pointer passa a informar o valor de Text
Pointer = "Amor"; // Achei que mudaria o valor de Text! Mas pelas explicações acima, entendi que "Amor" foi alocado em um novo espaço de memória! Certo?

Este programa abaixo, o resultado dele está correto? Foi uma forma de entender pra onde o ponteiro está apontando
Modifiquei para meu entendimento deste site: https://cboard.cprogramming.com/c-programming/127932-get-current-position-pointer.html
#include <stdio.h>  // printf
#include <stdlib.h> // free

int main(void) {
const char *TextPtr = "Linux-Ptr";
const char *TextPtr2 = TextPtr;

// Esse Resultado está correto?
for( ; *TextPtr2 != '\0'; TextPtr2++)
printf("%p TextPtr[%ld] %c\toffset of TextPtr2[%ld] %p\t(TextPtr2 - TextPtr) = %ld\n", TextPtr, TextPtr2 - TextPtr, *TextPtr2, TextPtr2 - TextPtr, TextPtr2, TextPtr2 - TextPtr);

// Porque não funciona free pra esses Ponteiros?
// Caso não coloque como const: error: attempt to free a non-heap object
//free(TextPtr); // error: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
//free(TextPtr2);
return 0;
}
/* RESULT
0x402004 TextPtr[0] L offset of TextPtr2[0] 0x402004 (TextPtr2 - TextPtr) = 0
0x402004 TextPtr[1] i offset of TextPtr2[1] 0x402005 (TextPtr2 - TextPtr) = 1
0x402004 TextPtr[2] n offset of TextPtr2[2] 0x402006 (TextPtr2 - TextPtr) = 2
0x402004 TextPtr[3] u offset of TextPtr2[3] 0x402007 (TextPtr2 - TextPtr) = 3
0x402004 TextPtr[4] x offset of TextPtr2[4] 0x402008 (TextPtr2 - TextPtr) = 4
0x402004 TextPtr[5] - offset of TextPtr2[5] 0x402009 (TextPtr2 - TextPtr) = 5
0x402004 TextPtr[6] P offset of TextPtr2[6] 0x40200a (TextPtr2 - TextPtr) = 6
0x402004 TextPtr[7] t offset of TextPtr2[7] 0x40200b (TextPtr2 - TextPtr) = 7
0x402004 TextPtr[8] r offset of TextPtr2[8] 0x40200c (TextPtr2 - TextPtr) = 8
*/





4. Re: Dúvida Básica sobre ponteiro! [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 30/01/2022 - 01:09h

Um outro ponto que gostaria de entender é: Se eu posso então usar um ponteiro pra receber valores que não faço idéia do tamanho, tipo um texto inteiro de um arquivo que não sei qts caracteres terá! Uma idéia Ex abaixo!
const char *Ptr = "Mini Text";
Ptr = "Texto muito Maior que o Mini Text";
Ptr = "Imagine aqui um Texto com 100.000 Caracteres";
Ptr = "Imagine aqui um Texto com 500.000 Caracteres";
Ptr = NULL; // Meu Computador liberou da memória os 500.000 Caracteres?
free(Ptr); // Meu Computador liberou da memória os 500.000 Caracteres?

Ou o que aconteceu acima, foi que ficou na memória todos os textos declarados que esse ponteiro usou?


5. Re: Dúvida Básica sobre ponteiro!

Paulo
paulo1205

(usa Ubuntu)

Enviado em 30/01/2022 - 22:01h

ApprenticeX escreveu:

Até aqui entendi então que quando eu atribuo uma string const literal, ao meu ponteiro, o sistema está colocando ela em um endereço de memória, cujo o qual eu não terei controle se eu desassociar meu ponteiro! Essa string por exemplo ela é liberada da memória qdo aponto meu Ponteiro pra outra string?
const char *text;
text = "1";
text = "2345"; // Neste caso o 1 acima, foi liberado da memória? Ou continua ocupando espaço? | Entendi que aqui 2345 está alocado corretamente com seu espaço necessário em um NOVO endereço de memória certo?


Tipicamente, quando dados são dispostos numa região de memória para dados constantes, isso não significa apenas que você não pode alterar o seu valor, mas também que esses valores permanecem nessa região de dados ao longo de toda a duração do programa. Apesar de não ser impossível imaginar, não conheço uma implementação que tenha outro comportamento.

Lembre, porém, que variáveis que guardam ponteiros não têm necessariamente nenhum compromisso com o dado apontado. É esperado pelo compilador que elas possam se referir a diferentes dados em diferentes momentos do tempo, de modo que a simples dissociação entre a variável ponteiro de um determinado conteúdo ou sua associação a outro conteúdo não significa que o tal conteúdo pode ser liberado. Esse comportamento de liberar (ou de marcar para uma eventual liberação no futuro) de conteúdo que deixa de ser referenciado pelo programa é comum em Java, C# e outras linguagens que trabalham com garbage collection para simular memória infinita, mas ele implica ter dentro da região de dados apontada um contador de quantas referências ativas existem para aquela região, e tal comportamento não é prescrito para ponteiros em C nem em C++.


char Text[] = "Linux-Text"; // Então char Text[] seu valor é imutável usando um ponteiro certo? Visto que ponteiros não modificam valores, apenas apontam para valores existentes ou novos valores certo?


Não entendi a dúvida. Vamos ver se a explicação genérica clareia seu entendimento.

Pela declaração acima, o identificador Text é um array com onze elementos do tipo char (onze porque o conteúdo entre aspas tem dez caracteres, e mais um elemento para conter um byte nulo que funciona como terminador da string).

Por razões sintáticas, após encerramento da declaração, o identificador Text não pode ser alterado para referir-se a nenhum outro objeto diferente do array ao qual ele foi ligado no momento de sua declaração. Se você tentar fazer qualquer operação semelhante a “Text= … ;”, vai receber erro de compilação.

O array criado durante a declaração foi para acomodar os caracteres da string (mais o byte nulo), e os caracteres da tal string são colocados como conteúdo inicial dos respectivos elementos do array. Entretanto, esses elementos não foram declarados como constantes, então tais elementos podem ser modificados (incluindo o último elemento, que inicialmente continha o byte nulo).

Se você quisesse que o array representado por Text fosse completamente imutável (isto é: seus elementos não podendo ser alterados após a atribuição dos valores iniciais), deveria ter declarador seus elementos como const char, em lugar de apenas char.
const char Text[]="Texto-fixo"; 


const char *Pointer = Text; // Entendi que Aponto Pointer para Text, assim Pointer passa a informar o valor de Text 


Literalmente falando, você copia o valor da expressão “Text” (o identificador do array), que é calculada e produz como resultado um valor que é um ponteiro para o primeiro elemento do array, e tal valor é atribuído como valor inicial da variável Pointer, que você está declarando como ponteiro para dado do tipo const char.

Note que não há problema em ter um ponteiro para dados constantes apontando para dados que não foram declarados originalmente como constantes. O que é problemático é o contrário, ou seja, um ponteiro para dados não-constantes sendo usado para referir-se a dados que são constantes.

Pointer = "Amor"; // Achei que mudaria o valor de Text! Mas pelas explicações acima, entendi que "Amor" foi alocado em um novo espaço de memória! Certo? 


Sim (porém, em lugar de “novo”, eu diria “outro” ou “distinto“; além disso, "Amor" designa uma string constante (ou, mais propriamente, um array com elementos constantes formado a partir de uma string), ao passo que string indicada por Text não era constante (ou, mais especificamente, os elementos do array que continha a string não eram constantes)).

Este programa abaixo, o resultado dele está correto? Foi uma forma de entender pra onde o ponteiro está apontando
Modifiquei para meu entendimento deste site: https://cboard.cprogramming.com/c-programming/127932-get-current-position-pointer.html
#include <stdio.h>  // printf
#include <stdlib.h> // free

int main(void) {
const char *TextPtr = "Linux-Ptr";
const char *TextPtr2 = TextPtr;

// Esse Resultado está correto?
for( ; *TextPtr2 != '\0'; TextPtr2++)
printf("%p TextPtr[%ld] %c\toffset of TextPtr2[%ld] %p\t(TextPtr2 - TextPtr) = %ld\n", TextPtr, TextPtr2 - TextPtr, *TextPtr2, TextPtr2 - TextPtr, TextPtr2, TextPtr2 - TextPtr);


A conversão correspondente aos argumentos “TextPtr2-TextPtr” (que ocorre três vezes — um tanto redundante, não?) deveria ser "%td", uma vez que a diferença entre ponteiros é do tipo ptrdiff_t, que não necessariamente será equivalente ao tipo long int, que é o tipo de conversão correspondente a "%ld" (funciona nas nossas máquinas com Linux de 64 bits e que têm tanto long int ponteiros de 64 bits, mas possivelmente daria erro em um compilador para WIndows, mesmo de 64 bits, no qual long int tem apenas 32 bits (cf. https://www.ibm.com/docs/en/ibm-mq/9.0?topic=platforms-standard-data-types-unix-linux-windows)).

Além disso, a mensagem fala em “offset of TextPtr2[n] endereço”, mas não é isso o que você está imprimindo: o endereço impresso é sempre o valor de TextPtr2 (que vai sendo alterado ao longo das iterações do laço de repetição). Acaba ficando uma coisa confusa entre endereço e deslocamento (offset), e entre TextPtr e TextPtr2.

Para tentar esclarecer, veja se a correspondência abaixo o ajuda a compreender o que cada coisa representa a cada iteração do laço de repetição:
Expressão            Informação                                    Conversão em printf
--------------------------------------------------------------------------------------
TextPtr Endereço inicial da string %p

TextPtr2 Endereço do ponteiro que percorre a string %p

*TextPtr2 Conteúdo do ponteiro que percorre a string %c

TextPtr2-TextPtr Deslocamento em relação ao início da string %td

TextPtr[ Valor do elemento correspondente ao deslo- %c
TextPtr2-TextPtr camento em relação ao início da string (=
] ao conteúdo do ponteiro que a percorre)



// Porque não funciona free pra esses Ponteiros?


Porque free() só pode ser usada para liberar memória que tenha sido alocada com malloc()/realloc()/calloc() (alocação dinâmica, que, em C, é feita de forma manual). Essas funções de alocação não dependem da compilação, mas sim de um recurso obtido, em tempo de execução, do sistema operacional ou ambiente de execução. As variáveis que você declarou apontam regiões de memória fixas do programa.

   // Caso não coloque como const: error: attempt to free a non-heap object 


O compilador usa uma heurística de acompanhamento do valor atribuído à variável de tipo ponteiro para saber se ele aponta para algum lugar que seguramente não é nulo nem proveniente de alocação dinâmica, e avisa se você chamar free() sobre um ponteiro que tenha sido identificado nessa situação.

   //free(TextPtr); // error: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 


Essa mensagem indica que você está tentando passar um ponteiro para dados constantes para como argumento para uma função que supõe que o ponteiro se refere a dados não-constantes. No caso das funções de alocação e desalocação dinâmica, não é de se supor que você trabalharia com ponteiros para dados constantes já que, se o fizesse, não poderia alterar o conteúdo da memória que pediu ao sistema que alocasse para você, já que o conteúdo dessa memória, imediatamente após a alocação, é indeterminado, e é improvável que tal conteúdo, justamente por ser indeterminado, possa ser útil a qualquer tipo de programa.

   //free(TextPtr2);
return 0;
}
/* RESULT
0x402004 TextPtr[0] L offset of TextPtr2[0] 0x402004 (TextPtr2 - TextPtr) = 0
0x402004 TextPtr[1] i offset of TextPtr2[1] 0x402005 (TextPtr2 - TextPtr) = 1
0x402004 TextPtr[2] n offset of TextPtr2[2] 0x402006 (TextPtr2 - TextPtr) = 2
0x402004 TextPtr[3] u offset of TextPtr2[3] 0x402007 (TextPtr2 - TextPtr) = 3
0x402004 TextPtr[4] x offset of TextPtr2[4] 0x402008 (TextPtr2 - TextPtr) = 4
0x402004 TextPtr[5] - offset of TextPtr2[5] 0x402009 (TextPtr2 - TextPtr) = 5
0x402004 TextPtr[6] P offset of TextPtr2[6] 0x40200a (TextPtr2 - TextPtr) = 6
0x402004 TextPtr[7] t offset of TextPtr2[7] 0x40200b (TextPtr2 - TextPtr) = 7
0x402004 TextPtr[8] r offset of TextPtr2[8] 0x40200c (TextPtr2 - TextPtr) = 8
*/


O resultado está, sim, “correto”, no sentido de fazer o que foi pedido, ainda que talvez o que tenha sido pedido talvez seja questionável, como vimos acima.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


6. Re: Dúvida Básica sobre ponteiro!

Paulo
paulo1205

(usa Ubuntu)

Enviado em 30/01/2022 - 22:34h

ApprenticeX escreveu:

Um outro ponto que gostaria de entender é: Se eu posso então usar um ponteiro pra receber valores que não faço idéia do tamanho, tipo um texto inteiro de um arquivo que não sei qts caracteres terá! Uma idéia Ex abaixo!


Cuidado! Lembre-se que variáveis de tipo ponteiro não recebe conteúdo, mas tão-somente endereços de onde dados já residem ou de uma região de memória onde novos dados poderão residir.

const char *Ptr = "Mini Text";
Ptr = "Texto muito Maior que o Mini Text";
Ptr = "Imagine aqui um Texto com 100.000 Caracteres";
Ptr = "Imagine aqui um Texto com 500.000 Caracteres";
Ptr = NULL; // Meu Computador liberou da memória os 500.000 Caracteres?


Não. Alterar o valor de um ponteiro não afeta de maneira nenhuma o conteúdo apontado anteriormente nem o conteúdo para o qual ele passou a apontar (ao menos não enquanto você não usar o ponteiro para explicitamente fazer uma alteração nesse conteúdo).

free(Ptr); // Meu Computador liberou da memória os 500.000 Caracteres? 


Você só poderia chamar free() para ponteiros que tenham sido manualmente alocados com malloc(), calloc() ou realloc() (ou alguma outra função que use uma dessas três internamente, e que diga explicitamente que é sua responsabilidade fazer a desalocação com free(); eu consigo lembrar de strdup(), asprintf() e getline(), entre outras que não faze parte do padrão do C, mas são comuns no mundo UNIX/POSIX).

Ou o que aconteceu acima, foi que ficou na memória todos os textos declarados que esse ponteiro usou?


Do jeito como você fez, você apontou o ponteiro para múltiplas strings que são constantes literais (armazenadas na forma de arrays) pelo programa. Você não tem como se desfazer desses arrays constantes ao longo de todo o tempo de vida do programa (pelo menos não de forma padronizada e trivial — para conseguir fazê-lo, no mínimo você teria de usar alguma técnica arcana do sistema operacional, se é que tal técnica existe).


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


7. Re: Dúvida Básica sobre ponteiro! [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 31/01/2022 - 16:43h

paulo1205 escreveu: Lembre-se, porém, que texto entre aspas produz uma string constante (cujo tipo é array de caracteres com tantos elementos quantos contidos entre as aspas mais um elemento para um byte nulo, após o último elemento explícito). O C permite que você use um ponteiro para caracteres não-constantes para apontar para essa área de dados constantes, mas isso é uma anomalia no sistema de tipos, que existe em função de compatibilidade com código obsoleto, que existia antes do padrão do C que introduziu a palavra-chave “const”. O C++, por exemplo, não permite uma declaração como a que você fez, e mesmo em C seria melhor que você tivesse qualificado o dado apontado como constante, como mostrado abaixo.
const char *Nome="Viva o Linux"; 
Achei muito importante destacar essa sua observação. Muito Obrigado!

O que entendi aqui é que então todo ponteiro declarado da forma abaixo são variáveis constantes certo? Isso porque declarei elas como Ponteiro! Constantes porque foram alocadas em um endereço de memória e como o Ponteiro apenas aponta para um endereço, eu NUNCA poderei mudar os valores registrados e alocados na memória, o que faço apenas é apontar o Ponteiro para outro local sempre!
const int *Numbers[] = { 3,  0,  1,  8,  7,  2,  5,  4,  9,  6 }; // Constante pq declarei como um ponteiro. Certo?
const *int Number = 10; // Constante pq declarei como um ponteiro. Certo?
const char *Nome; // Constante pq declarei como um ponteiro. Certo?


paulo1205 escreveu: Ponteiros funcionam como referências ou indireções, isto é, formas indiretas de chegar a um dado ou de referir-se a eles.
Como acessos indiretos frequentemente são mais lentos do que acessos diretos, provavelmente não convém chamar de atalho.

Então entendi que em muitos casos, não é o ideal usar ponteiros, o ideal mesmo então seria ter um tipo declarado e referenciado como é o caso das variáveis normais.

O que me fez pensar neste caso específico em C++, pois todas as janelas e objetos, usam ponteiros para acessá-las ao menos parece ser o padrão, mas também no caso da FLTK que estou estudando e vi exemplos aqui no VOL, ela pode usar as janelas sem serem ponteiros.

O que me fez pensar também em funções onde em seu cabeçalho muitas vezes usamos ponteiros, então seria melhor se possível não usar ponteiros e usar variáveis normais no lugar, isso seria o mais ideal, tipo:
void funcao(int vector[]) // Melhor assim
void funcao(int *vector) // Do que assim?
Se bem que acho que esse assunto de função trata-se de outro assunto diferente. Eu tenho que entender melhor ainda ponteiros pra entrar nesse outro campo de ponteiros com função, até porque ainda estou montando funções e entendendo como cada item nelas nelas funcionam.

paulo1205 escreveu: Não entendi a dúvida
char Text[] = "Linux-Text"; // Então char Text[] seu valor é imutável usando um ponteiro certo? Visto que ponteiros não modificam valores, apenas apontam para valores existentes ou novos valores certo?  

O que eu quis dizer que no caso acima, se eu atribuo um ponteiro para text
char Text[] = "Linux-Text";
const char *Ponteiro;
Ponteiro = Text; // Ponteiro aponta para o Valor em Text
O que eu quis dizer é que um ponteiro NUNCA vai conseguir mudar o valor de Text. Ou seja, não existe uma operação que permita um ponteiro mudar os dados atribuidos a uma variável, certo?

paulo1205 escreveu:Você só poderia chamar free() para ponteiros que tenham sido manualmente alocados com malloc(), calloc() ou realloc() (ou alguma outra função que use uma dessas três internamente, e que diga explicitamente que é sua responsabilidade fazer a desalocação com free(); eu consigo lembrar de strdup(), asprintf() e getline(), entre outras que não faze parte do padrão do C, mas são comuns no mundo UNIX/POSIX).
Agora entendi muito melhor o free(), eu achava que ele tinha a ver com ponteiros especificamente, e não exatamente relacionados as funções malloc(), calloc() ou realloc()


8. Re: Dúvida Básica sobre ponteiro!

Paulo
paulo1205

(usa Ubuntu)

Enviado em 03/02/2022 - 22:44h

ApprenticeX escreveu:

O que entendi aqui é que então todo ponteiro declarado da forma abaixo são variáveis constantes certo?


Não é isso, não. E você tem de saber a diferença entre ponteiros para dados que são constantes e variáveis ponteiros que podem ser elas próprias constantes mas podem apontar para dados que não são constantes.

Compare as declarações abaixo.
int *pi;  // Ponteiro (não-constante) para dado inteiro (não-constante).
const int *pci; // Ponteiro (não-constante) para dado inteiro constante.
int const *pci2; // Outra forma de fazer ponteiro (não-constante) para dado inteiro constante (i.e. “int const” é sinônimo de “const int”).

typedef unsigned char byte;

// Com ponteiros constantes, convém definir seu valor (para onde aponta) no momento da declaração, pois depois não será possível alterá-lo.
byte *const cp_video_mem=(byte *)0x000b8000; // Ponteiro constante para (região de) dados do tipo byte (não-constantes) (0x000b8000 até 0x000bffff é a região de memória de vídeo para texto e modos gráficos de baixa resolução). Posso ler e alterar o conteúdo da memória de vídeo, pois apesar de o ponteiro ser constante, o dado apontado não o é.
const byte *const cp_mb_bios=(byte *)0x000f0000; // Ponteiro constante para (região de) dados constantes do tipo byte (0x000f0000 até 0x000fffff é (era) a região do BIOS da placa-mãe). Posso usar cp_mb_bios para examinar o conteúdo do BIOS, mas não para modificá-lo, porque aqui eu disse que os dados são constantes.


Isso porque declarei elas como Ponteiro! Constantes porque foram alocadas em um endereço de memória e como o Ponteiro apenas aponta para um endereço, eu NUNCA poderei mudar os valores registrados e alocados na memória, o que faço apenas é apontar o Ponteiro para outro local sempre!
const int *Numbers[] = { 3,  0,  1,  8,  7,  2,  5,  4,  9,  6 }; // Constante pq declarei como um ponteiro. Certo?
const *int Number = 10; // Constante pq declarei como um ponteiro. Certo?
const char *Nome; // Constante pq declarei como um ponteiro. Certo?


As duas primeiras declarações estão erradas:

  • você declarou Numbers como um array de ponteiros para inteiros constantes, mas tentou atribuir valores inteiros diretamente a cada elemento do array, sendo que tais elementos são ponteiros (talvez algum compilador mais antigo ou com opções de compilação que o deixem mais tolerante a tipos incompatíveis deixe isso passar, providenciando a conversão implícita de cada valor inteiro na lista de inicialização para um valor absoluto de endereço correspondente, mas eu não creio que era isso que você queria que acontecesse);

  • a declaração de Number está com um erro de sintaxe (“const *int” não é uma construção permitida, e eu não sei se você quis dizer “const int *Number”, para declarar um ponteiro para valor inteiro constante, ou “int *const Number”, para declarar um ponteiro constante para valor inteiro (não-constante), mas, de um jeito ou de outro, a atribuição a esse ponteiro do valor inteiro 10 seria errônea.

Declarar alguma coisa como ponteiro não implica, por si só, nem que o ponteiro será constante nem que o objeto apontado será constante, então os comentários que acompanham cada declaração acima podem não fazer muito sentido.

Recapitulando, e quiçá esclarecendo, o que já foi dito acima e e em outros tópicos:

  • Um ponteiro para dados constantes significa que o objeto referenciado não poderá ter seu valor alterado quando o acesso for feito por meio desse ponteiro, quer seja tal ponteiro contido numa variável ponteiro, quer seja obtido por avaliação de alguma expressão.

      • Como caso particular, é perfeitamente válido ter um ponteiro para dados constantes apontando para um objeto que inicialmente não é constante, e isso significa, como dito explicitamente acima, que esse ponteiro, em particular, não poderá ser usado para modificar o objeto original.

      • O contrário, isto é, um ponteiro para dados não-constantes referenciando um objeto que é constante, é que normalmente indica algum erro cometido no projeto ou na codificação do programa, ou pode induzir a erros de execução.

          • Essa observação continua válida mesmo quando esse uso é tolerado pela linguagem, como é o caso particular, em C (mas não em C++), de ponteiros para caracteres (não-constantes) podendo receber valores provenientes de constantes literais strings entre aspas.
/* 01 */	#include <stdio.h>
/* 02 */
/* 03 */ const char vowels[]={'a', 'e', 'i', 'o', 'u'}; // Array de 5 elementos explicitamente definidos como constantes (e que não é string, porque falta o byte nulo).
/* 04 */ char *p1=vowels; // Tipo de ponteiro incompatível com objeto apontado (ponteiro que permite acesso de modificação do dado, mas objeto apontado é constante).
/* 05 */ // Dependendo das opções de compilação, esse tipo incompatível pode ser considerada como erro (interrompendo a compilação), ou pode ser
/* 06 */ // apenas exibido um alerta de incompatibilidade, e a compilação pode prosseguir. Recomendo sempre parar a compilação nesses casos.
/* 07 */
/* 08 */ const char vowels_str[]="aeiou"; // Array de 6 elementos constantes (tanto os cinco do texto quanto o byte nulo).
/* 09 */ char *p2=vowels_str; // Tipo de ponteiro incompatível com objeto apontado. Mesmos comentários feitos sobre p1, acima, aplicam-se aqui.
/* 10 */
/* 11 */ const char vowels_str2[]="aeiou"; // Outro array, distinto do primeiro, embora o conteúdo seja idêntico ao daquele, de 6 elementos constantes.
/* 12 */
/* 13 */ // NOTA (e corolário para guardar): Arrays explicitamente declarados como tais ocupam regiões de memória distintas de outros arrays.
/* 14 */
/* 15 */ char *p3="aeiou"; // Constante literal produz array anônimo com seis elementos constantes, distinto de todos os arrays acima. A atribuição array de elementos
/* 16 */ // constantres ao ponteiro para dado não-constante é válida em C, porém perigosa.
/* 17 */ const char *p4="aeiou"; // O compilador pode (e normalmente vai) reaproveitar o mesmo array anônimo para essas duas constantes literais idênticas, de modo que
/* 18 */ // p3 e p4 podem apontar para o mesmo array anônimo (mesmo valor de ponteiro em duas variáveis ponteiro diferentes).
/* 19 */
/* 20 */ // Todos os arrays acima são tipicamente dispostos numa região de memória que tem o atributo de somente leitura (read-only): os
/* 21 */ // explicitamente declarados porque usaram o atributo const para seus elementos, e os decorrentes das constantes literais string
/* 22 */ // porque — oh! surpresa! — são literalmente constantes. É por esse motivo que não é seguro usar um ponteiro que não especifica
/* 23 */ // que seus dados são constantes para referir-se a algo que é constante: alguém (incluindo o compilador) poderia achar que o fato
/* 24 */ // de o ponteiro não restringir a modificação do dado apontado implica que o dado necessariamente pode ser modificado.
/* 25 */
/* 26 */ char some_chars[]="aeiou"; // Array com elementos que não são constantes. Os elementos serão dispostos numa região de memória específica para este novo array e, ao
/* 27 */ // contrário dos arrays acima, será colocado numa região de memória que não tem o atributo de apenas leitura.
/* 28 */ char *p5=some_chars; // Aponto com um ponteiro que permite acesso indireto de modificação, o que é OK, neste caso, pois o dado apontado realmente é modificável.
/* 29 */ const char *p6=some_chars; // Aponto com um ponteiro que permite acesso indireto de consulta, mas não de modificação. Isso também é OK: o atributo const, aplicado ao
/* 30 */ // dado apontado, não especifica a natureza do dado apontado, mas sim de que modo o ponteiro pode ser usado para obter acesso ao conteúdo.
/* 31 */
/* 32 */ const char *f(void){
/* 33 */ static const char static_vowels[]="aeiou"; // Dados locais estáticos, assim como variáveis globais, têm seus valores dispostos em memória durante a compilação, e não redefinidos cada
/* 34 */ static char static_chars[]="aeiou"; // vez que a função é invocada (são “locais” apenas no sentido de seu escopo de visibilidade, não em termos de tempo ou fluxo de vida).
/* 35 */
/* 36 */ const char local_vowels[]="aeiou"; // Dados locais automáticos (não-estáticos) têm seus valores iniciais atribuídos cada vez que a função é invocada. Nestas duas linhas,
/* 37 */ char local_chars[]="aeiou"; // “"abcde"” é uma constante literal string, que pode produzir um array anônimo com elementos constantes (podendo até ser o mesmo array anônimo
/* 38 */ // gerado na linha 15 e reaproveitado na linha 17), e esses elementos são copiados para os arrays locais automáticos cada vez que o fluxo
/* 39 */ // de execução passa pelas linhas 36 e 37. A memória de dados automáticos normalmente não possui o atributo de somente leitura, o que
/* 40 */ // significa que garantir que o atributo const na linha 34 será honrado por todos os ponteiros que vierem a apontar para o objeto será
/* 41 */ // responsabilidade exclusivamente do compilador (mas é bom o programador ajudar, não tentando abusar do sistema de tipos).
/* 42 */
/* 43 */ char *lp1=static_vowels; // ERRO/ALERTA: Tipo de ponteiro incompatível com objeto apontado (ponteiro sugere que dado pode ser modificado, mas objeto apontado é constante).
/* 44 */ char *lp2=static_chars; // OK: ponteiro que permite acesso de modificação apontando para dado modificável.
/* 45 */ char *lp3=local_vowels; // ERRO/ALERTA: Tipo de ponteiro incompatível com objeto apontado (ponteiro sugere que dado pode ser modificado, mas objeto apontado é constante).
/* 46 */ char *lp4=local_chars; // OK: ponteiro que permite acesso de modificação apontando para dado modificável.
/* 47 */
/* 48 */ const char *lp5=static_vowels; // OK: ponteiro que não permite acesso de modificação apontando para dado que não permite modificação.
/* 49 */ const char *lp6=static_chars; // OK: ponteiro que não permite acesso de modificação apontando para dado que permite modificação.
/* 50 */ const char *lp7=local_vowels; // OK: ponteiro que não permite acesso de modificação apontando para dado que não permite modificação.
/* 51 */ const char *lp8=local_chars; // OK: ponteiro que não permite acesso de modificação apontando para dado que permite modificação.
/* 52 */
/* 53 */ printf("%s, %s, %s, %s\n", lp1, lp2, lp3, lp4);
/* 54 */
/* 55 */ ++*lp1; // Provavelmente isto aqui vai fazer o programa capotar com falha de segmentação (SIGSEGV) — o ponteiro permite o acesso de modificação, mas o
/* 56 */ // dado apontado reside numa região de memória marcada com acesso somente de leitura.
/* 57 */ ++*lp2; // OK.
/* 58 */ ++*lp3; // Comportamento indeterminado (se a compilação não tiver parado na linha 45 por causa do erro de incompatibilidade de tipos, pode ser que a
/* 59 */ // CPU não tenha como verificar, em tempo de execução, que este array disposto na memória de dados automáticos não deveria ser modificado).
/* 60 */ ++*lp4; // OK.
/* 61 */ //++*lp5; ++*lp6; ++*lp7; ++*lp8; // Linha comentada porque o compilador não vai permitir nenhuma dessas operações que tentam usar um ponteiro para dado constante para modificar
/* 62 */ // o dado apontado (e isso ocorre mesmo que você não use a opção de compilação que transforma alertas em erros).
/* 63 */
/* 64 */ printf("%s, %s, %s, %s\n", lp5, lp6, lp7, lp8);
/* 65 */
/* 66 */ return lp2;
/* 67 */ }
/* 68 */
/* 69 */ int main(void){
/* 70 */ printf("vowels: addr=%p, size=%zu, contents=\"%.5s\"; p1: addr=%p, value=%p, contents=\"%.5s\"\n", vowels, sizeof vowels, vowels, &p1, p1, p1);
/* 71 */ ++*p1;
/* 72 */ printf("vowels: addr=%p, size=%zu, contents=\"%.5s\"; p1: addr=%p, value=%p, contents=\"%.5s\"\n\n", vowels, sizeof vowels, vowels, &p1, p1, p1);
/* 73 */
/* 74 */ printf("vowels_str: addr=%p, size=%zu, contents=\"%s\"; p2: addr=%p, value=%p, contents=\"%s\"\n", vowels_str, sizeof vowels_str, vowels_str, &p2, p2, p2);
/* 75 */ ++*p2;
/* 76 */ printf("vowels_str: addr=%p, size=%zu, contents=\"%s\"; p2: addr=%p, value=%p, contents=\"%s\"\n\n", vowels_str, sizeof vowels_str, vowels_str, &p2, p2, p2);
/* 77 */
/* 78 */ printf("vowels_str2: addr=%p, size=%zu, contents=\"%s\"\n\n", vowels_str2, sizeof vowels_str2, vowels_str2);
/* 79 */
/* 80 */ printf("p3: addr=%p, value=%p, contents=\"%s\"; p4: addr=%p, value=%p, contents=\"%s\"\n", &p3, p3, p3, &p4, p4, p4);
/* 81 */ ++*p3;
/* 82 */ printf("p3: addr=%p, value=%p, contents=\"%s\"; p4: addr=%p, value=%p, contents=\"%s\"\n\n", &p3, p3, p3, &p4, p4, p4);
/* 83 */
/* 84 */ printf("some_chars: addr=%p, size=%zu, contents=\"%s\"; p5: addr=%p, value=%p, contents=\"%s\"; p6: addr=%p, value=%p, contents=\"%s\"\n", some_chars, sizeof some_chars, some_chars, &p5, p5, p5, &p6, p6, p6);
/* 85 */ ++*p5;
/* 86 */ printf("some_chars: addr=%p, size=%zu, contents=\"%s\"; p5: addr=%p, value=%p, contents=\"%s\"; p6: addr=%p, value=%p, contents=\"%s\"\n\n", some_chars, sizeof some_chars, some_chars, &p5, p5, p5, &p6, p6, p6);
/* 87 */
/* 88 */ printf("%s\n", f());
/* 89 */ printf("%s\n", f());
/* 90 */ }
/*
As linhas 04, 09, 43 e 45 produzem mensagens de alerta (ou erro — eu recomendo usar as opções que fazem com que dê erro!).

Sem forçar a compilação sem que os abusos provoquem erro, mas apenas alertas, memso assim, as linhas 55, 71, 75 e 81 têm de
ser comentadas. Caso contrário, o programa vai capotar.

A linha 50 representa uma violação que fica latente, embora seja semanticamente parecida com a da linha 48, mas o programa
não capota na linha 58, como o faz na linha 55 se não estiver comentada, porque o hardware do PC não consegue marcar a memória
automática onde local_vowels é colocada como read-only.

A saída do programa, quando compilado ingorando os alertas e comentando as linhas indicadas acima, está abaixo. Note como,
ao se chamar f(), uma das strings que é declarada como constante (local_vowels) acaba sendo alterada por conta de um ponteiro
para dados não constantes para apontar para ela.

vowels: addr=0x5621b350abe8, size=5, contents="aeiou"; p1: addr=0x5621b370c020, value=0x5621b350abe8, contents="aeiou"
vowels: addr=0x5621b350abe8, size=5, contents="aeiou"; p1: addr=0x5621b370c020, value=0x5621b350abe8, contents="aeiou"

vowels_str: addr=0x5621b350abed, size=6, contents="aeiou"; p2: addr=0x5621b370c028, value=0x5621b350abed, contents="aeiou"
vowels_str: addr=0x5621b350abed, size=6, contents="aeiou"; p2: addr=0x5621b370c028, value=0x5621b350abed, contents="aeiou"

vowels_str2: addr=0x5621b350abf3, size=6, contents="aeiou"

p3: addr=0x5621b370c030, value=0x5621b350abf9, contents="aeiou"; p4: addr=0x5621b370c038, value=0x5621b350abf9, contents="aeiou"
p3: addr=0x5621b370c030, value=0x5621b350abf9, contents="aeiou"; p4: addr=0x5621b370c038, value=0x5621b350abf9, contents="aeiou"

some_chars: addr=0x5621b370c010, size=6, contents="aeiou"; p5: addr=0x5621b370c040, value=0x5621b370c010, contents="aeiou"; p6: addr=0x5621b370c048, value=0x5621b370c010, contents="aeiou"
some_chars: addr=0x5621b370c010, size=6, contents="beiou"; p5: addr=0x5621b370c040, value=0x5621b370c010, contents="beiou"; p6: addr=0x5621b370c048, value=0x5621b370c010, contents="beiou"

aeiou, aeiou, aeiou, aeiou
aeiou, beiou, beiou, beiou
beiou
aeiou, beiou, aeiou, aeiou
aeiou, ceiou, beiou, beiou
ceiou

*/


  • Um ponteiro constante significa um valor constante para o endereço que ele indica, o que não necessariamente significa que o dado contido nesse endereço será constante: o tipo do dado apontado só vai ser considerado constante se você disser que ele o é.
char some_chars[]="abcde";

const char vowels[]="aeiou";

void f(void){
// Todas as atribuições dos ponteiros abixo são válidas.
char *const p1=some_chars;
char *p2=some_chars;
const char *p3=some_chars;
const char *const p4=some_chars;

++some_chars; // ERRO: neste contexto, some_chars decai para um ponteiro constante para o primeiro elemento; sendo constante, não pode ser alterado.
++*some_chars; // OK: posso alterar o elemento referido pelo ponteiro constante, pois o tipo do elemento não é constante.

++p1; // ERRO: o ponteiro é constante, logo não pode ser alterado.
++*p1; // OK: dado apontado não é constante.

++p2; // OK: ponteiro não-constante passa a apontar para o segundo elemento do array.
++*p2; // OK: altera o segundo elemento do array (dado apontado não é constante).

++p3; // OK: ponteiro não-constante passa a apontar para o segundo elemento do array.
++*p3; // ERRO: dado apontado é inidicado como constante, logo não pode ser alterado.

++p4; // ERRO: o ponteiro é constante, logo não pode ser alterado.
++*p4; // ERRO: o dado apontado é indicado como constante, logo não pode ser alterado.
}

void g(void){
// Não vou nem tentar usar ponteiros para dados não-constantes, que seria uma violação de tipo (ver programa exemplo mostrado no exemplo anterior).
const char *p1=vowels;
const char *const p2=vowels;

++vowels; // ERRO: Neste contexto, vowels decais para um ponteiro constante para o primeiro elemento; sendo constante, não pode ser alterado.
++*vowels; // ERRO: não posso alterar o elemento referido pelo ponteiro constante porque o tipo do elemento também é constante.

++p1; // OK: ponteiro não-constante passa a apontar para o segundo elemento do array.
++*p1; // ERRO: dado apontado é indicado como constante, logo não pode ser alterado.

++p2; // ERRO: o ponteiro é constante, logo não pode ser alterado.
++*p2; // ERRO: dado apontado é indicado como constante, logo não pode ser alterado.
}


paulo1205 escreveu: Ponteiros funcionam como referências ou indireções, isto é, formas indiretas de chegar a um dado ou de referir-se a eles.
Como acessos indiretos frequentemente são mais lentos do que acessos diretos, provavelmente não convém chamar de atalho.

Então entendi que em muitos casos, não é o ideal usar ponteiros, o ideal mesmo então seria ter um tipo declarado e referenciado como é o caso das variáveis normais.


Como praticamente todos os recursos de uma linguagem de programação, você deve dar preferência a uma determinada funcionalidade quando ela for a melhor (ou, às vezes, a única) maneira de fazer aquilo que você precisa que seja feito.

Esses exemplos toscos em que se uma uma variável ponteiro para referir-se a uma variável escalar que foi declara numa linha logo acima ou num contexto muito semelhante não são a principal razão de ser de ponteiros. Eis os principais motivos pelos quais ponteiros são muito usados em C:

  • Para evitar cópias de dados, sobretudo quando os dados envolvidos são muito grandes.

      • Isso é particularmente verdadeiro quando você tem dados que são arrays ou estruturas com vários campos e você quer passar tais dados como argumentos para funções. Em lugar de copiar dezenas, centenas ou mesmo milhares de bytes para cada argumento de um tipo composto ao chamar a função, você copia apenas o endereço de onde esses dados residem, e a função usa o endereço recebido para ter acesso aos dados que nele estão armazenados.

      • Também no momento de retornar dados que ocupam muitos bytes, pode ser melhor retornar um ponteiro para uma estrutura, array ou bloco de memória do que fazer uma cópia de um centenas ou milhares de bytes de dentro da função para o destino que reside fora dela.

  • Porque não existe cópia direta de arrays em C.

      • Isso implica que sempre que um array tiver de ser passado como argumento para uma função, não existe alternativa ao uso de ponteiros para receber esse parâmetro. Mesmo que você declare um parâmetro de função com aspecto de array, até mesmo colocando um valor numéro entre colchetes para especificar a suporta quantidade de elementos). o compilador silenciosamente vai converter aquele parâmetro em ponteiro, e vai converter também o argumento que você usar quando invocar a função.

O que me fez pensar neste caso específico em C++, pois todas as janelas e objetos, usam ponteiros para acessá-las ao menos parece ser o padrão, mas também no caso da FLTK que estou estudando e vi exemplos aqui no VOL, ela pode usar as janelas sem serem ponteiros.


O C++ tem um recurso chamado referências, que não existe em C, e que são uma forma disfarçada de ponteiro na qual só é possível apontar objetos válidos, correspondentes a alguma variável. Quando você chama uma função cujos parâmetros são referências, a forma escrever os argumentos pode dar a entender que você os está passando por (cópia de) valor, mas o compilador automaticamente obtém os endereços dos argumentos e passa tais endereços.

Eu não conheço o FLTK, mas pode ser que esses objetos que você diz que não são ponteiros sejam justamente referências.

O que me fez pensar também em funções onde em seu cabeçalho muitas vezes usamos ponteiros, então seria melhor se possível não usar ponteiros e usar variáveis normais no lugar, isso seria o mais ideal, tipo:
void funcao(int vector[]) // Melhor assim
void funcao(int *vector) // Do que assim?


Ambos são absolutamente sinônimos e implicam que o parâmetro é um ponteiro para int.

Há quem prefira usar a sintaxe com colchetes quando semanticamente fizer mais sentido chamar com um objeto que seja um array, e com asterisco num caso mais geral, em que a relação dos eventuais argumentos com algum array declarado como tal não for garantida ou garantidamente não existir. Mas isso é mera convenção, e nem tudo mundo usa (eu mesmo não uso: sempre uso apenas a sintaxe com asteriscos).


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts