2009-03-28 7 views
8

Estoy preparando algunas diapositivas para una clase introductoria de C, y estoy tratando de presentar buenos ejemplos (y motivación) para usar la aritmética de puntero sobre la subscripción de matriz.¿Cuáles son los ejemplos convincentes en los que la aritmética de puntero es preferible a la subscripción de matriz?

Muchos de los ejemplos que veo en libros son bastante equivalentes. Por ejemplo, muchos libros muestran cómo revertir el caso de todos los valores en una cadena, pero con la excepción de reemplazar una a [i] con una * p, el código es idéntico.

Estoy buscando un buen (y corto) ejemplo con arreglos unidimensionales donde la aritmética del puntero puede producir un código significativamente más elegante. ¿Algunas ideas?

Respuesta

15

Conseguir un puntero de nuevo en lugar de un valor:

Uno por lo general utiliza la aritmética de punteros cuando quieren obtener un puntero de nuevo. Para obtener un puntero mientras usa un índice de matriz: está 1) calculando el desplazamiento del puntero, luego 2) obteniendo el valor en esa ubicación de memoria, luego 3) debe usar & para obtener la dirección nuevamente. Eso es más tipeo y menos sintaxis limpia.

Ejemplo 1: Digamos que necesita un puntero al byte de 512º en un buffer

char buffer[1024] 
char *p = buffer + 512; 

es más limpio que:

char buffer[1024]; 
char *p = &buffer[512]; 

Ejemplo 2: strcat más eficiente

char buffer[1024]; 
strcpy(buffer, "hello "); 
strcpy(buffer + 6, "world!"); 

Este es más limpio que:

char buffer[1024]; 
strcpy(buffer, "hello "); 
strcpy(&buffer[6], "world!"); 

usando aritmética de puntero ++ como un iterador:

Incrementar punteros con ++, y decrementar con - es útil cuando iterar sobre cada elemento de una variedad de elementos. Es más limpio que usar una variable separada utilizada para realizar un seguimiento del desplazamiento.


puntero resta:

Usted puede utilizar restas puntos con la aritmética de punteros. Esto puede ser útil en algunos casos para obtener el elemento antes del que está señalando. Se puede hacer con subíndices de matriz también, pero se ve muy mal y confuso. Especialmente a un programador python donde se da un subíndice negativo para indexar algo del final de la lista.

+0

Pero todavía tiene el número total de variables. – Uri

+1

Ya sabes, una cosa incluso ** limpiadora ** para el segundo ejemplo sería 'sprintf (buffer,"% s% s "," hello "," world! ")'. –

+1

@sgm, y caro –

3
char *my_strcpy(const char *s, char *t) { 
    char *u = t; 
    while (*t++ = *s++); 
    return u; 
} 

¿Por qué querrías estropear tal belleza con un índice? (Consulte K & R y cómo se basan en este estilo.) Hay una razón por la que utilicé la firma anterior tal como está. Deje de editar sin pedir una aclaración primero. Para aquellos que piensan que saben, busque la firma actual; se saltaron algunas calificaciones restrict.

Prueba de alineación de estructuras y la implementación macro offsetof.

+1

Pensé en usar ese, pero todavía siento que le preocupa que no convencerá a los estudiantes, todavía se asigna a un código muy similar con índices. – Uri

+0

Pero esa es una variable extra que no necesita usar. – dirkgently

+0

No soy quien lo está editando y por qué. – Uri

1

iteración a través de una matriz de 2 dimensiones, donde la posición de un dato realmente no importa
si no punteros de uso, que tendría que realizar un seguimiento de dos subíndices
con punteros, usted podría apuntar a la parte superior de su matriz, y con un solo bucle, comprimir todo

1

Si estaba usando un compilador antiguo, o algún tipo de compilador especializado de sistemas integrados, podría tener ligeras diferencias de rendimiento, pero la mayoría de los compiladores modernos probablemente optimizar estas (pequeñas) diferencias.

El siguiente artículo puede ser algo que se podía recurrir a - depende del nivel de sus estudiantes:

http://geeks.netindonesia.net/blogs/risman/archive/2007/06/25/Pointer-Arithmetic-and-Array-Indexing.aspx

0

A menudo, la elección es sólo uno de estilo - uno ve o se siente más natural que el otro para un caso particular.

También existe el argumento de que el uso de índices puede hacer que el compilador tenga que volver a calcular repetidamente las compensaciones dentro de un bucle. No estoy seguro de cuán a menudo es este el caso (excepto en versiones no optimizadas), pero imagino sucede, pero es probable que rara vez sea un problema.

Un área que creo que es importante a largo plazo (que podría no aplicarse a una clase introductoria C, pero aprende temprano, digo) es que el uso de la aritmética de puntero se aplica a las expresiones idiomáticas utilizadas en C++ STL. Si logra que entiendan la aritmética del puntero y lo usen, cuando pasen al STL, tendrán una ventaja sobre cómo usar los iteradores correctamente.

1

Estás preguntando sobre C específicamente, pero C++ se basa en esto, así:

mayoría aritmética de punteros generaliza de forma natural al concepto Adelante iterador. Recorrer la memoria con *p++ se puede utilizar para cualquier contenedor secuenciado (lista vinculada, lista de omisiones, vector, árbol binario, árbol B, etc.), gracias a la sobrecarga del operador.

0

Algo divertido que espero que nunca tengas que tratar: los punteros pueden alias, mientras que los arreglos no. Aliasing puede causar todo tipo de generación de código no ideal, la más común de las cuales es usar un puntero como un parámetro de salida a otra función. Básicamente, el compilador no puede asumir que el puntero utilizado por la función no se alía ni a sí mismo ni a nada en ese marco de pila, por lo que tiene que volver a cargar el valor del puntero cada vez que se utiliza. O más bien, para estar seguro, lo hace.

1

La aritmética del puntero puede parecer elegante y "hacker", pero nunca he encontrado un caso, era MÁS RÁPIDO que la indexación estándar. Todo lo contrario, a menudo encuentro casos cuando ralentiza el código por un factor grande.

Por ejemplo, el bucle secuencial típico a través de una matriz con un puntero puede ser menos eficiente que el bucle con un índice clásico en un procesador moderno, que admite extensiones SSE. La aritmética del puntero en un bucle bloquea suficientemente a los compiladores para realizar la vectorización del bucle, lo que puede generar un aumento del rendimiento típico de 2x-4x. Además, el uso de punteros en lugar de variables enteras simples puede dar lugar a operaciones de almacenamiento de memoria innecesarias debido al alias del puntero.

Así que, en general, Aritmética de punteros en lugar de acceso indexado estándar nunca debe ser recomendada.

1
#include ctype.h 
void skip_spaces(const char **ppsz) 
{ 
    const char *psz = *ppsz; 
    while(isspace(*psz)) 
    psz++; 
    *ppsz = psz; 
} 

void fn(void) 
{ 
    char a[]=" Hello World!"; 
    const char *psz = a; 
    skip_spaces(&psz); 
    printf("\n%s", psz); 
} 
Cuestiones relacionadas