Entendiendo funciones y punteros en C

Esta es una pregunta muy simple, pero ¿qué significa la siguiente función de prototipo?

int cuadrado (int y, size_t * x)

¿Qué dosis significa size_t *? Sé que size_t es un tipo de datos (int> = 0). Pero, ¿cómo leo el * adjunto? ¿Es un puntero a la ubicación de memoria para x? En general, estoy teniendo problemas con esto, y si alguien pudiera proporcionar una referencia útil, lo apreciaría.


Gracias a todos. Entiendo lo que es un puntero, pero creo que me cuesta mucho entender la relación entre los punteros y las funciones. Cuando veo un prototipo de función definido como int sq(int x, int y) , entonces queda perfectamente claro lo que está pasando. Sin embargo, cuando veo algo como int sq( int x, int* y) , entonces no puedo, por mi vida, entender lo que realmente significa el segundo parámetro. En algún nivel, entiendo que significa “pasar un puntero”, pero no entiendo las cosas lo suficientemente bien como para manipularlo por mi cuenta.

¿Qué tal un tutorial sobre la comprensión de los punteros ?

En este caso, sin embargo, el puntero se usa probablemente para modificar / devolver el valor. En C, hay dos mecanismos básicos en los que una función puede devolver un valor (por favor, perdone el ejemplo tonto):

Puede devolver el valor directamente:

 float square_root( float x ) { if ( x >= 0 ) return sqrt( x ); return 0; } 

O puede volver por un puntero:

 int square_root( float x, float* result ) { if ( x >= 0 ) { *result = sqrt( result ); return 1; } return 0; } 

El primero se llama:

 float a = square_root( 12.0 ); 

… mientras que este último:

 float b; square_root( 12.00, &b ); 

Tenga en cuenta que el último ejemplo también le permitirá comprobar si el valor devuelto era real: este mecanismo se usa ampliamente en las bibliotecas de C, donde el valor de retorno de una función generalmente denota éxito (o la falta de él), mientras que los valores en sí mismos devuelto a través de parámetros.

De ahí que con este último se podría escribir:

 float sqresult; if ( !square_root( myvar, &sqresult ) ) { // signal error } else { // value is good, continue using sqresult! } 

* x significa que x es un puntero a una ubicación de memoria de tipo size_t.

Puede establecer la ubicación con x = & y; o establezca el valor donde x puntos con: * x = 0;

Si necesita más información, eche un vistazo a: Punteros

El prototipo significa que la función toma un entero arg y un arg que es un puntero a un tipo size_t. size_t es un tipo definido en un archivo de encabezado, generalmente para ser un int sin signo, pero la razón para no solo usar “unsigned int * x” es dar a los escritores del comstackdor flexibilidad para usar otra cosa.

Un puntero es un valor que contiene una dirección de memoria. Si escribo

int x = 42;

entonces el comstackdor asignará 4 bytes en la memoria y recordará la ubicación cada vez que use x. Si quiero pasar esa ubicación explícitamente, puedo crear un puntero y asignarle la dirección de x:

int * ptr = & x;

Ahora puedo pasar ptr a funciones que esperan un int * para un argumento, y puedo usar ptr por desreferenciación:

cout << * ptr + 1;

imprimirá 43. Hay una serie de razones por las que podría querer usar punteros en lugar de valores. 1) evita las estructuras y clases de construcción de copias cuando pasa a una función 2) puede tener más de un controlador a una variable 3) es la única forma de manipular variables en el montón 4) puede usarlas para pasar los resultados fuera de una función escribiendo en la ubicación señalada por un argumento

Fundamentos del puntero

Punteros y la memoria

En respuesta a tu último comentario, intentaré explicarte.

Sabe que las variables tienen un valor , y el tipo de la variable le dice qué tipo de valores puede tener. Por lo tanto, una variable de tipo int puede contener un número entero que cae dentro de un cierto rango. Si declaro una función como:

 int sq(int x); 

… entonces eso significa que la función sq necesita que usted proporcione un valor que sea un número entero, y devolverá un valor que también es un número entero.

Si una variable se declara con un tipo de puntero, significa que el valor de esa variable en sí es “la ubicación de otra variable”. Por lo tanto, una variable de tipo int * puede contener su valor, “la ubicación de otra variable, y esa otra variable tiene tipo int “. Entonces podemos extender eso a las funciones:

 int sqp(int * x); 

Eso significa que la función sqp necesita que usted proporcione un valor que sea en sí mismo la ubicación de una variable de tipo int . Eso significa que podría llamarlo así:

 int p; int q; p = sqp(&q); 

( &q solo significa “dame la ubicación de q , no su valor”). Dentro de sqp , podría usar ese puntero como este:

 int sqp(int * x) { *x = 10; return 20; } 

( *x significa “actuar sobre la variable en la ubicación dada por x , no x en sí”).

size_t *x significa que está pasando un puntero a una “instancia” size_t.

Hay un par de razones por las que quiere pasar un puntero.

  1. Para que la función pueda modificar la variable del llamante. C usa el paso por valor para que la modificación de un parámetro dentro de una función no modifique la variable original.
  2. Por razones de rendimiento. Si un parámetro es una estructura, paso por valor significa que debe copiar la estructura. Si la estructura es lo suficientemente grande, esto podría causar un impacto en el rendimiento.

