[Powered by Google Translate] [Punteiros]
[Rob Bowden] [Harvard University]
[Esta é CS50] [CS50.TV]
>> Imos falar sobre punteiros.
Ata agora, sempre referida só as cousas na memoria explicitamente polo nome.
Nós dixemos int n = 42, e entón, cando queremos utilizar a variable n,
Nós só chamalo polo nome que damos por escribir algo así como n * 2.
Pero esa variable debe vivir en algún lugar na memoria.
Cando se quere usar o valor que está almacenado dentro de n,
ou actualizar o valor que n está seguro,
o programa precisa saber onde na memoria de ollar para n.
Onde na memoria de vidas variable é chamado de enderezo.
É como un enderezo de casa.
Podo atopar a casa de alguén, sempre que eu saiba o seu enderezo de casa,
e un programa de ordenador pode atopar unha variable, xa que el coñece o seu enderezo de memoria.
What punteiros ofrecen é un xeito de tratar directamente con estes enderezos de memoria.
>> Unha morea de poder en C vén de ser capaz de manipular a memoria como este.
Pero con gran poder vén gran responsabilidade.
Punteiros poden ser usados ​​perigosamente o suficiente para que unha gran cantidade de linguaxes de programación
pointers ocultar completamente.
Entón, por que dar C pointers entón?
Como vostede sabe, argumentos para unha función C
sempre son copiados para os parámetros da función.
Entón, chamando algo troco dalgunhas variables x e y
non pode cambiar os valores de x e y na función de chamada,
a pesar de que pode ser útil.
Como veremos máis tarde intercambio, reescribir a tomar punteiros para os lugares que precisan ser trocados
permite que afectan variables seu chamador.
>> Imos examinar un exemplo relativamente simple que os punteiros poden facer.
Imos dicir que temos int n = 4, e int * pointer_to_n = n.
Guau! Un pouco de nova sintaxe para cubrir.
En primeiro lugar, imos interpretar isto e n.
Lembre que todo na memoria ten algún enderezo.
O comercial chámase "enderezo de" operador.
Entón, e n refírese ao enderezo de memoria onde n é almacenado.
Agora, estamos mantendo ese enderezo nunha nova variable, pointer_to_n.
Cal é o tipo desta nova variable?
O asterisco é unha parte do tipo da variable,
e leremos tipo int *.
* Int significa que pointer_to_n é unha variable que almacena o enderezo de un número enteiro.
Sabemos que & n é un int * sempre que n é un enteiro, e nós estamos levando o enderezo de n.
Int * é un exemplo dun tipo de punteiro.
Así que comezar a ver asteriscos no tipo,
vostede sabe que está lidando con punteiros.
Así como podemos declarar unha variable como int x e y char,
podemos dicir int * Z e char * w.
Int * e * char son só novos tipos de usarmos.
A localización do * pode ir a calquera lugar antes do nome da variable.
Así, tanto int * pointer_to_n - co seguinte * para int, como temos aquí -
e int * pointer_to_n co * xunto pointer_to_n son válidos.
Pero aquí, eu vou poñer o * próximo ao int.
Non importa o que prefire, pode ser coherente.
>> Imos deseñar un diagrama para iso.
Primeiro temos a variable n, que imos deseñar como unha pequena caixa de memoria.
Para este exemplo, imos dicir que esa caixa está situada no enderezo 100.
Dentro desta caixa, que está almacenando o valor 4.
Agora, temos unha nova variable, pointer_to_n.
El ten a súa propia caixa na memoria,
o que imos dicir é o enderezo 200.
Dentro desta caixa, estamos mantendo o enderezo de n,
que xa dixemos antes era 100.
Frecuentemente en diagramas, podes ver esta mostra como unha frecha literal
deixando a caixa pointer_to_n apuntando para a caixa que garda n.
Agora, o que pode realmente facer pointer_to_n?
Ben, se dicimos algo * pointer_to_n = 8, este é un uso diferente para o asterisco
que é totalmente separado do uso do asterisco en declarar unha variable
dun tipo de punteiro.
Aquí, o asterisco chámase operador dereference.
No noso diagrama, o que * pointer_to_n = 8 significa,
ir ao pointer_to_n caixa contendo, siga a frecha,
e despois asignar a caixa na parte final da frecha valor 8.
Isto significa que tras esa liña, se tentar empregar n terá o valor 8.
'Punteiro' A palabra é usada en moitos contextos diferentes.
Aquí, imos tratar de ser coherente.
Un tipo de punteiro é algo así como * int.
Neste vídeo, un punteiro só se usa para referirse a un valor cun tipo de punteiro,
como pointer_to_n que * tipo int.
Calquera lugar que adoitaba dicir n, podemos agora dicir, en vez pointer_to_n *,
e todo vai funcionar tan ben.
>> Imos examinar un exemplo simple.
Imos dicir que temos int n = 14; int * punteiro = &N; n + +, e (* punteiro) + +.
A primeira liña crea unha nova caixa na memoria rotulado n.
Esta vez non imos etiquetar a caixa con un enderezo explícito, pero aínda ten un.
Dentro da caixa, que está almacenando o número 14.
A liña seguinte crea un punteiro segunda caixa rotulada.
E dentro desta caixa, que está almacenando un punteiro para a caixa n.
Entón, imos deseñar a frecha de punteiro para n.
Agora n + + incrementa o valor na caixa rotulada n,
así que imos 14-15.
Finalmente, (* punteiro) + + vai o punteiro caixa rotulada
dereferences o valor na caixa, o que significa que a seguir a frecha que apunta,
e incrementa o valor almacenado alí, entón imos 15-16.
E é iso.
N agora almacena o número 16, despois de ser incrementado dúas veces -
unha vez directamente usando os nomes de variables n, ea outra a través dun pointer_to_n.
>> Test rápido. ¿Que pensas que iso significa que se eu tentar dicir algo así como && n?
Ben, imos reescribir isto como & (& n), que realiza a mesma cousa.
O (+ n) retorna o enderezo da variable n na memoria.
Pero, entón, a continuación, comercial externa intenta volver ao enderezo do enderezo.
Isto é como tentar facer e 2.
Non ten sentido para obter o enderezo de un número un pouco
unha vez que non está sendo almacenado en memoria.
Usando dous e comercial nunha liña non é a idea correcta.
Pero agora, o que significa que eu tentar dicir int ** double_pointer = punteiro?
Agora, eu estou creando unha nova caixa rotulada double_pointer,
e dentro da caixa que eu estou garda o enderezo do punteiro,
que significa que eu debuxe unha frecha da caixa double_pointer á caixa de punteiro.
Teña en conta o tipo de double_pointer, un ** int.
N era un número enteiro de punteiro, almacenado o enderezo de n, e por iso ten * int tipo.
Agora, double_pointer almacena o enderezo do punteiro, por iso ten o tipo int. **
>> Entón, o que nós pensamos que iso significa -
Double_pointer ** = 23?
Repare que eu estou agora dereferencing dúas veces.
Só ten que seguir o diagrama de caixa e-flecha que xa configurada.
En primeiro lugar, imos á caixa double_pointer.
O * primeiro significa seguir a frecha dunha vez.
Agora, estamos no punteiro caixa rotulada.
A segunda estrela di seguir a frecha de novo,
e agora estamos no cadro chamada n, e imos definir o valor desta caixa para 23.
Teña en conta que o "enderezo de" dereference e operadores son inversas unha da outra.
Tanto me permite facer algo como * & * & n = 42.
Mentres tanto funciona, vostede non debe facer algo así na práctica.
O que estamos realmente facendo aquí?
O primeiro e comercial agarra o enderezo da variable n.
Entón, temos un operador dereference, o que significa que estamos indo ao enderezo na memoria,
por iso estamos de volta ao n.
Agora, pegamos o enderezo do n novo e inmediatamente dereference,
por iso estamos de volta ao n e tenda 42.
Así, cada par de * & só cancela.
>> Hai un punteiro especial chamado punteiro nulo.
Este é un punteiro que nunca debemos dereference.
Este punteiro é importante porque nos dá un xeito de distinguir entre
un punteiro que debe e non debe ser dereferenced.
Se tentar eliminar a referencia dun punteiro nulo,
normalmente o seu programa ha fallar con un fallo de segmento,
que pode ter visto antes.
Entón, imos dicir que temos o código int * x = null; * x = 4.
Neste exemplo, pode parecer obvio que estamos facendo algo mal,
pero lembre que nulo podería realmente ser un valor retornado dunha chamada a unha función
como malloc, se malloc non pode reservar memoria solicitada polo usuario.
Por esta razón, en vez diso, estableza o valor de x de unha chamada a malloc,
como int * x = malloc (sizeof (int)), entón debemos sempre comprobar explicitamente
a ver se nulo foi devolto.
If (x == null) / / uhoh! retorno; máis que poidamos seguir e dicir * x = 4.
>> Entón, de novo, por que temos que usar punteiros?
Vexamos un exemplo dun programa que temos de usar punteiros -
unha función de cambio simple.
Imos dicir que eu teño dous enteiros, int x = 4, e int y = 15;
e quero escribir unha función chamada de intercambio que podo usar así: intercambio (x, y).
Tras esta liña, os valores dentro da variable x debe ser de 15,
é o valor de y no interior variable debe ser 4.
Os valores dentro de x e y foron trocados.
Sen punteiros, podemos tentar algo void intercambio (int a, int b);
int tmp = b, b = a, a = tmp.
Pero, entender o problema con iso?
Lembrar que o valor almacenado na variable a é só unha copia do valor de x,
é o valor de b é copiado a partir de y.
Calquera cambio feitas nun b e non será reflectida en x e y.
Así, mentres que os valores de a e b son correctamente trocados,
x e y non cambiou nada.
Agora, imos cambiar a función de intercambio para que os seus argumentos
son punteiros para as variables que debe cambiar, así:
void intercambio (int * a, int * b); int tmp = * b; * b * = a; * a = tmp.
Lembre que os argumentos swaps agora son punteiros,
e por iso temos que pasar o enderezo de X e Y na chamada para intercambiar, así:
intercambio (e x, e y).
Iso agora correctamente swaps os valores de x e y.
>> Imos deseñar un diagrama de caixa e-flecha para ver por que isto funciona.
Comezamos coas dúas caixas na memoria, x e y.
Dentro do cadro de x é o número 4, e no interior da caixa para y temos 15.
Agora, dentro da chamada á función de intercambio, temos dúas caixas
para os argumentos a e b;
a apunta para o cadro para x, e os puntos de B para a caixa para y.
Unha nova caixa é creado para o tmp variable,
e dentro del nós gardados resultado de dereferencing b,
que significa "siga a frecha da caixa rotulada b '.
Entón, nós almacenar 15 dentro tmp.
Entón, siga a frecha en B e almacenar aquí o resultado dunha dereferencing,
que é o valor 4.
Finalmente, segue a frecha nunha tenda e que está actualmente dentro tmp, que é 15.
Nótese que as caixas marcadas x e y teñen valores correctamente trocados.
>> Unha vez que nós aprenden sobre malloc e xestión dinámica de memoria,
imos ver que non temos elección a non ser usar punteiros.
Camiñando polo diagrama de caixa e-flecha para calquera programa
pode axudar a descubrir o que o programa está realmente facendo.
>> O meu nome é Rob Bowden, e este é o CS50.
[CS50.TV]
>> Este é un uso diferente para o asterisco - bleah, eu odio esa palabra.
Calquera lugar que adoitaba dicir n, podemos agora dicir pointer_to_n - non non pode - pointer_to_n *.