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)