2011-02-14 11 views
9

Me gustaría terminar mi confusión con el char **char ** y eliminación de referencias punteros

Cuando una vez que crea una serie de matrices de caracteres (strings) ¿Cómo char ** realmente lograr esto?

entiendo que char * es un puntero a un char y que char *array[] es una matriz de punteros de char, pero lo que hace exactamente char ** hace y cómo lo hace?

También cuando escucho la palabra referencias, me hace pensar que el puntero se elimina, ¿qué significa exactamente la desreferencia de un puntero? ¿Cambiar el valor al que apunta el puntero?

Gracias

Respuesta

10

"Desreferenciar" significa un puntero acceder al valor de los puntos de puntero a. Asumir las siguientes declaraciones:

int a = 10; 
int *p = &a; 

Así es un mapa de memoria hipotética de las dos variables:

 
Item  Address  0x00 0x01 0x02 0x03 
----  -------  ---- ---- ---- ---- 
    a  0x80001000 0x00 0x00 0x00 0x0A 
    p  0x80001004 0x80 0x00 0x10 0x00 

a contiene el valor entero 10. p contiene la dirección del a (0x80001000). Si queremos acceder al contenido de a a través de p, desreferenciap con el operador de direccionamiento indirecto *. Por lo tanto, la expresión *p es equivalente a la expresión a. Si escribimos

*p = 16; 

que es lo mismo que escribir

a = 16; 

Aquí está un breve fragmento de código que muestra cómo utilizar un objeto de tipo char ** para crear una matriz de cadenas:

#include <stdlib.h> 
#define N  20 // For this example, we will allocate 20 strings 
#define LENGTH 10 // of 10 characters each (not counting 0 terminator) 
... 
char **arr = malloc(sizeof *arr * N); 
if (arr) 
{ 
    size_t i; 
    for (i = 0; i < N; i++) 
    { 
    arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1)); 
    strcpy(arr[i], "   "); 
    } 
} 

Al pasar por línea por línea,

char **arr = malloc(sizeof *arr * N); 

asigna un bloque de N elementos, cada uno lo suficientemente grande para almacenar un puntero a char (sizeof *arr == sizeof (char *) ya que el tipo de *arr == char *), y asigna el valor del puntero resultante a arr. IOW, arr apunta al primer puntero a char, de ahí el tipo char **. Tenga en cuenta que si usted se separó la declaración y la llamada de función, que se vería así

char **arr; 
... 
arr = malloc(sizeof *arr * N); 

Queremos asignar el resultado de malloc a arr, no a lo que arrpuntos a.

if (arr) 

Es posible que malloc a fallar, por lo que queremos comprobar el resultado antes de usarlo. En caso de que falle malloc, devolverá un valor de puntero NULL.

{ 
    size_t i; 
    for (i = 0; i < N; i++) 
    { 
    arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1)); 

Para cada puntero de carácter arr[i], asignamos un bloque de memoria lo suficientemente grande para la longitud 1 elementos +, cada una lo suficientemente grande como para contener un valor char (sizeof *arr[i] == sizeof (char), ya que el tipo de *arr[i] == char; tenga en cuenta que sizeof (char) es siempre 1) y asigne el resultado al arr[i].

Dado que asignamos cada cadena con una llamada malloc por separado, es poco probable que sean contiguas en la memoria. Aquí hay otro mapa de memoria que muestra una posible resultado del código anterior:

 
Item   Address  0x00 0x01 0x02 0x03 
----   -------  ---- ---- ---- ---- 
arr   0x80001000  0xA0 0xCC 0x00 0x00 
      ... 
arr[0]  0xA0CC0000  0xA0 0xCC 0x20 0x00  
arr[1]  0xA0CC0004  0xA0 0xCC 0x20 0x40 
arr[2]  0xA0CC0008  0xA0 0xCC 0x21 0x28 
      ... 
arr[19]  0xA0CC0014  0xA0 0xCC 0x23 0x10 
      ... 
arr[0][0] 0xA0CC2000  ' ' ' ' ' ' ' ' 
arr[0][4] 0xA0CC2004  ' ' ' ' ' ' ' ' 
arr[0][8] 0xA0CC2008  ' ' ' ' 0x00 0x?? 
      ... 
arr[1][0] 0xA0CC2040  ' ' ' ' ' ' ' ' 
arr[1][4] 0xA0CC2044  ' ' ' ' ' ' ' ' 
arr[1][8] 0xA0CC2048  ' ' ' ' 0x00 0x?? 
      ... 