Hay una interpretación adicional dado que esto es un parámetro para una función.

Cuando usas punteros (algo *) en el argumento de una función y pasas una variable, no pasas un valor, pasas una referencia (un “puntero”) a un valor. Cualquier cambio realizado en la variable dentro de la función se realiza en la variable a la que se refiere, es decir, la variable fuera de la función.

Aún tiene que pasar el tipo correcto, hay dos formas de hacerlo; use un puntero en la rutina de llamada o use el operador & (addressof).

Acabo de escribir esto rápidamente para demostrar:

 #include  void add(int one, int* two) { *two += one; } int main() { int x = 5; int y = 7; add(x,&y); printf("%d %d\n", x, y); return 0; } 

Así es como funcionan las cosas como scanf.

 int square( int y, size_t* x ); 

Esto declara una función que toma dos argumentos : un entero y un puntero a un entero sin signo (probablemente grande) y devuelve un entero.

size_t es un tipo entero sin signo (generalmente un typedef ) devuelto por el operador sizeof() .

* (estrella) señala el tipo de puntero (por ejemplo, int* ptr; hace que ptr sea ​​puntero a entero) cuando se usa en declaraciones (y casts), o la desreferencia de un puntero cuando se usa en lvalue o rvalue ( *ptr = 10; asigna diez a memoria apuntada por ptr ). Es nuestra suerte que el mismo símbolo se use para la multiplicación (Pascal, por ejemplo, usa ^ para los punteros).

En el punto de statement de función, los nombres de los parámetros ( y aquí) no importan realmente. Puede definir su función con diferentes nombres de parámetros en el archivo .c . La persona que llama a la función solo está interesada en los tipos y el número de parámetros de la función y el tipo de retorno.

Cuando define la función, los parámetros ahora nombran variables locales , cuyos valores son asignados por la persona que llama.

Los parámetros de la función del puntero se utilizan al pasar objetos por referencia o como parámetros de salida cuando se pasa un puntero a la ubicación donde la función almacena el valor de salida.

C es un lenguaje hermoso y simple 🙂

U dijo que u sabe lo que es int sq(int x, int y) . Significa que estamos pasando dos variables x, y como aguentos a la función sq.Say la función sq se llama desde main() function como en

 main() { /*some code*/ x=sr(a,b); /*some other code*/ } int sq(int x,int y) { /*code*/ } 

cualquier operación realizada en x, y en la función sq no afecta los valores a, b mientras está en

 main() { /*some code*/ x=sq(a,&b); /*some other code*/ } int sq(int x,int* y) { /*code*/ } 

las operaciones realizadas en y modificarán el valor de b, porque nos referimos a b

por lo tanto, si desea modificar los valores originales, utilice punteros. Si desea utilizar esos valores, no necesita utilizar punteros.

La mayor parte de la explicación anterior está bastante bien explicada. Me gustaría agregar el punto de vista de la aplicación de este tipo de argumento que pasa.

1) cuando una función tiene que devolver más de un valor, no se puede hacer usando más de un tipo de retorno (trivial, y todos lo sabemos). Para lograr que los punteros pasen a la función, los argumentos proporcionarán una manera de reflejar los cambios realizados dentro de la función que se está llamando (por ejemplo: sqrt) en la función de llamada (por ejemplo: principal)

Ej: tonto pero te da un escenario.

 //a function is used to get two random numbers into x,y in the main function int main() { int x,y; generate_rand(&x,&y); //now x,y contain random values generated by the function } void generate_rand(int *x,int *y) { *x=rand()%100; *y=rand()%100; } 

2) cuando pasar un objeto (un objeto de clase o una estructura, etc.) es un proceso costoso (es decir, si el tamaño es demasiado grande, entonces la memoria n otras restricciones, etc.)

por ejemplo: en lugar de pasar una estructura a una función como un argumento, el puntero podría ser útil, ya que el puntero se puede usar para acceder a la estructura, pero también guarda memoria ya que no está almacenando la estructura en la ubicación temporal (o stack)

Sólo un par de ejemplos … espero que ayude …

2 años después y todavía no se acepta respuesta? Muy bien, voy a tratar de explicarlo …

Tomemos las dos funciones que mencionaste en tu pregunta:

int sq_A(int x, int y)

Usted sabe esto: es una función llamada sq_A que toma dos parámetros int . Fácil.

int sq_B(int x, int* y)

Esta es una función llamada sq_B que toma dos parámetros:

  • El parámetro 1 es un int

  • El parámetro 2 es un puntero. Este es un puntero que apunta a un int

Entonces, cuando llamamos a sq_B() , necesitamos pasar un puntero como segundo parámetro. Sin embargo, no podemos pasar ningún puntero, debe ser un puntero a un tipo int .

Por ejemplo:

 int sq_B(int x, int* y) { /* do something with x and y and return a value */ } int main() { int t = 6; int u = 24; int result; result = sq_B(t, &u); return 0; } 

En main() , la variable u es un int . Para obtener un puntero a u , usamos el operador &u&u . Esto significa “dirección de u “, y es un puntero.

Dado que u es un int , &u es un puntero a un int (o int * ), que es el tipo especificado por el parámetro 2 de sq_B() .

¿Alguna pregunta?