Dúvida ponteiro int [RESOLVIDO]

1. Dúvida ponteiro int [RESOLVIDO]

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 22/01/2023 - 23:03h

Boa Noite a todos!
Tentando declarar um ponteiro int, notei que NINGUÉM declara ponteiro int com valor!
Sempre declaram um int + um ponteiro separado pra apontar pra ele!

Quando finalmente consigo declarar, não consigo imprimir, tentei várias combinações, algumas abaixo!
É possível imprimir o ponteiro que criei?

//int *Ptr_Int = 2; // Não  Funciona | error: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
int *Ptr_Int = (int*)2; // Descobri que assim funciona! MAS, assim não consigo imprimir na tela!

// Deveria imprimir assim, mas não funciona
//printf("%d\n", *Ptr_Int); // error: array subscript 0 is outside array bounds of ‘int[0]’ [-Werror=array-bounds]

//printf("%d\n", Ptr_Int); // error: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Werror=format=]
//printf("%ls\n", *Ptr_Int); // error: format ‘%ls’ expects argument of type ‘wchar_t *’, but argument 2 has type ‘int’ [-Werror=format=]
//printf("%ls\n", Ptr_Int); // Segmentation fault





  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 30/01/2023 - 10:13h

ApprenticeX escreveu:

Eu entendi o que o SamL explicou, mas não entendi esse tipo de referência!

Qdo declaro um ponteiro de char, eu estou apontando o ponteiro para o endereço de memória onde está VOL ou seja VOL[0]
char *Ptr = "VOL"; 


Cuidado com esse “VOL[0]”... Não se aplica um índice ao conteúdo de uma string, mas sim ao endereço do primeiro elemento. Se você tivesse dito “"VOL"[0]” (que terá o mesmo valor que 'V'), aí, sim, estaria correto.

Então porque não é a mesma coisa usando int? Eu imaginava que eu estaria apontando o ponteiro para o endereço de memória onde está 2
int *Ptr = 2; 
Mas segundo o que SamL explica não é assim, pois entendi que SamL diz que estou apontando o Ponteiro para o endereço de memória 2 e não para o endereço de memória onde está o valor 2.


Lembra que eu disse em mensagens passadas que, no C, um texto entre aspas indica uma constante literal de string, cujo tipo de dados é array de const char com tantos elementos quanto houver caracteres entre as aspas mais um (para acomodar o byte terminador da string)?

Pois é... Se não lembrava ou não viu, lembrou ou viu agora. E esse é um ponto importante, pois quando você faz algo como
const char *ptr="uma string qualquer"; 
o que o compilador vai fazer é:

    1) alocar na área de dados constantes (no mundo UNIX, tipicamente a seção chamada .rodata, de Read-Only DATA, dados apenas de leitura) espaço suficiente para acomodar vinte caracteres (os dezenove do texto entre aspas, mais um byte nulo ao final);

    2) criar no local adequado (dependendo de se é uma variável global ou estática ou se é uma variável local) espaço para uma variável (no caso, chamada ptr) do tipo ponteiro para caracteres constantes (const char *), que é espaço suficiente para acomodar um endereço de memória (tipicamente 8 bytes (64 bits) num sistema de 64 bits, ou 4 bytes (32 bits) num sistema de 32 bits);

    3) copiar o endereço inicial da região alocada em (1) (o qual corresponde ao primeiro elemento do array indicado na declaração) para dentro do espaço alocado em (2).

Ou seja: na declaração acima, existe uma definição de array com associação desse array a um ponteiro, algo que é perfeitamente válido de fazer, uma vez que todo array em C decai automaticamente para um ponteiro com tipo de dado correspondente ao dos elementos do array e com o valor do endereço do primeiro elemento.

O análogo disso usando inteiros, portanto, tem de ter um array de inteiros no lado direito do sinal de atribuição, não um valor escalar inteiro.

Tal análogo não era possível no C90 (também conhecido como ANSI C ou C89), mas é possível no C11 (padrão de 2011).
int *pi=(int []){1, 2, 3};  // Define um array com três elementos (calculados a partir da quantidade de itens na lista composta de literais),
// e depois copia o endereço do 1º elemento desse array para a variável ponteiro (que armazena um endereço).

// A notação com colchetes vazios é opcional. Você pode determinar o número de elementos exatos do array.
double *pd=(double [2]){8.31446261815324, 6.02214076e23}; // Quantidade justa de elementos.

