que - ¿Los punteros se consideran un método de llamada por referencia en C?
punteros c++ (8)
En la clase de programación C de mi universidad, la profesora y el libro posterior escrito por ella usan el término llamar o pasar por referencia cuando se refieren a punteros en C.
Un ejemplo de lo que mi profesor considera una ''llamada por función de referencia'':
int sum(int *a, int *b);
Un ejemplo de lo que mi profesor considera una ''función de llamada por valor'':
int sum(int a, int b);
He read C no admite llamadas por referencia. A mi entender, los punteros pasan por valor .
Básicamente, ¿es incorrecto decir que los punteros son la forma en que C pasa por referencia? ¿Sería más correcto decir que no puede pasar por referencia en C pero que puede usar punteros como alternativa?
Actualización 11/11/15
Por la forma en que se originó mi pregunta, creo que ha surgido un debate de terminología y, de hecho, estoy viendo dos distinciones específicas.
- paso por referencia (el término utilizado principalmente hoy): el término específico como se usa en lenguajes como C ++
- Pasar por referencia (el término utilizado por mi profesor como paradigma para explicar los punteros): el término general utilizado antes de que se desarrollaran lenguajes como C ++ y, por lo tanto, antes de que el término fuera reescrito
Después de leer la respuesta actualizada de @Haris, tiene sentido por qué esto no es tan blanco y negro.
"Pasar por referencia" es un concepto. Sí, está pasando el valor de un puntero a la función, pero en ese caso el valor de ese puntero se está utilizando para hacer referencia a la variable.
Alguien más usó una analogía de destornillador para explicar que es incorrecto referirse al paso de punteros como pases por referencia, diciendo que puede atornillar un tornillo con una moneda, pero eso no significa que llamaría a la moneda un destornillador. Diría que es una gran analogía, pero llegan a una conclusión equivocada. De hecho, aunque no afirmarías que una moneda era un destornillador, aún dirías que atornillaste el tornillo con ella. es decir, aunque los punteros no son lo mismo que las referencias de c ++, lo que los está utilizando para hacer ES pasar por referencia.
C pasa argumentos por valor, punto. Sin embargo, los punteros son un mecanismo que se puede utilizar para pasar argumentos de manera efectiva por referencia. Al igual que una moneda se puede usar de manera efectiva como un destornillador si tiene el tipo correcto de tornillo: incluso se eligen algunas ranuras para que funcionen bien con monedas. Todavía no convierten las monedas en destornilladores reales.
C ++ todavía pasa argumentos por valor.
Las referencias de C ++ son bastante más limitadas que los punteros (aunque tienen conversiones más implícitas) y no pueden convertirse en parte de las estructuras de datos, y su uso se parece mucho más al aspecto habitual del código de llamada por referencia, pero su semántica, aunque muy atendida para satisfacer las necesidades de los parámetros de llamada por referencia, son aún más tangibles que las implementaciones puras de llamada por referencia, como los parámetros de Fortran o los parámetros de Pascal
var
, y puede usar referencias perfectamente fuera de los contextos de llamada de función.
Del estándar C99 (énfasis mío):
6.2.5 Tipos
20 Se puede construir cualquier número de tipos derivados a partir de los tipos de objeto y función, de la siguiente manera:
...
- Un puntero típico e puede derivarse de un tipo de función o un tipo de objeto, denominado tipo referenciado . Un tipo de puntero describe un objeto cuyo valor proporciona una referencia a una entidad del tipo referenciado . Un tipo de puntero derivado del tipo referenciado T a veces se denomina "puntero a T ". La construcción de un tipo de puntero a partir de un tipo referenciado se denomina "derivación de tipo de puntero". Un tipo de puntero es un tipo de objeto completo.
Con base en lo anterior, lo que dijo su profesor tiene sentido y es correcto. Se pasa un puntero por valor a las funciones. Si el puntero apunta a una entidad válida, su valor proporciona una referencia a una entidad .
En los lenguajes que admiten paso por referencia, existe un medio por el cual se le puede dar a una función algo que se puede usar para identificar una variable conocida para el llamante hasta que regrese la función llamada, pero que solo se puede almacenar en lugares que ganaron No existe después de eso . En consecuencia, la persona que llama puede saber que cualquier cosa que se haga con una variable como resultado de pasar alguna función a una referencia se habrá hecho para cuando la función regrese.
Compare los programas C y C #:
// C // C#
int x=0; int x=0;
foo(&x); foo(ref x);
x++; x++;
bar(); bar();
x++; x++;
boz(x); boz(x);
El compilador de C no tiene forma de saber si "bar" podría cambiar x, porque foo () recibió un puntero sin restricciones. Por el contrario, el compilador de C # sabe que bar () no puede cambiar x, ya que foo () solo recibe una referencia temporal (llamada "byref" en la terminología de .NET) y no hay forma de hacer una copia de eso byref para sobrevivir más allá del punto donde regresa foo ().
Pasar punteros a las cosas permite que el código haga lo mismo que se puede hacer con la semántica de paso por referencia, pero la semántica de paso por referencia hace posible que el código ofrezca garantías más sólidas sobre cosas que no hará.
La referencia es un término sobrecargado aquí; en general, una referencia es simplemente una forma de referirse a algo. Un puntero se refiere al objeto señalado, y pasar (por valor) un puntero a un objeto es la forma estándar de pasar por referencia en C.
C ++ introdujo los tipos de referencia como una mejor manera de expresar referencias e introduce una ambigüedad en el inglés técnico, ya que ahora podemos usar el término "pasar por referencia" para referirnos al uso de tipos de referencia para pasar un objeto por referencia.
En un contexto de C ++, el uso anterior es, IMO, en desuso. Sin embargo, creo que el uso anterior es común en otros contextos (por ejemplo, C puro) donde no hay ambigüedad.
Tu profesor tiene razón. Por valor, se copia. Por referencia, no se copia, la referencia dice dónde está.
Por valor, pasa un int a una función, se copia, los cambios en la copia no afectan al original.
Por referencia, pase el mismo int que el puntero, no se copia, está modificando el original.
Por referencia, una matriz siempre es por referencia, podría tener mil millones de elementos en su matriz, es más rápido decir simplemente dónde está, está modificando el original.
¿C tiene incluso `` pasar por referencia ''''?
Realmente no.
Estrictamente hablando, C siempre usa pasar por valor. Puede simular pasar por referencia usted mismo, definiendo funciones que acepten punteros y luego utilizando el operador
&
al llamar , y el compilador esencialmente lo simulará cuando pase una matriz a una función (al pasar un puntero, consulte la pregunta 6.4 et al.).Otra forma de verlo es que si un parámetro tiene tipo, digamos,
int *
, se pasa un número entero por referencia y se pasa un puntero a un número entero por valor .Básicamente, C no tiene nada realmente equivalente al pase formal por referencia o los parámetros de referencia de c ++ .
Para demostrar que los punteros se pasan por valor, consideremos un ejemplo de intercambio de números usando punteros.
int main(void)
{
int num1 = 5;
int num2 = 10;
int *pnum1 = &num1;
int *pnum2 = &num2;
int ptemp;
printf("Before swap, *Pnum1 = %d and *pnum2 = %d/n", *pnum1, *pnum2);
temp = pnum1;
pnum1 = pnum2;
pnum2 = ptemp;
printf("After swap, *Pnum1 = %d and *pnum2 = %d/n", *pnum1, *pnum2);
}
En lugar de intercambiar números, los punteros se intercambian. Ahora haga una función para el mismo
void swap(int *pnum1, int *pnum2)
{
int *ptemp = pnum1;
pnum1 = pnum2;
pnum2 = temp;
}
int main(void)
{
int num1 = 5;
int num2 = 10;
int *pnum1 = &num1;
int *pnum2 = &num2;
printf("Before swap, *pnum1 = %d and *pnum2 = %d/n", *pnum1, *pnum2);
swap(pnum1, pnum2);
printf("After swap, *pnum1 = %d and *pnum2 = %d/n", *pnum1, *pnum2);
}
¡Auge! No intercambiar!
Algunos tutorials mencionan la referencia del puntero como llamada por referencia, lo cual es engañoso. Vea la respuesta this para conocer la diferencia entre pasar por referencia y pasar por valor .
no puede pasar por referencia en C pero puede usar punteros como alternativa
Sí, eso es correcto.
Para elaborar un poco más. Lo que pase como argumento para las funciones c, solo se pasa por valores. Ya sea el valor de una variable o la dirección de la variable.
Lo que hace la diferencia es lo que está enviando.
Cuando pasamos por valor, pasamos el valor de la variable a una función. Cuando pasamos por referencia, pasamos un alias de la variable a una función. C puede pasar un puntero a una función, pero todavía es pasar por valor. Está copiando el valor del puntero, la dirección, en la función.
-
Si está enviando el valor de una variable, entonces solo la función recibirá el valor, y cambiar eso no afectará el valor original.
-
Si está enviando la dirección de una variable, entonces solo se envía el valor (la dirección en este caso), pero como tiene la dirección de una variable, puede usarse para cambiar el valor original.
Como ejemplo, podemos ver un código C ++ para comprender la diferencia real entre llamada por valor y llamada por referencia. Tomado de this sitio web.
// Program to sort two numbers using call by reference.
// Smallest number is output first.
#include <iostream>
using namespace std;
// Function prototype for call by reference
void swap(float &x, float &y);
int main()
{
float a, b;
cout << "Enter 2 numbers: " << endl;
cin >> a >> b;
if(a>b)
swap(a,b); // This looks just like a call-by-value, but in fact
// it''s a call by reference (because of the "&" in the
// function prototype
// Variable a contains value of smallest number
cout << "Sorted numbers: ";
cout << a << " " << b << endl;
return 0;
}
// A function definition for call by reference
// The variables x and y will have their values changed.
void swap(float &x, float &y)
// Swaps x and y data of calling function
{
float temp;
temp = x;
x = y;
y = temp;
}
En este ejemplo de C ++, se usa la variable de referencia (que no está presente en C). Para citar this sitio web,
"Una referencia es un alias o un nombre alternativo a una variable existente ..." ,
y
"El uso principal de las referencias es actuar como parámetros formales de la función para admitir el paso por referencia ..."
Esto es diferente al uso de punteros como parámetros de función porque,
"Una variable de puntero (o puntero en resumen) es básicamente lo mismo que las otras variables, que pueden almacenar un dato. A diferencia de la variable normal que almacena un valor (como un int, un doble, un char), un puntero almacena una dirección de memoria ".
Entonces, esencialmente cuando se envía una dirección y se recibe a través de punteros, se envía solo el valor, pero cuando se envía / recibe una variable de referencia, se envía un alias o una referencia.
ACTUALIZACIÓN: 11 de noviembre de 2015
Ha habido un largo debate en el C Chatroom , y después de leer los comentarios y las respuestas a esta pregunta, me di cuenta de que puede haber otra forma de ver esta pregunta, otra perspectiva.
Veamos un código C simple
int i;
int *p = &i;
*p = 123;
En este escenario, uno puede usar la terminología de que
el valor de p es una referencia a i
.
Entonces, si ese es el caso, entonces si enviamos el mismo puntero (
int* p
) a una función, se puede argumentar que, dado que la referencia de
i
se envía a la función, y por lo tanto, esto puede llamarse
pass-by- referencia
.
Entonces, es una cuestión de terminología y forma de ver el escenario.
No estaría completamente en desacuerdo con ese argumento. Pero para una persona que sigue completamente el libro y las reglas, esto estaría mal.
NOTA: Actualización inspirada en this chat.