¿Cuándo se debe usar una doble indirección en C? ¿Alguien puede explicar con un ejemplo?
Lo que sé es que una doble indirección es un puntero a un puntero. ¿Por qué necesitaría un puntero a un puntero?
¿Cuándo se debe usar una doble indirección en C? ¿Alguien puede explicar con un ejemplo?
Lo que sé es que una doble indirección es un puntero a un puntero. ¿Por qué necesitaría un puntero a un puntero?
Respuestas:
Si desea tener una lista de caracteres (una palabra), puede usar char *word
Si desea una lista de palabras (una oración), puede usar char **sentence
Si desea una lista de oraciones (un monólogo), puede usar char ***monologue
Si desea una lista de monólogos (una biografía), puede usar char ****biography
Si desea una lista de biografías (una biobiblioteca), puede usar char *****biolibrary
Si desea una lista de biobibliotecas (a ?? lol), puede usar char ******lol
... ...
Sí, sé que estas podrían no ser las mejores estructuras de datos.
Ejemplo de uso con un lol muy muy muy aburrido
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int wordsinsentence(char **x) {
int w = 0;
while (*x) {
w += 1;
x++;
}
return w;
}
int wordsinmono(char ***x) {
int w = 0;
while (*x) {
w += wordsinsentence(*x);
x++;
}
return w;
}
int wordsinbio(char ****x) {
int w = 0;
while (*x) {
w += wordsinmono(*x);
x++;
}
return w;
}
int wordsinlib(char *****x) {
int w = 0;
while (*x) {
w += wordsinbio(*x);
x++;
}
return w;
}
int wordsinlol(char ******x) {
int w = 0;
while (*x) {
w += wordsinlib(*x);
x++;
}
return w;
}
int main(void) {
char *word;
char **sentence;
char ***monologue;
char ****biography;
char *****biolibrary;
char ******lol;
//fill data structure
word = malloc(4 * sizeof *word); // assume it worked
strcpy(word, "foo");
sentence = malloc(4 * sizeof *sentence); // assume it worked
sentence[0] = word;
sentence[1] = word;
sentence[2] = word;
sentence[3] = NULL;
monologue = malloc(4 * sizeof *monologue); // assume it worked
monologue[0] = sentence;
monologue[1] = sentence;
monologue[2] = sentence;
monologue[3] = NULL;
biography = malloc(4 * sizeof *biography); // assume it worked
biography[0] = monologue;
biography[1] = monologue;
biography[2] = monologue;
biography[3] = NULL;
biolibrary = malloc(4 * sizeof *biolibrary); // assume it worked
biolibrary[0] = biography;
biolibrary[1] = biography;
biolibrary[2] = biography;
biolibrary[3] = NULL;
lol = malloc(4 * sizeof *lol); // assume it worked
lol[0] = biolibrary;
lol[1] = biolibrary;
lol[2] = biolibrary;
lol[3] = NULL;
printf("total words in my lol: %d\n", wordsinlol(lol));
free(lol);
free(biolibrary);
free(biography);
free(monologue);
free(sentence);
free(word);
}
Salida:
palabras totales en mi jajaja: 243
arr[a][b][c]
no es a ***arr
. El puntero de punteros usa referencias de referencias, mientras que arr[a][b][c]
se almacena como una matriz habitual en el orden principal de la fila.
Una razón es que desea cambiar el valor del puntero pasado a una función como argumento de la función, para hacer esto necesita un puntero a un puntero.
En palabras simples, utilícelo **
cuando desee conservar (O retener el cambio) la Asignación o asignación de memoria incluso fuera de una llamada de función.(Entonces, pase dicha función con doble puntero arg.)
Puede que este no sea un muy buen ejemplo, pero le mostrará el uso básico:
void allocate(int** p)
{
*p = (int*)malloc(sizeof(int));
}
int main()
{
int* p = NULL;
allocate(&p);
*p = 42;
free(p);
}
void allocate(int *p)
y lo llamaras como allocate(p)
?
pointer1 = pointer2
, le da a pointer1 la dirección de pointer2.¡pero! si hace eso dentro de una función y desea que el resultado persista después de que se realiza la función, debe realizar un trabajo adicional. necesita un nuevo puntero3 solo para apuntar al puntero1. Pase el puntero3 a la función.
Aquí hay un ejemplo. Mire primero la salida a continuación para comprender.
#include <stdio.h>
int main()
{
int c = 1;
int d = 2;
int e = 3;
int * a = &c;
int * b = &d;
int * f = &e;
int ** pp = &a; // pointer to pointer 'a'
printf("\n a's value: %x \n", a);
printf("\n b's value: %x \n", b);
printf("\n f's value: %x \n", f);
printf("\n can we change a?, lets see \n");
printf("\n a = b \n");
a = b;
printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
printf("\n cant_change(a, f); \n");
cant_change(a, f);
printf("\n a's value is now: %x, Doh! same as 'b'... that function tricked us. \n", a);
printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
printf("\n change(pp, f); \n");
change(pp, f);
printf("\n a's value is now: %x, YEAH! same as 'f'... that function ROCKS!!!. \n", a);
return 0;
}
void cant_change(int * x, int * z){
x = z;
printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);
}
void change(int ** x, int * z){
*x = z;
printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", *x);
}
Aquí está la salida: ( lea esto primero )
a's value: bf94c204
b's value: bf94c208
f's value: bf94c20c
can we change a?, lets see
a = b
a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see...
cant_change(a, f);
----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
a's value is now: bf94c208, Doh! same as 'b'... that function tricked us.
NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a'
change(pp, f);
----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
a's value is now: bf94c20c, YEAH! same as 'f'... that function ROCKS!!!.
Además de la respuesta de Asha , si usa un puntero único al ejemplo siguiente (por ejemplo, alloc1 ()) perderá la referencia a la memoria asignada dentro de la función.
void alloc2(int** p) {
*p = (int*)malloc(sizeof(int));
**p = 10;
}
void alloc1(int* p) {
p = (int*)malloc(sizeof(int));
*p = 10;
}
int main(){
int *p = NULL;
alloc1(p);
//printf("%d ",*p);//undefined
alloc2(&p);
printf("%d ",*p);//will print 10
free(p);
return 0;
}
La razón por la que ocurre así es que en alloc1
el puntero se pasa por valor. Por lo tanto, cuando se reasigna al resultado de la malloc
llamada dentro de alloc1
, el cambio no pertenece al código en un ámbito diferente.
free(p)
no es suficiente, es necesario if(p) free(*p)
además
*p
evalúa para int
mantener el valor de 10, pasar esto int
a free () `es una mala idea.
alloc1()
introduce una pérdida de memoria. El valor del puntero que se pasa libre se pierde al volver de la función.
Hoy vi un muy buen ejemplo de esta publicación de blog. , como resumiré a continuación.
Imagine que tiene una estructura para nodos en una lista vinculada, que probablemente sea
typedef struct node
{
struct node * next;
....
} node;
Ahora desea implementar una remove_if
función, que acepte un criterio de eliminación rm
como uno de los argumentos y atraviese la lista vinculada: si una entrada cumple el criterio (algo así rm(entry)==true
), su nodo se eliminará de la lista. Al final, remove_if
devuelve el encabezado (que puede ser diferente del encabezado original) de la lista vinculada.
Puedes escribir
for (node * prev = NULL, * curr = head; curr != NULL; )
{
node * const next = curr->next;
if (rm(curr))
{
if (prev) // the node to be removed is not the head
prev->next = next;
else // remove the head
head = next;
free(curr);
}
else
prev = curr;
curr = next;
}
como tu for
bucle El mensaje es que, sin punteros dobles, debe mantener una prev
variable para reorganizar los punteros y manejar los dos casos diferentes.
Pero con los punteros dobles, puedes escribir
// now head is a double pointer
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
free(entry);
}
else
curr = &entry->next;
}
No necesita un prev
ahora porque puede modificar directamente lo que prev->next
señaló .
Para aclarar las cosas, sigamos un poco el código. Durante el retiro:
entry == *head
: será *head (==*curr) = *head->next
- head
ahora apunta al puntero del nuevo nodo de encabezado. Para ello, cambia directamente head
el contenido de un nuevo puntero.entry != *head
: de manera similar, *curr
es lo que prev->next
señaló y ahora señala entry->next
.No importa en qué caso, puede reorganizar los punteros de manera unificada con punteros dobles.
1. Concepto básico -
Cuando declaras lo siguiente: -
1. char * ch - (llamado puntero de caracteres)
- ch contiene la dirección de un solo carácter.
- (* ch) desreferenciará al valor del personaje.
2. char ** ch -
'ch' contiene la dirección de una matriz de punteros de caracteres. (como en 1)
'* ch' contiene la dirección de un solo carácter. (Tenga en cuenta que es diferente de 1, debido a la diferencia en la declaración).
(** ch) desreferenciará al valor exacto del personaje.
Agregar más punteros expande la dimensión de un tipo de datos, de carácter a cadena, a matriz de cadenas, y así sucesivamente ... Puede relacionarlo con una matriz 1d, 2d, 3d.
Entonces, el uso del puntero depende de cómo lo declare.
Aquí hay un código simple ...
int main()
{
char **p;
p = (char **)malloc(100);
p[0] = (char *)"Apple"; // or write *p, points to location of 'A'
p[1] = (char *)"Banana"; // or write *(p+1), points to location of 'B'
cout << *p << endl; //Prints the first pointer location until it finds '\0'
cout << **p << endl; //Prints the exact character which is being pointed
*p++; //Increments for the next string
cout << *p;
}
2. Otra aplicación de punteros dobles:
(esto también cubriría el pase por referencia)
Suponga que desea actualizar un personaje desde una función. Si intenta lo siguiente: -
void func(char ch)
{
ch = 'B';
}
int main()
{
char ptr;
ptr = 'A';
printf("%c", ptr);
func(ptr);
printf("%c\n", ptr);
}
La salida será AA. Esto no funciona, ya que ha "Pasado por valor" a la función.
La forma correcta de hacerlo sería:
void func( char *ptr) //Passed by Reference
{
*ptr = 'B';
}
int main()
{
char *ptr;
ptr = (char *)malloc(sizeof(char) * 1);
*ptr = 'A';
printf("%c\n", *ptr);
func(ptr);
printf("%c\n", *ptr);
}
Ahora amplíe este requisito para actualizar una cadena en lugar de caracteres.
Para esto, debe recibir el parámetro en la función como un puntero doble.
void func(char **str)
{
strcpy(str, "Second");
}
int main()
{
char **str;
// printf("%d\n", sizeof(char));
*str = (char **)malloc(sizeof(char) * 10); //Can hold 10 character pointers
int i = 0;
for(i=0;i<10;i++)
{
str = (char *)malloc(sizeof(char) * 1); //Each pointer can point to a memory of 1 character.
}
strcpy(str, "First");
printf("%s\n", str);
func(str);
printf("%s\n", str);
}
En este ejemplo, el método espera un puntero doble como parámetro para actualizar el valor de una cadena.
#include <stdio.h> int main() { char *ptr = 0; ptr = malloc(255); // allocate some memory strcpy( ptr, "Stack Overflow Rocks..!!"); printf("%s\n", ptr); printf("%d\n",strlen(ptr)); free(ptr); return 0; }
Pero también puede hacerlo sin usar el puntero doble.
char
punteros. Un puntero a una matriz de char*
se escribiría, por ejemplo, así: se char(*(*p)[42])
define p
como un puntero a una matriz de 42 punteros a char
.
*str = ...
str
está desreferenciado sin inicializar invocando un comportamiento indefinido.
malloc(sizeof(char) * 10);
no asigna espacio para 10 punteros char
sino char
solo para 10 ..
for(i=0;i<10;i++) { str = ...
falla al usar el índice i
.
Los punteros a punteros también son útiles como "manejadores" de la memoria donde desea pasar un "manejador" entre las funciones de la memoria reubicable. Básicamente, eso significa que la función puede cambiar la memoria a la que apunta el puntero dentro de la variable del controlador, y cada función u objeto que está utilizando el controlador apuntará correctamente a la memoria recién reubicada (o asignada). A las bibliotecas les gusta hacer esto con tipos de datos "opacos", es decir, tipos de datos en los que no tiene que preocuparse por lo que están haciendo con la memoria apuntada, simplemente pasa el "identificador" entre funciones de la biblioteca para realizar algunas operaciones en esa memoria ...
Por ejemplo:
#include <stdlib.h>
typedef unsigned char** handle_type;
//some data_structure that the library functions would work with
typedef struct
{
int data_a;
int data_b;
int data_c;
} LIB_OBJECT;
handle_type lib_create_handle()
{
//initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
handle_type handle = malloc(sizeof(handle_type));
*handle = malloc(sizeof(LIB_OBJECT) * 10);
return handle;
}
void lib_func_a(handle_type handle) { /*does something with array of LIB_OBJECTs*/ }
void lib_func_b(handle_type handle)
{
//does something that takes input LIB_OBJECTs and makes more of them, so has to
//reallocate memory for the new objects that will be created
//first re-allocate the memory somewhere else with more slots, but don't destroy the
//currently allocated slots
*handle = realloc(*handle, sizeof(LIB_OBJECT) * 20);
//...do some operation on the new memory and return
}
void lib_func_c(handle_type handle) { /*does something else to array of LIB_OBJECTs*/ }
void lib_free_handle(handle_type handle)
{
free(*handle);
free(handle);
}
int main()
{
//create a "handle" to some memory that the library functions can use
handle_type my_handle = lib_create_handle();
//do something with that memory
lib_func_a(my_handle);
//do something else with the handle that will make it point somewhere else
//but that's invisible to us from the standpoint of the calling the function and
//working with the handle
lib_func_b(my_handle);
//do something with new memory chunk, but you don't have to think about the fact
//that the memory has moved under the hood ... it's still pointed to by the "handle"
lib_func_c(my_handle);
//deallocate the handle
lib_free_handle(my_handle);
return 0;
}
Espero que esto ayude,
Jason
unsigned char
se usa específicamente porque estamos almacenando un puntero a datos binarios que se representarán como bytes sin formato. El uso void
requerirá un lanzamiento en algún momento, y generalmente no es tan legible como la intención de lo que se está haciendo.
int main(int argc, char **argv)
En el segundo parámetro lo tiene: puntero a puntero a char.
Tenga en cuenta que la notación de puntero ( char* c
) y la notación de matriz ( char c[]
) son intercambiables en argumentos de función. Entonces también podrías escribir char *argv[]
. En otras palabras char *argv[]
y char **argv
son intercambiables.
Lo que representa lo anterior es, de hecho, una serie de secuencias de caracteres (los argumentos de la línea de comandos que se le dan a un programa al inicio).
Consulte también esta respuesta para obtener más detalles sobre la firma de la función anterior.
char* c
) y la notación de matriz ( char c[]
) son intercambiables" (y tienen el mismo significado exacto) en los argumentos de la función . Sin embargo, son diferentes a los argumentos de funciones externas.
Por ejemplo, es posible que desee asegurarse de que cuando libere la memoria de algo, establezca el puntero en nulo después.
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
Cuando llamas a esta función, la llamarías con la dirección de un puntero
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
Ahora myMemory
está configurado en NULL y cualquier intento de reutilizarlo será obviamente un error.
if(*memory)
yfree(*memory);
Por ejemplo, si desea acceso aleatorio a datos no contiguos.
p -> [p0, p1, p2, ...]
p0 -> data1
p1 -> data2
-- C ª
T ** p = (T **) malloc(sizeof(T*) * n);
p[0] = (T*) malloc(sizeof(T));
p[1] = (T*) malloc(sizeof(T));
Almacena un puntero p
que apunta a una matriz de punteros. Cada puntero apunta a una pieza de datos.
Si sizeof(T)
es grande, puede que no sea posible asignar un bloque contiguo (es decir, usando malloc) de sizeof(T) * n
bytes.
Una cosa para la que los uso constantemente es cuando tengo una matriz de objetos y necesito realizar búsquedas (búsqueda binaria) en ellos por diferentes campos.
Mantengo la matriz original ...
int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);
Luego, haga una matriz de punteros ordenados a los objetos.
int compare_object_by_name( const void *v1, const void *v2 ) {
OBJECT *o1 = *(OBJECT **)v1;
OBJECT *o2 = *(OBJECT **)v2;
return (strcmp(o1->name, o2->name);
}
OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
int i = 0;
for( ; i<num_objects; i++)
object_ptrs_by_name[i] = original_array+i;
qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);
Puede hacer tantos arreglos de punteros ordenados como necesite, luego usar una búsqueda binaria en el arreglo de punteros ordenados para acceder al objeto que necesita por los datos que tiene. La matriz original de objetos puede permanecer sin clasificar, pero cada matriz de punteros se ordenará por su campo especificado.
¿Por qué dobles punteros?
El objetivo es cambiar lo que apunta el estudiante A, usando una función.
#include <stdio.h>
#include <stdlib.h>
typedef struct Person{
char * name;
} Person;
/**
* we need a ponter to a pointer, example: &studentA
*/
void change(Person ** x, Person * y){
*x = y; // since x is a pointer to a pointer, we access its value: a pointer to a Person struct.
}
void dontChange(Person * x, Person * y){
x = y;
}
int main()
{
Person * studentA = (Person *)malloc(sizeof(Person));
studentA->name = "brian";
Person * studentB = (Person *)malloc(sizeof(Person));
studentB->name = "erich";
/**
* we could have done the job as simple as this!
* but we need more work if we want to use a function to do the job!
*/
// studentA = studentB;
printf("1. studentA = %s (not changed)\n", studentA->name);
dontChange(studentA, studentB);
printf("2. studentA = %s (not changed)\n", studentA->name);
change(&studentA, studentB);
printf("3. studentA = %s (changed!)\n", studentA->name);
return 0;
}
/**
* OUTPUT:
* 1. studentA = brian (not changed)
* 2. studentA = brian (not changed)
* 3. studentA = erich (changed!)
*/
El siguiente es un ejemplo muy simple de C ++ que muestra que si desea utilizar una función para establecer un puntero para apuntar a un objeto, necesita un puntero a un puntero . De lo contrario, el puntero seguirá volviendo a nulo .
(Una respuesta de C ++, pero creo que es lo mismo en C.)
(También, para referencia: Google ("pass by value c ++") = "Por defecto, los argumentos en C ++ se pasan por valor. Cuando un argumento se pasa por valor, el valor del argumento se copia en el parámetro de la función").
Por lo tanto, queremos establecer el puntero b
igual a la cadena a
.
#include <iostream>
#include <string>
void Function_1(std::string* a, std::string* b) {
b = a;
std::cout << (b == nullptr); // False
}
void Function_2(std::string* a, std::string** b) {
*b = a;
std::cout << (b == nullptr); // False
}
int main() {
std::string a("Hello!");
std::string* b(nullptr);
std::cout << (b == nullptr); // True
Function_1(&a, b);
std::cout << (b == nullptr); // True
Function_2(&a, &b);
std::cout << (b == nullptr); // False
}
// Output: 10100
¿Qué pasa en la línea Function_1(&a, b);
?
El "valor" de &main::a
(una dirección) se copia en el parámetro std::string* Function_1::a
. Por Function_1::a
lo tanto, es un puntero a (es decir, la dirección de memoria de) la cadena main::a
.
El "valor" de main::b
(una dirección en la memoria) se copia en el parámetro std::string* Function_1::b
. Por lo tanto, ahora hay 2 de estas direcciones en la memoria, ambos punteros nulos. En la línea b = a;
, la variable local Function_1::b
se cambia a igual Function_1::a
(= &main::a
), pero la variable main::b
no cambia. Después de la llamada a Function_1
, main::b
sigue siendo un puntero nulo.
¿Qué pasa en la línea Function_2(&a, &b);
?
El tratamiento de la a
variable es el mismo: dentro de la función, Function_2::a
es la dirección de la cadena main::a
.
Pero la variable b
ahora se pasa como un puntero a un puntero. El "valor" de &main::b
(la dirección del puntero main::b
) se copia en std::string** Function_2::b
. Por lo tanto, dentro de Function_2, desreferenciando esto como *Function_2::b
accederá y modificará main::b
. Entonces, la línea *b = a;
está configurando main::b
(una dirección) igual a Function_2::a
(= dirección de main::a
) que es lo que queremos.
Si desea utilizar una función para modificar una cosa, ya sea un objeto o una dirección (puntero), debe pasar un puntero a esa cosa. Lo que realmente pasa no puede modificarse (en el alcance de la llamada) porque se realiza una copia local.
(Una excepción es si el parámetro es una referencia, como por ejemplo std::string& a
. Pero por lo general son const
. Generalmente, si llama f(x)
, si x
es un objeto, debería poder asumir que f
no modificará x
. Pero si x
es un puntero, entonces debería suponga que f
podría modificar el objeto señalado por x
.)
Un poco tarde para la fiesta, pero espero que esto ayude a alguien.
En las matrices C siempre se asigna memoria en la pila, por lo tanto, una función no puede devolver una matriz (no estática) debido al hecho de que la memoria asignada en la pila se libera automáticamente cuando la ejecución llega al final del bloque actual. Eso es realmente molesto cuando desea lidiar con matrices bidimensionales (es decir, matrices) e implementar algunas funciones que pueden alterar y devolver matrices. Para lograr esto, puede usar un puntero a puntero para implementar una matriz con memoria asignada dinámicamente:
/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
// Allocate memory for num_rows float-pointers
double** A = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(A == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols floats
for(int i = 0; i < num_rows; i++){
A[i] = calloc(num_cols, sizeof(double));
// return NULL if the memory couldn't allocated
// and free the already allocated memory
if(A[i] == NULL){
for(int j = 0; j < i; j++){
free(A[j]);
}
free(A);
return NULL;
}
}
return A;
}
Aquí hay una ilustración:
double** double* double
------------- ---------------------------------------------------------
A ------> | A[0] | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
| --------- | ---------------------------------------------------------
| A[1] | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[i] | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
------------- ---------------------------------------------------------
El doble puntero a doble puntero A apunta al primer elemento A [0] de un bloque de memoria cuyos elementos son punteros dobles. Puede imaginar estos punteros dobles como las filas de la matriz. Esa es la razón por la cual cada puntero doble asigna memoria para elementos num_cols de tipo double. Además, A [i] apunta a la fila i-ésima, es decir, A [i] apunta a A [i] [0] y ese es solo el primer elemento doble del bloque de memoria para la fila i-ésima. Finalmente, puede acceder al elemento en la i-ésima fila y j-ésima columna fácilmente con A [i] [j].
Aquí hay un ejemplo completo que demuestra el uso:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
// Allocate memory for num_rows double-pointers
double** matrix = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(matrix == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols
// doubles
for(int i = 0; i < num_rows; i++){
matrix[i] = calloc(num_cols, sizeof(double));
// return NULL if the memory couldn't allocated
// and free the already allocated memory
if(matrix[i] == NULL){
for(int j = 0; j < i; j++){
free(matrix[j]);
}
free(matrix);
return NULL;
}
}
return matrix;
}
/* Fills the matrix with random double-numbers between -1 and 1 */
void randn_fill_matrix(double** matrix, int rows, int cols){
for (int i = 0; i < rows; ++i){
for (int j = 0; j < cols; ++j){
matrix[i][j] = (double) rand()/RAND_MAX*2.0-1.0;
}
}
}
/* Frees the memory allocated by the matrix */
void free_matrix(double** matrix, int rows, int cols){
for(int i = 0; i < rows; i++){
free(matrix[i]);
}
free(matrix);
}
/* Outputs the matrix to the console */
void print_matrix(double** matrix, int rows, int cols){
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
printf(" %- f ", matrix[i][j]);
}
printf("\n");
}
}
int main(){
srand(time(NULL));
int m = 3, n = 3;
double** A = init_matrix(m, n);
randn_fill_matrix(A, m, n);
print_matrix(A, m, n);
free_matrix(A, m, n);
return 0;
}
Hoy usé punteros dobles mientras programaba algo para el trabajo, así que puedo responder por qué tuvimos que usarlos (es la primera vez que realmente tuve que usar punteros dobles). Tuvimos que lidiar con la codificación en tiempo real de tramas contenidas en buffers que son miembros de algunas estructuras. En el codificador tuvimos que usar un puntero a una de esas estructuras. El problema era que nuestro puntero estaba siendo cambiado para apuntar a otras estructuras desde otro hilo. Para usar la estructura actual en el codificador, tuve que usar un puntero doble, para apuntar al puntero que se estaba modificando en otro hilo. Al principio no era obvio, al menos para nosotros, que teníamos que adoptar este enfoque. Se imprimieron muchas direcciones en el proceso :)).
DEBE utilizar punteros dobles cuando trabaje en punteros que se cambian en otros lugares de su aplicación. También es posible que los punteros dobles sean imprescindibles cuando se trata de hardware que regresa y se dirige a usted.
Compare el valor de modificación de la variable con el valor de modificación del puntero :
#include <stdio.h>
#include <stdlib.h>
void changeA(int (*a))
{
(*a) = 10;
}
void changeP(int *(*P))
{
(*P) = malloc(sizeof((*P)));
}
int main(void)
{
int A = 0;
printf("orig. A = %d\n", A);
changeA(&A);
printf("modi. A = %d\n", A);
/*************************/
int *P = NULL;
printf("orig. P = %p\n", P);
changeP(&P);
printf("modi. P = %p\n", P);
free(P);
return EXIT_SUCCESS;
}
Esto me ayudó a evitar el valor de retorno del puntero cuando el puntero fue modificado por la función llamada (utilizada en una lista individualmente vinculada).
VIEJO (malo):
int *func(int *P)
{
...
return P;
}
int main(void)
{
int *pointer;
pointer = func(pointer);
...
}
NUEVO (mejor):
void func(int **pointer)
{
...
}
int main(void)
{
int *pointer;
func(&pointer);
...
}
double*
.