+1

Disculpa, soy un novato, así que tal vez esté mal, pero para limpiar totalmente después de la asignación, deberías recorrer el número total de cadenas y el acceso gratuito [i ] así como liberar toda la matriz libre (arr), ¿es así? ¿O sería suficiente simplemente 'free (arr);'? –

3

eliminación de referencias a un puntero significa acceder al valor del puntero puntos a. Por ejemplo,

char c = 'c'; // This is a primitive value. You cannot dereference it. 
char* p1 = &c; // A pointer to the address of c 
char** p2 = &p1; // A pointer to the address of p1 
/* Now, the following is true: 
*p1 == c, i.e. dereferencing p1 allows us to read from/write to c. 
*p2 == p1 
**p2 == *(*p2) == *p1 = c - dereferencing p2 twice is c, too */ 

La razón por la cual se utiliza un puntero a C en lugar de C es directamente un puntero que le permite acceder a más de 1 valor. Tome este ejemplo:

char[4] str; 
char c0 = 'a', c1 = 'b', c3 = 'c', c4 = '\0'; 
str[0] = c0; str[1] = c1; str[2] = c2; str[3] = c3; 
str = "abc"; // Same as the above line 

Supongamos que necesitamos el segundo carácter. Podríamos acceder a él con c1. Pero como pueden ver, esta notación es realmente engorrosa. Además, si leemos la cadena de un archivo en lugar de escribirlo, tendríamos que hacer cosas complicadas. En su lugar, simplemente escribimos

str[1] /* or */ *(str+1) 

Tenga en cuenta que el primer elemento tiene el índice de 0, el segundo 1 - es por eso que estamos usando 1 aquí. Un char** lo convierte en once: tenemos una matriz de una serie de caracteres. Supongamos que tenemos una matriz así, llamémosla input, y necesitamos averiguar la longitud de todas las cadenas en ella. Esta es la forma en que lo haría:

int lensum(char **input) { 
    int res = 0; 
    while (*input) { // This loops as long as the value input points to is not 0. 
     char* p = *input; // Make a copy of the value input currently points to 
     while (*p != '\0') { // Loop while the copy does not point to a char '\0' 
      res += 1; // We found a character 
      p++; // Check next character in the next iteration 
     } 
     input++; // Check next string in the next iteration 
    } 
    return res; 
} 
+0

"* p1 == c, es decir, p1 eliminación de referencias nos permite leer desde/escribir en c." ¿En qué línea se hace esto? char * p1 = & c; ? – jarryd

+0

@alJaree Exactamente. & c es la dirección de c. – phihag

9

Un puntero es un tipo que tiene una dirección en un valor, en lugar de mantener el valor real.

Entonces, en el caso de char * p, una vez asignada, p contendrá una dirección A. Desviar ese puntero significa acceder al valor almacenado en la dirección A. La razón por la que puede almacenar cadenas en un char * es porque la memoria que se asigna es contigua. Entonces, A es una dirección que almacena el primer carácter, A + 1 es una dirección que almacena el segundo carácter y así sucesivamente.

En el caso de char ** pp, almacena una dirección de un char *. Llame a esta dirección B. Por lo tanto, desreferenciar pp significa acceder al valor en la dirección B, que resulta ser un char *, lo que sucede al mantener una cadena. De la misma manera, B + 1 (en realidad B + sizeof (char *)) almacena el siguiente valor, que es otra cadena.

Descartar pp dos veces (es decir, ** pp) significa que primero está accediendo al valor en la dirección B, que por ejemplo es A, y luego desreferenciando eso una vez más para obtener el valor en la dirección A, que es algún carácter.

+0

Explicación realmente agradable. Gracias. – jarryd

3

Los diagramas valen unas 1000 palabras. Eche un vistazo here

char, char * y char ** son simplemente tipos que describen qué contiene una variable (área de memoria).

El uso de la desreferenciación como * variable en realidad dice tratar el valor en la variable como una dirección de memoria y realmente devolver el valor en esa dirección. Esto es indirección.

** variable es simplemente dos niveles de indirección. es decir, el valor en la variable es una dirección de memoria de otra dirección de memoria de los datos que se devolverán.

direcciones normalmente provienen de la dirección del operador, & o de una asignación de memoria de función/operadores como new

+1

Gracias :) 5 más para ir ..: P – jarryd

Cuestiones relacionadas