char *nome=(char [30]){'p', 'a', 'u', 'l', 'o', '1', '2', '0', '5'}; // Os elementos adicionais são preenchidos com zeros (neste caso, permite usar ‘nome’ como string, pois haverá pelo menos um byte nulo após o texto “paulo1205”).
char *nao_string=(char [2]){'\123', '\234'}; // Não pode ser usado como string porque não tem um byte nulo como elemento do array.

wchar_t *erro=(wchar_t[2]){L'A', L'B', L'C'}; // ERRO: tentativa de usar uma lista com três elementos para formar um array com apenas dois elementos reservados.


Note que nos exemplos acima não existe nenhuma mágica. Essa sintaxe semelhante à de conversão de tipos (type casting) aplicada à compostos de constantes literais entre chaves nada mais faz do que criar arrays anônimos e copiar os endereços dos primeiros elementos de cada um desses arrays para os respectivos ponteiros. Em termos de consumo de memória, isso seria completamente idêntico a criar um array explicitamente, e depois criar uma variável ponteiro à qual seria atribuído o array. Isso pode ser demonstrado com uma comparação lado a lado do código gerado pelo compilador (GCC 11.3) para os dois seguintes programas.
// Arquivo a.c

static int _anon_0[]={1, 2, 3};
int *ptr=_anon_0;

// Arquivo b.c

int *ptr=(int []){1, 2, 3};

$ gcc -O0 -S -c a.c

$ gcc -O0 -S -c b.c

$ paste <(expand -t 8 a.s) <(expand -t 8 b.s) | expand -t 70 | cat -n # O que nos interessa está entre as linhas 3 e 17, mas todas as linhas são equivalentes.
1 .file "a.c" .file "b.c"
2 .text .text
3 .data .data Mesma seção de dados (“.data”)
4 .align 8 .align 8 Mesmo alinhamento (disposição em endereço que seja múltiplo de 8 bytes)
5 .type _anon_0, @object .type __compound_literal.0, @object Mesmo tipo, muda só o nome (em b.c, o compilador gerou um nome interno para o array anônimo)
6 .size _anon_0, 12 .size __compound_literal.0, 12 Mesmo tamanho (12 bytes: 3 elementos de 4 bytes)
7 _anon_0: __compound_literal.0: Mesma posição
8 .long 1 .long 1 Mesmos...
9 .long 2 .long 2 ... três...
10 .long 3 .long 3 ... elementos
11 .globl ptr .globl ptr Mesma variável ponteiro (ptr)
12 .section .data.rel.local,"aw" .section .data.rel.local,"aw" Mesma seção de dados relocáveis
13 .align 8 .align 8 Mesmo alinhamento (disposição em endereço que seja múltiplo de 8 bytes)
14 .type ptr, @object .type ptr, @object Mesmo tipo (e mesmo nome explícito)
15 .size ptr, 8 .size ptr, 8 Mesmo tamanho (8 bytes, porque meu sistema é de 64 bits, logo ponteiros têm 64 bits)
16 ptr: ptr: Mesma posição
17 .quad _anon_0 .quad __compound_literal.0 Mesmo valor atribuído (posição do começo do array disposto entre as linhas 3 e 10)
18 .ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0" .ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
19 .section .note.GNU-stack,"",@progbits .section .note.GNU-stack,"",@progbits
20 .section .note.gnu.property,"a" .section .note.gnu.property,"a"
21 .align 8 .align 8
22 .long 1f - 0f .long 1f - 0f
23 .long 4f - 1f .long 4f - 1f
24 .long 5 .long 5
25 0: 0:
26 .string "GNU" .string "GNU"
27 1: 1:
28 .align 8 .align 8
29 .long 0xc0000002 .long 0xc0000002
30 .long 3f - 2f .long 3f - 2f
31 2: 2:
32 .long 0x3 .long 0x3
33 3: 3:
34 .align 8 .align 8
35 4: 4:


A única referência que vi que aponta para o endereço de memória é quando se declara NULL, apontando para um valor Nulo, entendo que Nulo seria 0.
char *Ptr = NULL; 
Essa estranheza é porque já li tanta coisa sobre ponteiros e ou não tive atenção a isso, ou ninguém explica isso em livros ou textos na internet!


