Por favor, inclua um exemplo com a explicação.
int *p;
definiria um ponteiro para um número inteiro e *p
desdiferenciaria esse ponteiro, o que significa que ele realmente recuperaria os dados para os quais p aponta.
Por favor, inclua um exemplo com a explicação.
int *p;
definiria um ponteiro para um número inteiro e *p
desdiferenciaria esse ponteiro, o que significa que ele realmente recuperaria os dados para os quais p aponta.
Respostas:
É geralmente bom o suficiente - a menos que você está programando montagem - prever um ponteiro contém um endereço de memória numérica, com 1 referente ao segundo byte na memória do processo, 2 o terceiro, 3 a quarta e assim por diante ....
Quando você deseja acessar os dados / valor na memória para a qual o ponteiro aponta - o conteúdo do endereço com esse índice numérico -, desrefere o ponteiro.
Linguagens de computador diferentes têm notações diferentes para dizer ao compilador ou intérprete que você agora está interessado no valor (atual) do objeto apontado - eu me concentro abaixo em C e C ++.
Considere em C, dado um ponteiro como p
abaixo ...
const char* p = "abc";
... quatro bytes com os valores numéricos usados para codificar as letras 'a', 'b', 'c' e um byte 0 para indicar o final dos dados textuais, são armazenados em algum lugar na memória e o endereço numérico desse os dados são armazenados em p
. Dessa forma, C codifica texto na memória é conhecido como ASCIIZ .
Por exemplo, se a literal da string estivesse no endereço 0x1000 e p
um ponteiro de 32 bits em 0x2000, o conteúdo da memória seria:
Memory Address (hex) Variable name Contents
1000 'a' == 97 (ASCII)
1001 'b' == 98
1002 'c' == 99
1003 0
...
2000-2003 p 1000 hex
Note-se que não há uma variável nome / identificador para o endereço 0x1000, mas podemos indiretamente consulte a string literal usando um ponteiro armazenar o endereço: p
.
Para nos referir aos caracteres p
aos quais apontamos, fazemos a desreferência p
usando uma dessas notações (novamente, para C):
assert(*p == 'a'); // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
// p and 1 times the size of the things to which p points:
// In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b'); // Another notation for p[1]
Você também pode mover os ponteiros pelos dados apontados, desmarcando-os à medida que avança:
++p; // Increment p so it's now 0x1001
assert(*p == 'b'); // p == 0x1001 which is where the 'b' is...
Se você tiver alguns dados que podem ser gravados, poderá fazer coisas como estas:
int x = 2;
int* p_x = &x; // Put the address of the x variable into the pointer p_x
*p_x = 4; // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4
Acima, você deve saber em tempo de compilação que precisaria de uma variável chamada x
, e o código solicita ao compilador que organize onde deve ser armazenado, garantindo que o endereço esteja disponível via &x
.
Em C, se você tiver uma variável que é um ponteiro para uma estrutura com membros de dados, poderá acessar esses membros usando o ->
operador de desreferenciação:
typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159; // Dereference and access data member x.d_
(*p).d_ *= -1; // Another equivalent notation for accessing x.d_
Para usar um ponteiro, um programa de computador também precisa de algumas dicas sobre o tipo de dados que está sendo apontado - se esse tipo de dado precisar de mais de um byte para representar, o ponteiro normalmente apontará para o byte de número mais baixo dos dados.
Então, analisando um exemplo um pouco mais complexo:
double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3); // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4); // Actually looks at bytes from address p + 1 * sizeof(double)
// (sizeof(double) is almost always eight bytes)
++p; // Advance p by sizeof(double)
assert(*p == 13.4); // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8; // Change sizes[3] from 19.4 to 29.8
// Note earlier ++p and + 2 here => sizes[3]
Às vezes, você não sabe quanta memória precisará até que seu programa esteja em execução e veja quais dados são lançados nele ... então você pode alocar dinamicamente a memória usando malloc
. É prática comum armazenar o endereço em um ponteiro ...
int* p = (int*)malloc(sizeof(int)); // Get some memory somewhere...
*p = 10; // Dereference the pointer to the memory, then write a value in
fn(*p); // Call a function, passing it the value at address p
(*p) += 3; // Change the value, adding 3 to it
free(p); // Release the memory back to the heap allocation library
Em C ++, a alocação de memória é normalmente feita com o new
operador e a desalocação com delete
:
int* p = new int(10); // Memory for one int with initial value 10
delete p;
p = new int[10]; // Memory for ten ints with unspecified initial value
delete[] p;
p = new int[10](); // Memory for ten ints that are value initialised (to 0)
delete[] p;
Veja também ponteiros inteligentes em C ++ abaixo.
Freqüentemente, um ponteiro pode ser a única indicação de onde existem alguns dados ou buffer na memória. Se for necessário o uso contínuo desses dados / buffer, ou a capacidade de chamar free()
ou delete
evitar vazamento de memória, o programador deverá operar em uma cópia do ponteiro ...
const char* p = asprintf("name: %s", name); // Common but non-Standard printf-on-heap
// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
if (!isprint(*q))
*q = '_';
printf("%s\n", p); // Only q was modified
free(p);
... ou orquestrar cuidadosamente a reversão de quaisquer alterações ...
const size_t n = ...;
p += n;
...
p -= n; // Restore earlier value...
free(p);
No C ++, é uma prática recomendada usar objetos de ponteiro inteligente para armazenar e gerenciar os ponteiros, desalocando-os automaticamente quando os destruidores dos ponteiros inteligentes são executados. Desde o C ++ 11, a Biblioteca Padrão fornece dois, unique_ptr
para quando houver um único proprietário para um objeto alocado ...
{
std::unique_ptr<T> p{new T(42, "meaning")};
call_a_function(p);
// The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete
... e shared_ptr
pela propriedade das ações (usando a contagem de referência ) ...
{
auto p = std::make_shared<T>(3.14, "pi");
number_storage1.may_add(p); // Might copy p into its container
number_storage2.may_add(p); // Might copy p into its container } // p's destructor will only delete the T if neither may_add copied it
Em C NULL
e 0
- e adicionalmente em C ++ nullptr
- pode ser usado para indicar que um ponteiro não contém atualmente o endereço de memória de uma variável e não deve ser desreferenciado ou usado na aritmética de ponteiros. Por exemplo:
const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
int c;
while ((c = getopt(argc, argv, "f:")) != -1)
switch (c) {
case f: p_filename = optarg; break;
}
if (p_filename) // Only NULL converts to false
... // Only get here if -f flag specified
Em C e C ++, assim como os tipos numéricos incorporados não necessariamente são padronizados para 0
, nem bools
para false
, nem sempre os ponteiros são definidos como NULL
. Tudo isso é definido como 0 / false / NULL quando são static
variáveis ou (apenas C ++) variáveis de membro diretas ou indiretas de objetos estáticos ou suas bases, ou sofrem inicialização zero (por exemplo, new T();
e new T(x, y, z);
executam inicialização zero nos membros de T, incluindo ponteiros, enquanto new T;
não).
Além disso, quando você atribuir 0
, NULL
e nullptr
para um ponteiro os bits no ponteiro não são necessariamente todos de reset: o ponteiro não pode conter "0" ao nível do hardware, ou consulte o endereço 0 no seu espaço de endereço virtual. O compilador é permitido para armazenar outra coisa lá se tiver motivos para, mas o que ele faz - se você vir e comparar o ponteiro para 0
, NULL
, nullptr
ou outro ponteiro que foi atribuído qualquer um desses, o trabalho de comparação obrigação como esperado. Portanto, abaixo do código-fonte no nível do compilador, "NULL" é potencialmente um pouco "mágico" nas linguagens C e C ++ ...
Mais estritamente, os ponteiros inicializados armazenam um padrão de bits que identifica um NULL
ou um endereço de memória (geralmente virtual ).
O caso simples é onde esse é um deslocamento numérico em todo o espaço de endereço virtual do processo; em casos mais complexos, o ponteiro pode ser relativo a alguma área específica da memória, que a CPU pode selecionar com base nos registros de "segmento" da CPU ou em algum tipo de ID de segmento codificado no padrão de bits e / ou procurando em lugares diferentes, dependendo do instruções de código de máquina usando o endereço.
Por exemplo, uma int*
inicialização adequada para apontar para uma int
variável pode - após converter para float*
- a memória de acesso na memória "GPU" bastante distinta da memória em que a int
variável está; depois, uma vez convertida e usada como ponteiro de função, ela pode apontar para opcodes de máquina de retenção de memória distintos para o programa (com o valor numérico do int*
efetivamente um ponteiro aleatório e inválido nessas outras regiões de memória).
Linguagens de programação 3GL como C e C ++ tendem a esconder essa complexidade, de modo que:
Se o compilador fornecer um ponteiro para uma variável ou função, você pode desdiferenciá-lo livremente (desde que a variável não seja destruída / desalocada) e é problema do compilador se, por exemplo, um determinado registro de segmento de CPU precisa ser restaurado antes, ou um instrução de código de máquina distinta usada
Se você obtiver um ponteiro para um elemento em uma matriz, poderá usar a aritmética do ponteiro para mover-se para qualquer outro lugar na matriz, ou mesmo para formar um endereço que seja legal para comparar com outros ponteiros para elementos na matriz (ou que foram movidos de maneira semelhante pela aritmética do ponteiro para o mesmo valor de um passado-o-final); novamente em C e C ++, cabe ao compilador garantir que isso "funcione"
Funções específicas do SO, por exemplo, mapeamento de memória compartilhada, podem fornecer ponteiros e eles "simplesmente funcionam" dentro do intervalo de endereços que faz sentido para eles
As tentativas de mover ponteiros legais além desses limites, ou converter números arbitrários em ponteiros ou usar ponteiros convertidos para tipos não relacionados, geralmente têm um comportamento indefinido , portanto devem ser evitados em bibliotecas e aplicativos de nível superior, mas codificam para sistemas operacionais, drivers de dispositivo, etc. talvez precise confiar em comportamentos deixados indefinidos pelo padrão C ou C ++, que, no entanto, são bem definidos por sua implementação ou hardware específico.
p[1]
e *(p + 1)
idêntico ? Ou seja, gera p[1]
e *(p + 1)
gera as mesmas instruções?
p
é apenas 2000: se você tivesse outro ponteiro p
, teria que armazenar 2000 em seus quatro ou oito bytes. Espero que ajude! Felicidades.
u
contiver uma matriz arr
, o gcc e o clang reconhecerão que o lvalue u.arr[i]
pode acessar o mesmo armazenamento que outros membros da união, mas não reconhecerão que o lvalue *(u.arr+i)
pode fazê-lo. Não tenho certeza se os autores desses compiladores pensam que o último chama UB, ou que o primeiro chama UB, mas eles devem processá-lo de qualquer maneira, mas eles claramente veem as duas expressões como diferentes.
Desreferenciar um ponteiro significa obter o valor armazenado no local da memória apontado pelo ponteiro. O operador * é usado para fazer isso e é chamado de operador de desreferenciação.
int a = 10;
int* ptr = &a;
printf("%d", *ptr); // With *ptr I'm dereferencing the pointer.
// Which means, I am asking the value pointed at by the pointer.
// ptr is pointing to the location in memory of the variable a.
// In a's location, we have 10. So, dereferencing gives this value.
// Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.
*ptr = 20; // Now a's content is no longer 10, and has been modified to 20.
[]
também desreferencia um ponteiro ( a[b]
está definido para significar *(a + b)
).
Um ponteiro é uma "referência" a um valor .. bem como um número de chamada de biblioteca é uma referência a um livro. "Desreferenciando" o número de chamada está passando e recuperando fisicamente esse livro.
int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;
// The * causes pA to DEREFERENCE... `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4..
Se o livro não estiver lá, o bibliotecário começa a gritar, fecha a biblioteca e duas pessoas estão prontas para investigar a causa de uma pessoa encontrar um livro que não está lá.
Em palavras simples, desreferenciar significa acessar o valor de um determinado local de memória contra o qual esse ponteiro está apontando.
Código e explicação do básico do ponteiro :
A operação de desreferência começa no ponteiro e segue a seta para acessar o ponteiro. O objetivo pode ser olhar para o estado de ponta ou alterar o estado de ponta. A operação de desreferência em um ponteiro funciona apenas se o ponteiro tiver um pontapé - o ponteiro deve ser alocado e o ponteiro deve estar definido para apontar para ele. O erro mais comum no código do ponteiro é esquecer de configurar o ponta. O travamento de tempo de execução mais comum devido a esse erro no código é uma falha na operação de desreferência. Em Java, a desreferência incorreta será sinalizada educadamente pelo sistema de tempo de execução. Em linguagens compiladas como C, C ++ e Pascal, a desreferência incorreta às vezes falha e outras vezes corrompem a memória de alguma maneira sutil e aleatória.
void main() {
int* x; // Allocate the pointer x
x = malloc(sizeof(int)); // Allocate an int pointee,
// and set x to point to it
*x = 42; // Dereference x to store 42 in its pointee
}
Acho que todas as respostas anteriores estão erradas, pois afirmam que a desreferenciação significa acessar o valor real. A Wikipedia fornece a definição correta: https://en.wikipedia.org/wiki/Dereference_operator
Ele opera em uma variável de ponteiro e retorna um valor l equivalente ao valor no endereço do ponteiro. Isso é chamado de "desreferenciando" o ponteiro.
Dito isso, podemos desreferenciar o ponteiro sem acessar o valor que ele aponta. Por exemplo:
char *p = NULL;
*p;
Desreferenciamos o ponteiro NULL sem acessar seu valor. Ou poderíamos fazer:
p1 = &(*p);
sz = sizeof(*p);
Novamente, desreferenciando, mas nunca acessando o valor. Esse código NÃO trava: a trava acontece quando você realmente acessa os dados por um ponteiro inválido. No entanto, infelizmente, de acordo com o padrão, desreferenciar um ponteiro inválido é um comportamento indefinido (com algumas exceções), mesmo que você não tente tocar nos dados reais.
Então, resumindo: desreferenciar o ponteiro significa aplicar o operador de desreferência. Esse operador apenas retorna um valor l para seu uso futuro.
*p;
causa comportamento indefinido. Embora você está certo de que dereferencing não acessa o valor per se , o código *p;
faz o acesso ao valor.