Última atualização:2026-10-12
Ponteiros e referências em C++¶
Sobre o autor:¶
Conta oficial do WeChat:

1.Referências e ponteiros¶
Visão geral:
| Referência | Ponteiro |
|---|---|
| Deve ser inicializada | Pode não ser inicializado |
| Não pode ser nula | Pode ser nulo |
| Não pode trocar de destino | Pode trocar de destino |
A referência deve ser inicializada, enquanto o ponteiro pode ficar sem inicialização.
Ao definir uma referência, devemos obrigatoriamente lhe atribuir um valor inicial; já o ponteiro não precisa disso.
A referência não pode ser nula, enquanto o ponteiro pode.
Como a referência não pode ser nula, não precisamos testar sua validade ao usá-la; já com o ponteiro, é preciso primeiro verificar se ele é um ponteiro nulo, caso contrário o programa pode travar.
void test_p(int* p)
{
if(p != null_ptr) //对p所指对象赋值时需先判断p是否为空指针
*p = 3;
return;
}
void test_r(int& r)
{
r = 3; //由于引用不能为空,所以此处无需判断r的有效性就可以对r直接赋值
return;
}
A referência não pode trocar de destino
O ponteiro pode mudar de alvo a qualquer momento, mas a referência só pode apontar para o objeto indicado na inicialização, sem possibilidade de mudança.
int a = 1;
int b = 2;
int &r = a; //初始化引用r指向变量a
int *p = &a; //初始化指针p指向变量a
p = &b; //指针p指向了变量b
r = b; //引用r依然指向a,但a的值变成了b
2.Referências¶
Referências lvalue¶
A referência comum, que em geral representa a identidade de um objeto.
Referências rvalue¶
Uma referência rvalue é uma referência que deve ser vinculada a um rvalue (um objeto temporário, um objeto prestes a ser destruído); em geral, representa o valor de um objeto.
As referências rvalue permitem implementar semântica de movimentação (Move Semantics) e repasse exato (Perfect Forwarding); seus principais objetivos são dois:
- Eliminar cópias desnecessárias de objetos na interação entre dois objetos, economizando recursos de computação e armazenamento e melhorando a eficiência.
- Permitir definir funções genéricas de forma mais concisa e clara.
Colapso de referências (reference collapsing)¶
X& &,X& &&,X&& &podem ser colapsados emX&X&& &&pode ser colapsado emX&&
As referências do C++ reduzem a liberdade do programador, mas em contrapartida aumentam a segurança das operações de memória e a elegância da semântica. Por exemplo, a exigência de inicialização obrigatória da referência nos dispensa de verificar se ela é nula no momento do uso, tornando o código mais conciso e elegante e evitando ponteiros espalhados por toda parte. Além desse cenário, as referências também são usadas nos dois cenários a seguir:
Parâmetros por referência
Em geral, usamos um parâmetro const reference como parâmetro formal somente leitura; nesse caso, evitamos a cópia do parâmetro e ainda obtemos uma forma de chamada igual à da passagem por valor.
void test(const vector<int> &data)
{
//...
}
int main()
{
vector<int> data{1,2,3,4,5,6,7,8};
test(data);
}
Valores de retorno por referência
O C++ oferece o recurso de sobrecarga de operadores; ao sobrecarregar certos operadores, usar um valor de retorno por referência permite manter a mesma sintaxe de chamada que o operador tinha originalmente, preservando a consistência da semântica do operador. Um exemplo é o operador operator [], que em geral precisa retornar um objeto por referência para poder ser modificado corretamente.
vector<int> v(10);
v[5] = 10; //[]操作符返回引用,然后vector对应元素才能被修改
//如果[]操作符不返回引用而是指针的话,赋值语句则需要这样写
*v[5] = 10; //这种书写方式,完全不符合我们对[]调用的认知,容易产生误解
3.Diferença de desempenho entre ponteiros e referências¶
Haveria diferença de desempenho entre ponteiros e referências? Para uma pergunta assim, é preciso descer ao nível do assembly. Primeiro, escrevamos uma função test1, com passagem de parâmetro por ponteiro:
O código assembly correspondente a esse trecho é o seguinte:
(gdb) disassemble
Dump of assembler code for function test1(int*):
0x0000000000400886 <+0>: push %rbp
0x0000000000400887 <+1>: mov %rsp,%rbp
0x000000000040088a <+4>: mov %rdi,-0x8(%rbp)
=> 0x000000000040088e <+8>: mov -0x8(%rbp),%rax
0x0000000000400892 <+12>: movl $0x3,(%rax)
0x0000000000400898 <+18>: nop
0x0000000000400899 <+19>: pop %rbp
0x000000000040089a <+20>: retq
End of assembler dump.
No código acima, as linhas 1 e 2 são a preservação de contexto da chamada; a linha 3 é a passagem do parâmetro — o primeiro parâmetro de uma chamada de função normalmente fica no registrador rdi, e essa linha grava o valor do registrador rdi (o valor do ponteiro p) na pilha; a linha 4 grava o valor de p da pilha no registrador rax; a linha 5 grava o valor imediato 3 na memória apontada pelo valor do registrador rax — note os parênteses em (%rax): eles não são opcionais; (%rax) e %rax têm significados completamente distintos: (%rax) representa a memória cujo endereço é o valor contido no registrador rax, equivalente ao *p do código C++, enquanto %rax representa o registrador rax, equivalente ao valor de p no código C++; por isso o assembly usa (%rax), e não %rax.
Escrevamos agora o trecho de código C++ test2, com passagem de parâmetro por referência:
O código assembly correspondente a este código é o seguinte:
(gdb) disassemble
Dump of assembler code for function test2(int&):
0x000000000040089b <+0>: push %rbp
0x000000000040089c <+1>: mov %rsp,%rbp
0x000000000040089f <+4>: mov %rdi,-0x8(%rbp)
=> 0x00000000004008a3 <+8>: mov -0x8(%rbp),%rax
0x00000000004008a7 <+12>: movl $0x3,(%rax)
0x00000000004008ad <+18>: nop
0x00000000004008ae <+19>: pop %rbp
0x00000000004008af <+20>: retq
End of assembler dump.
Descobrimos que o código assembly correspondente a test2 é exatamente igual ao de test1, o que mostra que o compilador C++ compila ponteiros e referências exatamente no mesmo código de máquina. Portanto, a referência em C++ é apenas um "açúcar sintático" do C++ para a operação com ponteiros: na implementação de baixo nível, o compilador C++ implementa as duas operações de forma idêntica.
3.Resumo¶
O C++ introduziu a operação de referência e, impondo mais restrições ao seu uso, garante segurança e conveniência, além de preservar a elegância do código. Usando a operação adequada em cada situação, o uso de referências pode, em certa medida, evitar a situação de "ponteiros voando por toda parte", com significado positivo também para a estabilidade do programa. Por fim, ponteiros e referências têm implementações idênticas em baixo nível: não é preciso se preocupar com diferença de desempenho entre os dois.
Parte do acima foi retirada de: http://irootlee.com/juicer_pointer_reference/#
Tópicos relacionados¶
- Funções virtuais puras e classes abstratas em C++ | C++那些事
- Asserções com assert em C++ | C++那些事
- Bit fields em C++ | C++那些事
- Implementando polimorfismo de C++ em linguagem C | C++那些事
- Constantes e modificadores com const em C++ | C++那些事
- A família completa do const em C++ | C++那些事
- O operador de desreferência * em C++ | C++那些事
- Inicialização e atribuição em C++ | C++那些事
- Formas de retornar múltiplos valores em C++ | C++那些事
- A palavra-chave mutable em C++ | C++那些事