Pois é... Tecnicamente falando, NULL não é exatamente igual a zero, porque o tipo dele não é um tipo aritmético comum. O valor de NULL em C é ((void *)0). Pelo fato de ser um void *, não é possível fazer aritmética de ponteiro com esse valor.

Mas esse tipo de coisa é explicada, sim. O mais provável é que você não tenha encontrado os lugares certos, ou que talvez ainda não tenha adquirido conhecimento suficiente para tirar algumas conclusões corretas sobre aspectos que ainda não tinha visto, mas a partir de informações que você já possui.

Eu nunca iria saber que um ponteiro int não vai apontar para o valor atribuído diretamente! Visto que um ponteiro char não tem esse comportamento!


Acho que já expliquei acima, mas reiterando de uma outra forma — digamos — mais “bruta” (e com um ponto a mais, para incluir algo que foi explicado pelo SamL na resposta dele):

    • “const char *nome="Paulo";” e “int *ptr=2;” não são análogos, trocando apenas o tipo de dado apontado, porque "Paulo" é um array de caracteres constantes (const char) com seis elementos, e 2 é um constante literal inteira; como não existe uma regra definida sobre o armazenamento em memória de constantes literais, não existe uma maneira de o compilador reconhecer a validade da segunda declaração.

    • A primeira declaração acima é valida porque o array "Paulo" decai automaticamente para ponteiro para o seu primeiro elemento. Por analogia de construção, um ponteiro para inteiros pode receber como valor inicial no momento de sua declaração um array de inteiros que decaia automaticamente para ponteiro para seu primeiro elemento.

    • A forma de obter arrays sem ter de criar uma variável específica é usando a notação de literais compostos (§6.5.2.5 do padrão da linguagem C de 2011), que permite criar arrays anônimos.

    • O análogo à declaração “const char *nome="Paulo";” tendo um ponteiro para inteiros, portanto, seria algo como “int *ptr_int=(int []){80, 97, 117, 108, 111, 0};”. Aliás, um sinônimo da primeira declaração usando a sintaxe de literais compostos poderia ser “const char *nome=(const char []){'P', 'a', 'u', 'l', 'o', '\0'};”.

    • O análogo da declaração errônea “int *ptr_int=2;” seria algo como “char *pc='a';”, também errôneo.

    • A forma de apontar para endereços absolutos na memória, convertendo um valor inteiro diretamente para ponteiro (e.g. “char *p=(char *)5;” ou “int *pi=(int *)10;”) é válida, mas seu uso pode ser limitado, porque não necessariamente qualquer endereço possível de se obter com essa sintaxe vai estar disponível para uso pelo programa.

        • Nos nossos PCs de 64 bits com alguma versão de Linux, UNIX, Windows ou MacOS, provavelmente nossos programinhas vão executar com algum nível de memória virtual, podendo produzir ponteiros com valores de endereços completamente fora dos limites de memória física instalada na máquina. Além disso, em sucessivas execuções do programa, os endereços dos mesmos objetos podem ser diferentes, conforme ilustra o programa abaixo e as saídas de três execuções sucessivas do código compilado (note os valores absurdos dos ponteiros — eu GARANTO que meu micro não possui 128TiB de RAM (nem de HD) para suportar tais endereços, se fossem absolutos — e como os endereços são diferentes em cada execução).
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int i; // Disposto na seção .bss (dados com valor zero).
int j=5; // Disposto na seção .data (dados com valores explicitamente inicializados).
const int k=10; // Disposto na seção .rodata (dados com valores somente para leitura).

#define DISPLAY(x) printf(#x ": %p (@%td, @%#.35gGiB)\n", x, (intptr_t)x, (intptr_t)x/1073741824.0)

