2010-08-15 17 views
17

Supongamos que tengo una matriz de punteros a char en C:Cómo ordenar una matriz de punteros a char en C?

char *data[5] = { "boda", "cydo", "washington", "dc", "obama" }; 

y quiero resolver esto matriz mediante qsort:

qsort(data, 5, sizeof(char *), compare_function); 

Soy incapaz de llegar a la función de comparación. Por alguna razón esto no funciona:

int compare_function(const void *name1, const void *name2) 
{ 
    const char *name1_ = (const char *)name1; 
    const char *name2_ = (const char *)name2; 
    return strcmp(name1_, name2_); 
} 

Hice un montón de búsqueda y se encontró que tenía que utilizar ** interior de qsort:

int compare_function(const void *name1, const void *name2) 
{ 
    const char *name1_ = *(const char **)name1; 
    const char *name2_ = *(const char **)name2; 
    return strcmp(name1_, name2_); 
} 

Y esto funciona.

¿Alguien puede explicar el uso de *(const char **)name1 en esta función? No lo entiendo en absoluto. ¿Por qué el doble puntero? ¿Por qué no funcionó mi función original?

Gracias, Boda Cydo.

+2

'data' debe declararse' const'. –

+0

Billy, si es const, ¿todavía se puede ordenar? – bodacydo

+1

Sí. La matriz puede ser no 'const', pero los punteros contenidos dentro de esa matriz deben ser' const'. No está permitido modificar literales constantes en tiempo de compilación como ese (es un comportamiento indefinido para hacerlo). Para obtener eso, quieres 'const char * data [5]'. Si quieres que la matriz también sea constante, harías 'const char * const data [5]'. –

Respuesta

17

Si ayuda a mantener las cosas claras en su cabeza, el tipo al que debe aplicar los punteros en su comparador es el mismo que el tipo original del puntero de datos que pasa al qsort (que los documentos qsort llaman base). Pero para que qsort sea genérico, simplemente maneja todo como void*, independientemente de lo que "realmente" sea.

Por lo tanto, si está ordenando una matriz de enteros, pasará un int* (convertido a). qsort le devolverá dos punteros void* al comparador, que convertirá a int*, y la desreferencia para obtener los valores int que realmente compara.

Ahora reemplace int con char*:

si estás ordenar una matriz de char*, a continuación, se pasa en un char** (convertido a void*). qsort le devolverá dos punteros void* al comparador, que convertirá a char**, y la desreferencia para obtener los valores char* que realmente compara.

En su ejemplo, porque está utilizando una matriz, la char** que pasa es el resultado de la matriz de char* "en descomposición" en un puntero a su primer elemento. Como el primer elemento es char*, un puntero a este es char**.

3

Imagine que sus datos eran double data[5].

Su método de comparación recibiría punteros (dobles *, pasados ​​como nulos *) a los elementos (dobles).
Ahora reemplace doble con char * nuevamente.

2

qsort es lo suficientemente general como para ordenar matrices que consta de otras cosas que los punteros. Es por eso que el parámetro de tamaño está ahí. No puede pasar los elementos del conjunto a la función de comparación directamente, ya que no sabe en tiempo de compilación qué tan grandes son. Por lo tanto, pasa punteros. En su caso, obtendrá punteros al char *, char **.

+0

No entiendo, lo siento. El primer argumento para 'qsort' es un' * '. Paso un '**'. Lo que significa que paso efectivamente solo un '*'. Pero uno '*' es exactamente un 'char *'. ¿Ver? Es por eso que estoy confundido. – bodacydo

+0

@bodacydo: El punto importante es que la función de comparación toma punteros a * elementos * de la matriz. Como cada elemento de su matriz es un puntero a char, la función de comparación opera en punteros-a-punteros-a-char. – jamesdlin

0

de man qsort:

The contents of the array are sorted in ascending 
order according to a comparison function pointed to by 
compar, which is called with two arguments that **point** 
to the objects being compared. 

así que suena como la función de comparación recibe punteros a los elementos de la matriz. Ahora un puntero a char * es char ** (es decir, un puntero a un puntero a un carácter).

0

char *data[5] = { "boda", "cydo", "washington", "dc", "obama" };

es una declaración pidiendo al compilador para una matriz de tamaño 5 de punteros a carácter. Ha inicializado esos punteros a literales de cadenas, pero para el compilador, sigue siendo una matriz de cinco punteros.

Cuando pasa esa matriz a qsort, la matriz de punteros se desintegra en un puntero que apunta al primer elemento, de acuerdo con las reglas de paso del parámetro de matriz C.

Por lo tanto, debe procesar un nivel de indirección antes de poder acceder a las matrices de caracteres reales que contienen las constantes.

2

La función de comparación toma punteros al tipo de objeto que está en la matriz que desea ordenar. Como la matriz contiene char *, su función de comparación toma punteros a char *, también conocido como char **.

0

@bodacydo aquí es un programa que puede explicar lo que otros programadores están tratando de transmitir, pero esto sería en el contexto de "enteros"

#include <stdio.h> 


int main() 
{ 
    int i , j; 
    int *x[2] = {&i, &j}; 

    i = 10; j = 20; 

    printf("in main() address of i = %p, address of j = %p \r\n", &i, &j); 

    fun(x); 
    fun(x + 1); 

    return 0; 
} 


void fun(int **ptr) 
{ 
    printf("value(it would be an address) of decayed element received = %p, double dereferenced value is %d \r\n",*ptr, **ptr); 
    printf("the decayed value can also be printed as *(int **)ptr = %p \r\n", *(int **)ptr); 
} 
Cuestiones relacionadas