c - que - ¿Por qué usar doble puntero? o ¿Por qué usar punteros a punteros?
punteros c++ (19)
¿Cuándo debería usarse un puntero doble en C? ¿Alguien puede explicar con un ejemplo?
Lo que sé es que un puntero doble es un puntero a un puntero. ¿Por qué necesitaría un puntero a un puntero?
Ejemplo simple que probablemente haya visto muchas veces antes
int main(int argc, char **argv)
En el segundo parámetro lo tienes: 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 los argumentos de función. Así que 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 dan a un programa en el inicio).
Consulte también esta respuesta para obtener más detalles sobre la firma de la función anterior.
¿Por qué punteros dobles?
El objetivo es cambiar lo que señala StudentA mediante 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!)
*/
Agregando a respuesta , si usa un solo puntero para el ejemplo de abajo (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;
alloc1(p);
//printf("%d ",*p);//value is 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 llamada malloc
dentro de alloc1
, el cambio no corresponde al código en un ámbito diferente.
Aquí hay una respuesta simple !!!!
- Digamos que tienes un puntero que su valor es una dirección.
- Pero ahora quieres cambiar esa dirección.
- podría, haciendo puntero1 = puntero2, y puntero1 ahora tendría la dirección de puntero2.
¡PERO! si desea que una función haga eso por usted, y desea que el resultado persista después de que se realice la función, necesita hacer un trabajo adicional, necesita un nuevo puntero3 solo para apuntar al puntero1 y pasar el puntero3 a la función.
Aquí hay un ejemplo divertido (¡mire la salida de abajo, para entender!):
#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);
}
- Y aquí está la salida:
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!!!.
Como se dijo, una de las aplicaciones de doble puntero es actualizar la cadena para que los cambios realizados se reflejen nuevamente.
#include <iostream>
#include <cstring> // for using strcpy
using namespace std;
void change(char **temp)
{
strcpy(temp[0],"new");
strcpy(temp[1],"value");
}
int main()
{
char **str;
str = (char **)malloc(sizeof(char *)*3);
str[0]=(char *)malloc(10);
str[1]=(char *)malloc(10);
strcpy(str[0],"old");
strcpy(str[1],"name");
char **temp = str; // always use the temporary variable
while(*temp!=NULL)
{
cout<<*temp<<endl;
temp++;
}
temp = str; // making it point it to the original head because we have changed the address in while loop above
change(str);
while(*temp!=NULL)
{
cout<<*temp<<endl;
temp++;
}
free(temp);
free(str[0]);
free(str[1]);
free(str);
El siguiente ejemplo, que estoy dando, dará una idea o una intuición sobre cómo funcionan los punteros dobles, iré a través de los pasos
1) try to understand the following statements
char **str ;
a) str is of type char ** whose value is an address of another pointer.
b) *str is of type char * whose value is an address of variable or (it is a string itself).
c) **str is of type char ,gives the value stored, in this case a character.
el siguiente es el código con el que puede relacionarse con los puntos anteriores (a, b, c) para comprender
str = (char **)malloc(sizeof(char *) *2); // here i am assigning the mem for two char *
str[0]=(char *)"abcdefghij"; // assignin the value
str[1]=(char *)"xyzlmnopqr";
Ahora, para imprimir el valor, es decir, las cadenas en la matriz, solo mire el punto b. En el caso de la cadena, el valor y la dirección son los mismos, por lo que no es necesario volver a hacer referencia al mismo.
cout<<*str<<endl; // abcdefghij;
Ahora, para imprimir la siguiente cadena, salga de una desreferencia, es decir (*) de * str a str y luego incremente, como se muestra a continuación
str++;
ahora imprime la cadena
cout<<*str<<endl; //xyzlmnopqr
ahora para imprimir solo caracteres en una cadena, refiérase al punto c)
cout<<**str<<endl; // prints the first character i.e "a"
Ahora, para imprimir el siguiente carácter de una cadena, es decir, "b" sale de 1 operador de desreferencia y auméntelo, es decir, de ** str a * str y do * str ++
*str++;
ahora imprime el personaje
cout<<**str<<endl; // prints the second character i.e "b"
ya que las dos matrices ("abcdefghij", "xylmnopqr") se almacenan en un bloque continuo de memoria si se hace lo mismo al incrementar la dirección, todos los caracteres de dos cadenas se imprimirán
El siguiente es un ejemplo muy simple de C ++ que muestra que si desea utilizar una función para establecer un puntero para que apunte 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.)
(Además, para referencia: Google ("pasar por valor 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".
Así que 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é sucede en la línea Function_1(&a, b);
?
El "valor" de
&main::a
(una dirección) se copia en el parámetrostd::string* Function_1::a
. Por lo tanto,Function_1::a
es un puntero a (es decir, la dirección de memoria de) la cadenamain::a
.El "valor" de
main::b
(una dirección en la memoria) se copia en el parámetrostd::string* Function_1::b
. Por lo tanto, ahora hay 2 de estas direcciones en la memoria, ambos punteros nulos. En la líneab = a;
, la variable localFunction_1::b
se cambia para que sea igual aFunction_1::a
(=&main::a
), pero la variablemain::b
se modifica. Después de la llamada aFunction_1
,main::b
sigue siendo un puntero nulo.
¿Qué sucede en la línea Function_2(&a, &b);
?
El tratamiento de la variable a es el mismo: dentro de la función,
Function_2::a
es la dirección de la cadenamain::a
.Pero la variable
b
ahora se pasa como un puntero a un puntero. El "valor" de&main::b
(la dirección del punteromain::b
) se copia enstd::string** Function_2::b
. Por lo tanto, dentro de la Función_2, al desreferenciar esto como*Function_2::b
accederemos y modificaremosmain::b
. Entonces la línea*b = a;
en realidad está configurandomain::b
(una dirección) igual aFunction_2::a
(= address ofmain::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 se puede modificar (en el ámbito de la llamada) porque se realiza una copia local.
(Una excepción es si el parámetro es una referencia, como std::string& a
. Pero generalmente son const
. Por lo general, si llama a f(x)
, si x
es un objeto, debería poder asumir que f
won '' t modifique x
. Pero si x
es un puntero, entonces debe asumir que f
podría modificar el objeto apuntado por x
.)
Esperemos que el siguiente ejemplo elimine algunos conceptos relacionados con los punteros y los punteros dobles, sus diferencias y el uso en escenarios comunes.
int* setptr(int *x)
{
printf("%u/n",&x);
x=malloc(sizeof(int));
*x=1;
return x;
}
In the above function setptr we can manipulate x either
1. by taking fn arg as int *x , doing malloc and setting value of x and return x
Or
2. By taking arg as int ** and malloc and then set **x value to some value.
Note: we cant set any general pointer directly without doing malloc.Pointer indicates that it is a type of variable which can hold address of any data type.Now either we define a variable and give reference to it or we declare a pointer(int *x=NULL) and allocate some memory to it inside the called function where we pass x or a reference to it .. In either case we need to have address of a memory in the pointer and in the case pointer initially points to NULL or it is defined like int *x where it points to any random address then we need to assign a valid memory address to pointer
1. either we need to allocate memory to it by malloc
int *x=NULL means its address is 0.
Now we need to either o following
1.
void main()
{
int *x;
x=malloc
*x=some_val;
}
Or
void main()
{
int *x
Fn(x);
}
void Fn(int **x)
{
*x=malloc;
**x=5;
}
OR
int * Fn(int *x)
{
x=malloc();
*x=4;
Return x;
}
2. Or we need to point it to a valid memory like a defined variable inside the function where pointer is defined.
OR
int main()
{
int a;
int *x=&a;
Fn(x);
printf("%d",*x);
}
void Fn(int *x)
{
*x=2;
}
in both cases value pointed by x is changed inside fn
But suppose if we do like
int main()
{
int *x=NULL;
printf("%u/n",sizeof(x));
printf("%u/n",&x);
x=setptr(x);
//*x=2;
printf("%d/n",*x);
return 0;
}
/* output
4
1
*/
#include<stdio.h>
void setptr(int *x)
{
printf("inside setptr/n");
printf("x=%u/n",x);
printf("&x=%u/n",&x);
//x=malloc(sizeof(int));
*x=1;
//return x;
}
int main()
{
int *x=NULL;
printf("x=%u/n",x);
printf("&x=%u/n",&x);
int a;
x=&a;
printf("x=%u/n",x);
printf("&a=%u/n",&a);
printf("&x=%u/n",&x);
setptr(x);
printf("inside main again/n");
//*x=2;
printf("x=%u/n",x);
printf("&x=%u/n",&x);
printf("*x=%d/n",*x);
printf("a=%d/n",a);
return 0;
}
He usado los punteros dobles hoy mientras programaba algo para el trabajo, así que puedo responder por qué tuvimos que usarlos (es la primera vez que tuve que usar punteros dobles). Tuvimos que lidiar con la codificación en tiempo real de los marcos contenidos 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 se estaba cambiando 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 estaba siendo modificado en otro hilo. Al principio no era obvio, al menos para nosotros, que tuviéramos 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 una necesidad cuando se trata de hardware que le devuelve y se dirige a usted.
Las cuerdas son un gran ejemplo de usos de punteros dobles. La cadena en sí es un puntero, por lo que cada vez que necesite apuntar a una cadena, necesitará un puntero doble.
Los punteros a los punteros también son útiles como "manejadores" a la memoria, donde usted quiere pasar un "manejador" entre las funciones y 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 de la manija, y cada función u objeto que usa la manija apuntará correctamente a la memoria recién reubicada (o asignada). A las bibliotecas les gusta hacer esto con tipos de datos "opacos", es decir, con los tipos de datos en los que no tiene que preocuparse por lo que están haciendo con la memoria a la que se apunta, simplemente pase el "identificador" entre los funciones de la biblioteca para realizar algunas operaciones en esa memoria ... las funciones de la biblioteca pueden ser asignar y desasignar la memoria interna sin que tenga que preocuparse explícitamente por el proceso de administración de la memoria o hacia dónde apunta el manejador.
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
Por ejemplo, es posible que desee asegurarse de que cuando libera la memoria de algo, el puntero se establece en nulo después.
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
Cuando llamas a esta función, la llamas con la dirección de un puntero
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
Ahora myMemory
se establece en NULL y cualquier intento de reutilización será muy obvio.
Por ejemplo, si desea un acceso aleatorio a datos no contiguos.
p -> [p0, p1, p2, ...]
p0 -> data1
p1 -> data2
-- Cía
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 un dato.
Si sizeof(T)
es grande, puede que no sea posible asignar un bloque contiguo (es decir, usando malloc) de sizeof(T) * n
bytes.
Si desea tener una lista de caracteres (una palabra), puede usar la char *word
Si desea una lista de palabras (una oración), puede usar la char **sentence
Si desea una lista de oraciones (un monólogo), puede usar el char ***monologue
Si desea una lista de monólogos (una biografía), puede utilizar la char ****biography
Si quieres una lista de biografías (una bio-biblioteca), puedes usar char *****biolibrary
Si quieres una lista de bio-bibliotecas (a ?? lol), puedes 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:
total words in my lol: 243
Una cosa para la que los uso constantemente es cuando tengo una variedad de objetos y necesito realizar búsquedas (búsqueda binaria) en diferentes campos.
Guardo la matriz original ...
int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);
Luego haz una serie 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 mediante los datos que tiene. La matriz original de objetos puede permanecer sin clasificar, pero cada matriz de punteros se ordenará por su campo especificado.
Una de las razones es que desea cambiar el valor del puntero pasado a una función como argumento de la función, para ello necesita un puntero a un puntero.
En palabras sencillas, Use **
cuando desee conservar (O conservar el cambio en) la asignación o asignación de memoria incluso fuera de una llamada de función. (Por lo tanto, pase dicha función con doble puntero arg.)
Puede que este no sea un 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);
}
Vi un muy buen ejemplo hoy, de esta publicación de blog , como lo resumiré a continuación.
Imagina que tienes una estructura para nodos en una lista enlazada, que probablemente sea
typedef struct node
{
struct node * next;
....
} node;
Ahora desea implementar una función remove_if
, que acepta un criterio de eliminación rm
como uno de los argumentos y atraviesa la lista enlazada: si una entrada satisface el criterio (algo como 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 enlazada.
Usted puede 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 su bucle for
. El mensaje es que, sin los punteros dobles, debe mantener una variable prev
para reorganizar los punteros y manejar los dos casos diferentes.
Pero con 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 una prev
ahora porque puede modificar directamente lo que prev->next
apunta .
Para aclarar las cosas, sigamos un poco el código. Durante el retiro:
- si la
entry == *head
: será*head (==*curr) = *head->next
-head
ahora apunta al puntero del nuevo nodo de rumbo. Para ello, cambie directamente el contenido de lahead
a un nuevo puntero. - si
entry != *head
: similarmente,*curr
es a lo queprev->next
refiere, y ahora apunta aentry->next
.
No importa en qué caso, puede reorganizar los punteros de forma unificada con punteros dobles.
la aplicación de doble puntero como lo muestra Bhavuk Mathur parece estar equivocada. Aquí el siguiente ejemplo es el válido.
void func(char **str)
{
strcpy(str[0],"second");
}
int main(){
char **str;
str = (char **)malloc(sizeof(char*)*1); // allocate 1 char* or string
str[0] = (char *)malloc(sizeof(char)*10); // allocate 10 character
strcpy(str[0],"first"); // assign the string
printf("%s/n",*str);
func(str);
printf("%s/n",*str);
free(str[0]);
free(str);
}
1. Concepto Básico -
Cuando declare lo siguiente: -
1. char * ch - (llamado puntero de carácter)
- ch contiene la dirección de un solo carácter.
- (* ch) hará referencia al valor del carácter.
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) hará referencia al valor exacto del carácter.
Agregando más punteros, expanda la dimensión de un tipo de datos, de carácter a cadena, a una matriz de cadenas, y así sucesivamente ... Puede relacionarlo con una matriz 3d 1d, 2d ..
Por lo tanto, el uso del puntero depende de cómo lo declara.
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 los punteros dobles:
(Esto también cubriría pasar por referencia)
Supongamos que desea actualizar un carácter de una función. Si intentas 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 un carácter.
Para esto, necesita 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.