int main(void){
int l; // Disposto na memória automática (provavelmente na pilha do processador).
int *pi=malloc(sizeof *pi); // Alocado dinamicamente a partir da memória livre (free store; algumas pessoas chamam de heap) para o programa.
DISPLAY(&i);
DISPLAY(&j);
DISPLAY(&k);
DISPLAY(&l);
DISPLAY(pi);
DISPLAY(main); // Disposta na seção .text do programa
DISPLAY(printf); // Disposta numa biblioteca dinâmica (libc) compartilhada por vários programas.
}
&i: 0x55969aae6018 (@94105328574488, @87642.416893027722835540771484375000GiB)
&j: 0x55969aae6010 (@94105328574480, @87642.416893020272254943847656250000GiB)
&k: 0x55969aae4008 (@94105328566280, @87642.416885383427143096923828125000GiB)
&l: 0x7ffce197e6ac (@140724093314732, @131059.52489630505442619323730468750GiB)
pi: 0x55969c65a2a0 (@94105357361824, @87642.443703323602676391601562500000GiB)
main: 0x55969aae3179 (@94105328562553, @87642.416881912387907505035400390625GiB)
printf: 0x7f4380e7f770 (@139927902222192, @130318.01415811479091644287109375000GiB)
&i: 0x564c0770c018 (@94884542332952, @88368.116256736218929290771484375000GiB)
&j: 0x564c0770c010 (@94884542332944, @88368.116256728768348693847656250000GiB)
&k: 0x564c0770a008 (@94884542324744, @88368.116249091923236846923828125000GiB)
&l: 0x7ffd0299597c (@140724647057788, @131060.04060971364378929138183593750GiB)
pi: 0x564c079992a0 (@94884545008288, @88368.118748337030410766601562500000GiB)
main: 0x564c07709179 (@94884542321017, @88368.116245620884001255035400390625GiB)
printf: 0x7f360052a770 (@139869910378352, @130264.00504480302333831787109375000GiB)
&i: 0x5592edc11018 (@94089542438936, @87627.714908622205257415771484375000GiB)
&j: 0x5592edc11010 (@94089542438928, @87627.714908614754676818847656250000GiB)
&k: 0x5592edc0f008 (@94089542430728, @87627.714900977909564971923828125000GiB)
&l: 0x7ffdcdfa849c (@140728059200668, @131063.21841540560126304626464843750GiB)
pi: 0x5592eeae52a0 (@94089557988000, @87627.729389816522598266601562500000GiB)
main: 0x5592edc0e179 (@94089542427001, @87627.714897506870329380035400390625GiB)
printf: 0x7ff3fe058770 (@140685915555696, @131023.96908746659755706787109375000GiB)

        • Mesmo que você esteja programando mais próximo do acesso direto ao hardware (por exemplo, fazendo programação de dispositivos embarcados, ou num PC antigo rodando MS-DOS, ou mesmo um MSX ou Apple II da vida), nem sempre qualquer endereço possível vai estar disponível para acesso (por exemplo: a máquina tem menos RAM do que o processador é capaz de endereçar, ou o endereço que você gerou para tentar fazer um acesso de escrita reside numa área de ROM, em vez de residir em RAM), ou mesmo um determinado endereço de memória pode estar mapeando um dispositivo ou soft swicth, cujo acesso direto pode causar efeitos indesejados se feito sem o devido cuidado.

O que também soou curioso é que se posso apontar um ponteiro diretamente para um local na memória, significa que eu poderia imprimir a memória! Um assunto que me interessa estudar no futuro!


Não exatamente vai funcionar assim, conforme mostra o último programa de exemplo mostrado acima. Nos nossos PCs existe um sistema operacional se interpondo entre a memória física e os programas que nós, “reles mortais”, executamos. Para examinar o conteúdo da memória do seu PC, você teria de criar um executável que não usasse um sistema operacionai (como costumava fazer, por exemplo, o Memtest86+, que era usado justamente para testar a memória física, mas que era carregado no momento do boot em lugar dos nossos SOs do dia-a-dia), ou você teria de solicitar ao SO que fizesse o acesso para você, o que implica ter credenciais certas e usar APIs específicas de cada SO.

No Linux, creio que o jeito mais básico de se fazer algo assim é através de mapeamento em memória de seções do arquivo virtual /dev/mem. Esta postagem no Stack Overflow pode lhe interessar: https://stackoverflow.com/questions/12040303/how-to-access-physical-addresses-from-user-space-in-lin....


