Consejos en C: ¿cuándo utilizar el signo y el asterisco?
Recién estoy comenzando con los consejos y estoy un poco confundido. Sé &
significa la dirección de una variable y se *
puede usar delante de una variable de puntero para obtener el valor del objeto al que apunta el puntero. Pero las cosas funcionan de manera diferente cuando trabajas con matrices, cadenas o cuando llamas funciones con una copia de puntero de una variable. Es difícil ver un patrón de lógica dentro de todo esto.
¿ Cuándo debo usar &
y *
?
Tienes sugerencias y valores:
int* p; // variable p is pointer to integer type
int i; // integer value
Conviertes un puntero en un valor con *
:
int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to
Conviertes un valor en un puntero con &
:
int* p2 = &i; // pointer p2 will point to the integer i
Editar: en el caso de las matrices, se tratan de forma muy parecida a los punteros. Si piensa en ellos como punteros, los utilizará *
para obtener los valores dentro de ellos como se explicó anteriormente, pero también hay otra forma más común de usar el []
operador:
int a[2]; // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element
Para obtener el segundo elemento:
int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element
Entonces, el []
operador de indexación es una forma especial de *
operador y funciona así:
a[i] == *(a + i); // these two statements are the same thing
Existe un patrón cuando se trata de matrices y funciones; Es un poco difícil de ver al principio.
Cuando se trata de matrices, es útil recordar lo siguiente: cuando aparece una expresión de matriz en la mayoría de los contextos, el tipo de expresión se convierte implícitamente de "matriz de N elementos de T" a "puntero a T", y se establece su valor. para apuntar al primer elemento de la matriz. Las excepciones a esta regla son cuando la expresión de matriz aparece como un operando de los operadores &
o sizeof
, o cuando es una cadena literal que se utiliza como inicializador en una declaración.
Por lo tanto, cuando llamas a una función con una expresión de matriz como argumento, la función recibirá un puntero, no una matriz:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
Es por eso que no usa el &
operador para los argumentos correspondientes a "%s" en scanf()
:
char str[STRING_LENGTH];
...
scanf("%s", str);
Debido a la conversión implícita, scanf()
recibe un char *
valor que apunta al comienzo de la str
matriz. Esto es válido para cualquier función llamada con una expresión de matriz como argumento (casi cualquiera de las str*
funciones, *scanf
funciones *printf
, etc.).
En la práctica, probablemente nunca llames a una función con una expresión de matriz usando el &
operador, como en:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
Este código no es muy común; debe conocer el tamaño de la matriz en la declaración de la función, y la función solo funciona con punteros a matrices de tamaños específicos (un puntero a una matriz de 10 elementos de T es de un tipo diferente a un puntero a una matriz de 11 elementos de T).
Cuando una expresión de matriz aparece como un operando para el &
operador, el tipo de expresión resultante es "puntero a una matriz de N elementos de T", o T (*)[N]
, que es diferente de una matriz de punteros ( T *[N]
) y un puntero al tipo base ( T *
).
Cuando se trata de funciones y punteros, la regla a recordar es: si desea cambiar el valor de un argumento y reflejarlo en el código de llamada, debe pasar un puntero a lo que desea modificar. Una vez más, las matrices suponen un pequeño problema, pero primero nos ocuparemos de los casos normales.
Recuerde que C pasa todos los argumentos de la función por valor; el parámetro formal recibe una copia del valor del parámetro real y cualquier cambio en el parámetro formal no se refleja en el parámetro real. El ejemplo común es una función de intercambio:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
Obtendrá el siguiente resultado:
antes del intercambio: a = 1, b = 2 después del intercambio: a = 1, b = 2
Los parámetros formales x
y y
son objetos distintos de a
y b
, por lo que los cambios en x
y y
no se reflejan en a
y b
. Como queremos modificar los valores de a
y b
, debemos pasarles punteros a la función de intercambio:
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
Ahora tu salida será
antes del intercambio: a = 1, b = 2 después del intercambio: a = 2, b = 1
Tenga en cuenta que, en la función de intercambio, no cambiamos los valores de x
y y
, sino los valores de what x
y y
apuntan a . Escribir a *x
es diferente a escribir a x
; No estamos actualizando el valor en x
sí mismo, obtenemos una ubicación x
y actualizamos el valor en esa ubicación.
Esto es igualmente cierto si queremos modificar el valor de un puntero; si escribimos
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
entonces estamos modificando el valor del parámetro de entrada stream
, no lo que stream
apunta , por lo que cambiar stream
no tiene ningún efecto en el valor de in
; Para que esto funcione, debemos pasar un puntero al puntero:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
Una vez más, las matrices suponen un pequeño obstáculo para el proceso. Cuando pasas una expresión de matriz a una función, lo que recibe la función es un puntero. Debido a cómo se define el subíndice de una matriz, puede usar un operador de subíndice en un puntero de la misma manera que puede usarlo en una matriz:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
Tenga en cuenta que es posible que los objetos de matriz no se puedan asignar; es decir, no puedes hacer algo como
int a[10], b[10];
...
a = b;
por lo que debes tener cuidado cuando trabajes con punteros a matrices; algo como
void (int (*foo)[N])
{
...
*foo = ...;
}
no funcionará.
En pocas palabras
&
significa la dirección de , verá que en los marcadores de posición para funciones para modificar la variable de parámetro como en C, las variables de parámetro se pasan por valor, usando el símbolo comercial para pasar por referencia.*
significa la desreferencia de una variable de puntero, lo que significa obtener el valor de esa variable de puntero.
int foo(int *x){
*x++;
}
int main(int argc, char **argv){
int y = 5;
foo(&y); // Now y is incremented and in scope here
printf("value of y = %d\n", y); // output is 6
/* ... */
}
El ejemplo anterior ilustra cómo llamar a una función foo
usando paso por referencia, compárelo con esto
int foo(int x){
x++;
}
int main(int argc, char **argv){
int y = 5;
foo(y); // Now y is still 5
printf("value of y = %d\n", y); // output is 5
/* ... */
}
A continuación se muestra una ilustración del uso de una desreferencia.
int main(int argc, char **argv){
int y = 5;
int *p = NULL;
p = &y;
printf("value of *p = %d\n", *p); // output is 5
}
Lo anterior ilustra cómo obtuvimos la dirección de y
y la asignamos a la variable de puntero p
. Luego eliminamos la referencia p
adjuntando el *
al frente para obtener el valor de p
, es decir *p
.
Sí, eso puede ser bastante complicado ya que se *
usa para muchos propósitos diferentes en C/C++.
Si *
aparece delante de una variable/función ya declarada, significa que:
- a)
*
da acceso al valor de esa variable (si el tipo de esa variable es un tipo puntero o está sobrecargado el*
operador). - b)
*
tiene el significado del operador multiplicar, en ese caso, tiene que haber otra variable a la izquierda del*
Si *
aparece en una variable o declaración de función significa que esa variable es un puntero:
int int_value = 1;
int * int_ptr; //can point to another int variable
int int_array1[10]; //can contain up to 10 int values, basically int_array1 is an pointer as well which points to the first int of the array
//int int_array2[]; //illegal, without initializer list..
int int_array3[] = {1,2,3,4,5}; // these two
int int_array4[5] = {1,2,3,4,5}; // are identical
void func_takes_int_ptr1(int *int_ptr){} // these two are identical
void func_takes_int_ptr2(int int_ptr[]){}// and legal
Si &
aparece en una variable o declaración de función, generalmente significa que esa variable es una referencia a una variable de ese tipo.
Si &
aparece delante de una variable ya declarada, devuelve la dirección de esa variable
Además, debe saber que al pasar una matriz a una función, siempre tendrá que pasar también el tamaño de la matriz de esa matriz, excepto cuando la matriz sea algo así como una cadena c terminada en 0 (matriz de caracteres).