... 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 ponteiro int [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 24/01/2023 - 20:50h

Uma das vantagens de ser iniciante é que tu acaba quebrando as regras, o que normalmente não acontece com alguém experiente, e isso é ótimo, pois ajuda na evolução da linguagem.

//int *Ptr_Int = 2; // Não  Funciona | error: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
int *Ptr_Int = (int*)2; // Descobri que assim funciona! MAS, assim não consigo imprimir na tela!

Eu diria que é o seguinte:
int *ptr = 2, aqui vc tá atribuindo o endereço 2 ao ptr.
Isso é a grosso modo, apontar pra área da RAM que começa em 2, e então, nada garante que não esteja dentro dos limites do programa. O que na verdade é um ponteiro "selvagem".
int *ptr = (int *)2;
Aqui é o mesmo caso do de cima ali. Vc está convertendo o valor 2 num valor (endereço) 2, e ai a mesma situação de não saber pra onde está apotando nem lendo nem escrevendo, fica indefinido o comportamento.

//int *Ptr_Int = 2; // Não  Funciona | error: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
int *Ptr_Int = (int*)2; // Descobri que assim funciona! MAS, assim não consigo imprimir na tela!
// Deveria imprimir assim, mas não funciona
//printf("%d\n", *Ptr_Int); // error: array subscript 0 is outside array bounds of ‘int[0]’ [-Werror=array-bounds]

Veja o seguinte: como vc aponta pro endereço 2 na RAM, e ali com *Ptr_int vc está lendo fora dos limites de memoria do programa, é óbvio que vai dar falha de segmentação. Basicamente quando não se sabe onde está lendo, é de esperar que dê erro grave no programa, o mesmo vale quando não se sabe onde está escrevendo com ponteiro.

Mas se vc tiver a seguinte situação:
const int * n = (int *)2;
int *Ptr_Int = (int*)n; // Descobri que assim funciona! MAS, assim não consigo imprimir na tela!
// Deveria imprimir assim, mas não funciona
printf("%d\n", *Ptr_Int);

Por que dá a mesma falha de segmentação?
Creio eu que por conta que o n não é uma área de memória definida, ou melhor, reservada para conter o valor 2. E ai caimos no mesmo erro de antes, um ponteiro n que aponta pra área de endereço 2, e um Ptr_int que aponta para n. Só complicou mais e ficou na mesma.

Agora, nessa situação:
int n = 2;
int *Ptr_Int = &n; // atribuie a Ptr_int o endereço de n
printf("%d\n", *Ptr_Int);

Por que funciona?
Porque o n tem uma área de memória reservada e dentro dos limites da memória do programa. Não é o endereço 2, mas um valor préalocado estaticamente que existe dentro do programa. Diferente das outras situações, aqui existe a memória apontada por Ptr_int, enquando que nas outras não.

Agora veja uma variante:
const int *n = (int *)2;//aponta pro endereço 2
const int *Ptr_Int = n; // atribuie a Ptr_int o endereço de n
printf("%d\n", *Ptr_Int);

Por que deu falha de segmentação?
Ora, muito simples, é porque o ponteiro n não aponta pra nenhuma área de memória alocada. O (int*)2 ali, nada mais é que o endereço 2 na RAM.
É por isso que, se vc quiser que seu programa com ponteiros rode sem erro, vc deve apontar pra áreas de memória nos limites da área do programa.

Veja esse exemplo super perigoso:
int v = 2;
const int *n = &v;
iint *Ptr_Int = (n + 2); // desloca o endereço de N mais 2 * sizeof(int) na memória
printf("%d\n", *Ptr_Int);

Por que esse compila e roda sem problema enquanto que os outros não?
Porque o n aponta para v que é um endereço válido dentro da área do programa na RAM, e não para o endereço 2. E ai, como o v está dentro do espaço do programa e o n aponta pra ele, que é uma área de memória válida, então podemos deslocar o Ptr_int um pouco mais a vontade e ainda estaremos numa área válida do programa (o que nem sempre é o caso).

É um exemplo perigoso, mas se vc tentar escrever como *Ptr_int = 2 depois de apontar pra n (colocar o valor 2 na área de memória do Ptr_int), dai ia dar falha de segmentação porque estaria escrevendo numa área não visível para escrita, mas apenas leitura.

Veja agora isso:
//observe que, int *n = {} é apenas um único elemento, enquanto que int n[] = {} aloca previamente um ou mais elementos
int n[] = {2, 4 , 8, 16};
int *Ptr_Int = (n + 2);
printf("%d\n", *Ptr_Int);

Sem compilar e executar o código, qual será o número imprimido no terminal?
Observe que, o n aponta para o endereço do primeiro elemento, enquanto que o Ptr_int aponta para o elemento no endereço n+2, que ainda está dentro da área de memória do vetor n e do programa.

https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!


4. Re: Dúvida ponteiro int [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 27/01/2023 - 08:47h

ApprenticeX escreveu:

Boa Noite a todos!


O SAML já deu uma resposta bem didática. Só quero aqui comentar alguns pontos.

Note que o objetivo de ter variáveis de tipo ponteiro é justamente poder fazer acessos indiretos.

Uma analogia com um relacionamento entre pessoas seria a seguinte: se você tiver uma dúvida e souber que eu sei a resposta, você pode me perguntar diretamente a respeito, que eu vou lhe responder; por outro lado, se você tem uma dúvida e sabe que eu conheço alguém que poderá respondê-la, você pode me perguntar quem eu conheço que poderá dar a resposta correta a essa dúvida, e eu, ao indicar que o SAML é q pessoa que pode elucidar essa dúvida, terei contribuído indiretamente na para a sua solução.

Entendeu a analogia? Em outras palavras, acesso direto é quando você chega à informação diretamente, e acesso indireto é quando você não tem a informação diretamente, mas tem instrução de como pode chegar até a informação.

Tentando declarar um ponteiro int, notei que NINGUÉM declara ponteiro int com valor!
Sempre declaram um int + um ponteiro separado pra apontar pra ele!


Nenhuma das duas frases acima é verdadeira.

É possível, sim, definir um valor de endereço fixo para um ponteiro, e o SAML inclusive lhe mostrou como. De fato, isso não é comum quando você faz programas dentro de um sistema operacional como o Windows ou UNIX, porque geralmente esses sistemas operacionais, além de não carregarem os nossos programas sempre numa posição fixa da memória, ainda proíbem o acesso direto e indiscriminado a endereços absolutos na memória sem a intermediação do sistema operacional. Entretanto, quando você usa a linguagem C para desenvolver o próprio sistema operacional (tanto o UNIX quanto o Windows são escritos majoritariamente em C), ou quando você está desenvolvendo para um hardware dedicado e sem sistema operacional próprio (por exemplo: um microcontrolador embarcado num carro, numa geladeira ou num no-break), então esse tipo de operação se torna mais comum.

Aliás, no tempo do MS-DOS, que era o sistema que a gente usava nos PCs nas décadas de 1980 e 1990 e que não oferecia recursos de proteção de acesso ao hardware, era muito comum realizar operações tais como gravar diretamente na região de memória reservada para o vídeo (0xb8000 a 0xbffff), ou no controlador de teclado (não lembro o endereço) ou mesmo na tabela dos vetores (i.e. ponteiros) de interrupções (0x00000 a 0x003ff). Parte desses recursos possivelmente está obsoleta hoje em dia, mas o que permanece dificilmente está disponível para programadores que não mexam diretamente com o SO.

Já a segunda frase é inadequada porque não é diretamente declarar um inteiro e um ponteiro que o copie. De fato, fazer algo como “int i; int *pi=&i;” e manipular i através de *pi é meio sem sentido mesmo. Só que isso não costuma ser o caso em programas reais, e quando isso aparece na literatura ou num site (incluindo este fórum) é normalmente para ilustrar didaticamente e de modo sucinto o fato de que um ponteiro pode ser usado para manipular indiretamente outra variável.

Os casos em que ponteiros são efetivamente usados em C são principalmente dois:

    • na passagem de argumentos para funções, quer seja para reduzir o volume de dados passados como argumentos, quer para permitir que a função consiga alterar um dado declarado fora dela, e

    • como meios de manipular arrays de modo mais flexível, particularmente quando tais arrays são alocados dinamicamente.


[Preciso de sair agora. Caso ainda haja alguma dúvida, vou tentar responder mais tarde.]


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


5. Re: Dúvida ponteiro int [RESOLVIDO]

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 27/01/2023 - 09:41h

Eu entendi o que o SamL explicou, mas não entendi esse tipo de referência!

Qdo declaro um ponteiro de char, eu estou apontando o ponteiro para o endereço de memória onde está VOL ou seja VOL[0]
char *Ptr = "VOL"; 

Então porque não é a mesma coisa usando int? Eu imaginava que eu estaria apontando o ponteiro para o endereço de memória onde está 2
int *Ptr = 2; 
Mas segundo o que SamL explica não é assim, pois entendi que SamL diz que estou apontando o Ponteiro para o endereço de memória 2 e não para o endereço de memória onde está o valor 2.

A única referência que vi que aponta para o endereço de memória é quando se declara NULL, apontando para um valor Nulo, entendo que Nulo seria 0.
char *Ptr = NULL; 
Essa estranheza é porque já li tanta coisa sobre ponteiros e ou não tive atenção a isso, ou ninguém explica isso em livros ou textos na internet!
Eu nunca iria saber que um ponteiro int não vai apontar para o valor atribuído diretamente! Visto que um ponteiro char não tem esse comportamento!

O que também soou curioso é que se posso apontar um ponteiro diretamente para um local na memória, significa que eu poderia imprimir a memória! Um assunto que me interessa estudar no futuro!

Mas porque esse comportamento tão diferente de ponteiro char e ponteiro int? Fiquei inclusive pensando sobre outros valores que nunca usei, tipo float, double



6. Re: Dúvida ponteiro int [RESOLVIDO]

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 30/01/2023 - 15:44h

paulo1205 escreveu:
$ gcc -O0 -S -c a.c
$ gcc -O0 -S -c b.c
Interessante esse comando, estudarei depois ele com calma!

Tecnicamente falando, NULL não é exatamente igual a zero, porque o tipo dele não é um tipo aritmético comum. O valor de NULL em C é ((void *)0). Pelo fato de ser um void *, não é possível fazer aritmética de ponteiro com esse valor.
Eu já li sobre isso e passou desapercebido, pq mentalmente atribui NULL a 0, vou re-anotar isso com mais atenção, inclusive fiquei pensando que o correto é usar NULL quando estamos nos referindo a NULL e não 0 como vejo ocorrer substituindo o NULL por ser mais bonito visualmente. Darei mais atenção a isso também!

const char *nome="Paulo"; e int *ptr=2; não são análogos, trocando apenas o tipo de dado apontado, porque "Paulo" é um array de caracteres constantes (const char) com seis elementos, e 2 é um constante literal inteira; como não existe uma regra definida sobre o armazenamento em memória de constantes literais, não existe uma maneira de o compilador reconhecer a validade da segunda declaração.

A primeira declaração acima é valida porque o array "Paulo" decai automaticamente para ponteiro para o seu primeiro elemento. Por analogia de construção, um ponteiro para inteiros pode receber como valor inicial no momento de sua declaração um array de inteiros que decaia automaticamente para ponteiro para seu primeiro elemento.

A forma de obter arrays sem ter de criar uma variável específica é usando a notação de literais compostos (§6.5.2.5 do padrão da linguagem C de 2011), que permite criar arrays anônimos.

O análogo à declaração const char *nome="Paulo"; tendo um ponteiro para inteiros, portanto, seria algo como int *ptr_int=(int []){80, 97, 117, 108, 111, 0};. Aliás, um sinônimo da primeira declaração usando a sintaxe de literais compostos poderia ser const char *nome=(const char []){'P', 'a', 'u', 'l', 'o', '\0'};.

O análogo da declaração errônea int *ptr_int=2; seria algo como char *pc='a';, também errôneo.
Eu entendi lá em cima qdo vc explicou, mas essas últimas linhas ficaram melhor, inclusive a analogia ao char, pq isso resultaria possívelmente em uma dúvida futura se eu caísse na declaração de um Ptr de char 'a'. É importante entender exatamente o que estamos fazendo e porque estamos fazendo, eu gosto disso!

Nos nossos PCs existe um sistema operacional se interpondo entre a memória física e os programas que nós, “reles mortais”, executamos. Para examinar o conteúdo da memória do seu PC, você teria de criar um executável que não usasse um sistema operacionai (como costumava fazer, por exemplo, o Memtest86+, que era usado justamente para testar a memória física, mas que era carregado no momento do boot em lugar dos nossos SOs do dia-a-dia), ou você teria de solicitar ao SO que fizesse o acesso para você, o que implica ter credenciais certas e usar APIs específicas de cada SO.
Bastante útil essa informação, eu conheço os programas que citou, e vc mostrou uma camada extra relacionada ao sistema operacional que eu havia esquecido, é que por um momento, mechendo atualmente com malloc, eu já estava achando que eu estava conversando diretamente com a memória do PC, e bastante empolgado com isso, mas pelo visto não é apenas isso e nem é tão simples como pareceu!

No Linux, creio que o jeito mais básico de se fazer algo assim é através de mapeamento em memória de seções do arquivo virtual /dev/mem. Esta postagem no Stack Overflow pode lhe interessar: https://stackoverflow.com/questions/12040303/how-to-access-physical-addresses-from-user-space-in-lin....
Sim me interessou, vou estudar a respeito com calma!








Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts