2009-03-03 6 views

Respuesta

19

Casi, pero no del todo. La respuesta correcta es:

*((*(a+1))+2) 

porque necesita primero de-referencia a uno de los punteros de cadena reales y luego de retirar la información que selecciona cadena puntero hacia abajo hasta el carácter deseado. (Tenga en cuenta que agregué paréntesis adicionales para mayor claridad en el orden de las operaciones allí).

Por otra parte, esta expresión:

a[1][2] 

también funcionará .... y tal vez sería preferible debido a que la intención de lo que estamos tratando de hacer es más evidente y la notación en sí es más sucinta! . Este formulario puede no ser inmediatamente obvio para las personas nuevas en el lenguaje, pero entiendo que la razón por la que funciona la notación de matriz es porque en C, una operación de indexación de matriz es solo una abreviatura de la operación de puntero equivalente. es decir: * (a + x) es igual que a [x]. Entonces, extendiendo esa lógica a la pregunta original, hay dos operaciones de desreferenciación de puntero separadas en cascada, por lo que la expresión a [x] [y] es equivalente a la forma general de * ((* (a + x)) + y)

+0

¿De modo que los punteros a los punteros se pueden usar como una matriz sin ningún otro tipo de declaración? – sdellysse

+0

Tendría +1, si explica por qué. –

+0

detrás de escena, a [b] se traduce en * (a + b) –

1

Probar a[1][2]. O *(*(a+1)+2).

Básicamente, las referencias de matriz son azúcar sintáctico para la desreferenciación de punteros. a [2] es lo mismo que a + 2, y también lo mismo que 2 [a] (si realmente quiere un código ilegible). Una matriz de cadenas es igual a un puntero doble. Entonces puede extraer la segunda cadena usando un [1] o *(a+1). A continuación, puede encontrar el tercer carácter en esa cadena (llámalo 'b' por el momento) con b [2] o *(b + 2). Sustituyendo la segunda cuerda original por 'b', terminamos con un [1] [2] o *(*(a+1)+2).

2

IIRC, una cadena es en realidad un conjunto de caracteres, así que esto debería funcionar:

a[1][2] 
3

Usted no tiene que utilizar punteros.

int main (int argc, char ** argv) {

printf ("El tercer carácter de argv [1] es [% c]. \ N", argv [1] [2 ]);

}

continuación:

$ ./main hola El tercer personaje de argv [1] es [l].

Esa es una y una.

Puede usar punteros si quiere ...

* (argv [1] +2)

o incluso

* ((* (a + 1)) + 2)

Como alguien señalado arriba.

Esto se debe a que los nombres de matriz son punteros.

+0

Pendantry: argv [1] [3] si el cuarto personaje de argv [1] ... – dmckee

+0

Sí. Solo lo cambié Eso es lo que recibo por intentar publicar rápidamente, y no revisar mis subíndices. –

2

Cita de la wikipedia article sobre los punteros de C -

En C, gama de indexación se define formalmente en términos de la aritmética de punteros; es decir, la especificación del lenguaje requiere que la matriz [i] sea equivalente a * (matriz + i). Por lo tanto, en C, las matrices pueden considerarse punteros a áreas de memoria consecutivas (sin espacios), y la sintaxis para acceder a las matrices es idéntica a la que se puede usar para desreferenciar los punteros . Por ejemplo, un array puede ser declarado y se utiliza de la manera siguiente:

int array[5];  /* Declares 5 contiguous (per Plauger Standard C 1992) integers */ 
int *ptr = array; /* Arrays can be used as pointers */ 
ptr[0] = 1;  /* Pointers can be indexed with array syntax */ 
*(array + 1) = 2; /* Arrays can be dereferenced with pointer syntax */ 

Así, en respuesta a su pregunta - , punteros a punteros se pueden utilizar como una matriz sin ningún tipo de otra declaración en ¡todas!

1

Hay una brillante explicación de la programación en C en el libro Hackear el arte de la explotación Segunda edición de Jon Erickson que trata sobre los punteros, cadenas, que vale la pena mencionar solo en la sección de explicación de la programación https://leaksource.files.wordpress.com/ 2014/08/hacking-the-art-of-exploitation.pdf.

Aunque la pregunta ya ha sido respondida, alguien más que desee saber más puede encontrar los siguientes aspectos destacados del libro de Erickson útiles para comprender parte de la estructura detrás de la pregunta.

encabezados

Ejemplos de archivos de cabecera disponibles para la manipulación de variables que probablemente va a utilizar.

stdio.h - http://www.cplusplus.com/reference/cstdio/

stdlib.h - http://www.cplusplus.com/reference/cstdlib/

cadena. h - http://www.cplusplus.com/reference/cstring/

limits.h - http://www.cplusplus.com/reference/climits/

Funciones

Ejemplos de funciones de propósito general que probablemente utilizará.

malloc() - http://www.cplusplus.com/reference/cstdlib/malloc/

calloc() - http://www.cplusplus.com/reference/cstdlib/calloc/

strcpy() - http: //www.cplusplus.com/referencia/cstring/strcpy/

memoria

" la memoria de un programa compilado se divide en cinco segmentos:. texto, datos, BSS, montón, y la pila Cada segmento representa una parte especial de memoria que se reserva para un fin determinado. el segmento de texto también se le llama el segmento de código. Aquí es donde las instrucciones en lenguaje máquina montada del programa se encuentran".

" La ejecución de las instrucciones en este segmento no es lineal, gracias a las citadas estructuras de alto nivel de control y funciones, que compilan en rama, saltar, y llaman a instrucciones en lenguaje ensamblador. Como programa ejecuta, el EIP se establece en la primera instrucción en el segmento de texto el procesador sigue entonces un bucle de ejecución que hace lo siguiente:. "

" 1. Lee la instrucción que EIP está apuntando a "

" 2. Añade la longitud en bytes de la instrucción a EIP "

" 3. ejecuta la instrucción que se ha leído en el paso 1 "

" 4. vuelve al paso 1 "

" veces la instrucción habrá un salto o una instrucción de llamada, que cambia la EIP a una dirección diferente de la memoria. El procesador no atención sobre el cambio, porque se espera que la ejecución sea no lineal de todos modos. Si EIP se cambia en el paso 3, el procesador se acaba de volver al paso 1 y lea la instrucción que se encuentra en la dirección de lo que sea EIP fue cambiado a "

" permiso de escritura está desactivado en el segmento de texto, como no se usa para almacenar variables, solo código. Esto evita que las personas realmente modifiquen el código del programa; cualquier intento de escribir en este segmento de la memoria hará que el programa para alertar al usuario de que sucedió algo malo, y el programa será matado. Otra ventaja de este segmento de solo lectura es que se puede compartir entre diferentes copias del programa, permitiendo múltiples ejecuciones del programa al mismo tiempo sin ningún problema. Debe también tenerse en cuenta que este segmento de memoria tiene un tamaño fijo, ya que nunca nada cambios en ella "

" Los datos y los segmentos del SRS se utilizan para almacenar el programa global y estática variables. El segmento de datos se llena con las variables globales y estáticas inicializados, mientras que el segmento bss se llena con sus homólogos no inicializados. Aunque estos segmentos son grabables, también tienen un tamaño fijo. Recuerde que las variables globales persisten, a pesar del contexto funcional (como la variable j en los ejemplos anteriores). Tanto las variables globales y estáticas son capaces de persistir ya que se almacenan en sus propios segmentos de memoria "

" El segmento montón es un segmento de memoria que un programador puede directamente control. Los bloques de memoria en este segmento se pueden asignar y usar para lo que el programador pueda necesitar.Un punto notable sobre el segmento montón es que no es de tamaño fijo, por lo que puede crecer más grande o más pequeño, según sea necesario "

" Todos los de la memoria dentro del montón es administrado por asignador y deallocator algoritmos , que respectivamente reservan una región de memoria en el montón para su uso y eliminan las reservas para permitir que esa parte de la memoria se reutilice para reservas posteriores. El montón crecerá y disminuirá según cómo se reserve la memoria . Esto significa que un programador que usa las funciones de asignación del montón puede reservar y liberar memoria sobre la marcha. El crecimiento de el montón se mueve hacia abajo, hacia la memoria superior aborda "

" El segmento de pila también tiene tamaño variable y se utiliza como un bloc de notas temporal para almacenar variables de la función y el contexto locales durante las llamadas de función. Esto es lo que mira el comando backtrace de GDB. Cuando un programa llama a una función, esa función tendrá su propio conjunto de variables pasadas, y el código de la función estará en una ubicación de memoria diferente en el segmento de texto (o código). Dado que el contexto y el EIP deben cambiar cuando se llama a una función, la pila se usa para recordar todas las variables pasadas, la ubicación a la que debe volver el EIP después de que se finaliza la función y todas las variables locales utilizadas por esa función. Toda esta información se almacena en la pila en lo que colectivamente se llama un marco de pila. La pila contiene muchos marcos de pila "

" En términos generales de informática, una pila es una estructura de datos abstracta que se utiliza con frecuencia. Tiene orden de primer entrada, último en salir (FILO) , lo que significa que el primer elemento que se coloca en una pila es el último elemento que sale. Piense en ello como poner perlas en un trozo de cuerda que tiene un nudo en un extremo; no puede sacar el primer talón hasta que haya eliminado todas las demás. Cuando un elemento se coloca en una pila, se le conoce como empujar, y cuando se elimina un elemento de una pila, que se llama apareciendo "

" Como su nombre lo indica, el segmento de pila de la memoria es, de hecho, , una estructura de datos de pila, que contiene marcos de pila. El registro de ESP se utiliza para realizar un seguimiento de la dirección del final de la pila, que cambia constantemente a medida que los elementos se insertan y salen de ella. Como este es un comportamiento muy dinámico, tiene sentido que la pila tampoco sea de un tamaño fijo. Frente a la dinámica de crecimiento de la pila, como el cambio de pila s de tamaño, que crece hacia arriba en una lista visual de la memoria, a la memoria más baja direcciones "

" La naturaleza FILO de una pila podría parecer extraño , pero como la pila se usa para almacenar el contexto, es muy útil. Cuando se llama a una función, varias cosas se envían juntas a la pila en un marco de pila. El registro EBP, a veces llamado puntero de marco (FP) o puntero de base local (LB) , se utiliza para hacer referencia a variables de función locales en el marco de pila actual. Cada marco de pila contiene los parámetros de la función, sus variables locales y dos indicadores que son necesarios para volver a colocar las cosas como estaban: el puntero de marco guardado (SFP) y la dirección de retorno. El SFP se usa para restaurar EBP a su valor anterior, y la dirección de retorno se usa para restablecer el EIP a la siguiente instrucción encontrada después de la llamada a la función. Esto restaura el contexto funcional de la anterior marco de pila "

Strings

" En C, una matriz es simplemente una lista de n elementos de un tipo de datos específico. Una matriz de 20 caracteres es simplemente 20 caracteres adyacentes ubicados en la memoria.Las matrices se denominan también como tampones "

#include <stdio.h> 

int main() 
{ 
    char str_a[20]; 
    str_a[0] = 'H'; 
    str_a[1] = 'e'; 
    str_a[2] = 'l'; 
    str_a[3] = 'l'; 
    str_a[4] = 'o'; 
    str_a[5] = ','; 
    str_a[6] = ' '; 
    str_a[7] = 'w'; 
    str_a[8] = 'o'; 
    str_a[9] = 'r'; 
    str_a[10] = 'l'; 
    str_a[11] = 'd'; 
    str_a[12] = '!'; 
    str_a[13] = '\n'; 
    str_a[14] = 0; 
    printf(str_a); 
} 

" En el programa anterior, un carácter matriz de 20-elemento se define como str_a, y cada elemento de la matriz se escribe en, uno por uno. Observe que el número comienza en 0, en oposición a 1. También observe que el último carácter es 0 ".

" (Esto también se conoce como byte nulo). Se definió la matriz de caracteres, por lo que 20 bytes están asignados para ello, pero solo 12 de estos bytes se usan realmente. La programación de byte nulo al final se usa como un carácter delimitador para indicar a cualquier función que está tratando con la cadena que detenga las operaciones allí mismo. Los bytes adicionales restantes son solo basura y se ignorarán. Si un byte nulo se inserta en el quinto elemento de la matriz de caracteres, sólo los caracteres Hola sería impresa por la función printf() "

" Desde el establecimiento de cada carácter en una matriz de caracteres es laborioso y cadenas son usado con bastante frecuencia, se creó un conjunto de funciones estándar para la manipulación de cadenas. Por ejemplo, la función strcpy() copiará una cadena de un origen a un destino, iterando a través de la cadena de origen y copiando cada byte al destino (y deteniéndolo después de copiar el byte de terminación nulo) ".

" El orden de los argumentos de las funciones es similar al destino de la sintaxis del ensamblado Intel primero y luego al origen. El programa char_array.c se puede reescribir usando strcpy() para lograr lo mismo utilizando la biblioteca de cadenas. La próxima versión del programa char_array se muestra a continuación incluye string.h ya que utiliza una función de cadena".

#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char str_a[20]; 
    strcpy(str_a, "Hello, world!\n"); 
    printf(str_a); 
} 

encontrar más información sobre cadenas de C

http://www.cs.uic.edu /~jbell/CourseNotes/C_Programming/CharacterStrings.html

http://www.tutorialspoint.com/cprogramming/c_strings.htm

Punteros

" El registro EIP es un puntero que" apunta "a la instrucción actual durante la ejecución de un programa al contener su dirección de memoria. La idea de punteros también se usa en C. Como la memoria física no puede moverse realmente, la información que contiene debe copiarse. Puede ser muy costoso computacionalmente copiar grandes fragmentos de memoria para ser utilizados por diferentes funciones o en diferentes lugares. Esto también es costoso desde el punto de vista de la memoria, ya que el espacio para la nueva copia de destino se debe guardar o asignar antes de poder copiar la fuente. Los indicadores son una solución a este problema. En lugar de copiar un bloque grande de memoria, es mucho más fácil pasar la dirección del comienzo de ese bloque de memoria ".

" Los punteros en C se pueden definir y usar como cualquier otro tipo de variable. Como la memoria en la arquitectura x86 usa el direccionamiento de 32 bits, los punteros también tienen 32 bits de tamaño (4 bytes). Los punteros se definen anteponiendo un asterisco (*) al nombre de la variable. En lugar de definir una variable de ese tipo, un puntero se define como algo que apunta a datos de ese tipo. El programa pointer.c es un ejemplo de un puntero que se usa con el tipo de datos char, que tiene solo 1 byte de tamaño ".

#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char str_a[20]; // A 20-element character array 
    char *pointer; // A pointer, meant for a character array 
    char *pointer2; // And yet another one 
    strcpy(str_a, "Hello, world!\n"); 
    pointer = str_a; // Set the first pointer to the start of the array. 
    printf(pointer); 
    pointer2 = pointer + 2; // Set the second one 2 bytes further in. 
    printf(pointer2); // Print it. 
    strcpy(pointer2, "y you guys!\n"); // Copy into that spot. 
    printf(pointer); // Print again. 
} 

" Como los comentarios en el código indica, el primer puntero se ajusta al principio de la matriz de caracteres. Cuando se hace referencia a la matriz personaje como este, es en realidad un puntero en sí. Así es como este el búfer se pasó como un puntero a las funciones printf() y strcpy() antes. El segundo puntero se establece en la dirección de los primeros punteros más dos, y luego se imprimen algunas cosas (se muestran en el resultado a continuación) ".

[email protected]:~/booksrc $ gcc -o pointer pointer.c 
[email protected]:~/booksrc $ ./pointer 
Hello, world! 
llo, world! 
Hey you guys! 
[email protected]:~/booksrc $ 

" El operador de dirección se utiliza a menudo en conjunción con los punteros, ya punteros contienen direcciones de memoria. El programa addressof.c demuestra la dirección-del operador que se utiliza para poner la dirección de una variable de número entero en un puntero. Esta línea se muestra en negrita debajo de ".

#include <stdio.h> 

int main() 
{ 
    int int_var = 5; 
    int *int_ptr; 
    int_ptr = &int_var; // put the address of int_var into int_ptr 
} 

" Un operador unitario adicional llamado existe el operador eliminar la referencia para su uso con los punteros. Este operador devolverá los datos que se encuentran en la dirección del puntero que hace referencia, en lugar de la dirección de sí mismo. Se presenta en forma de un asterisco delante del nombre de la variable, similar a la declaración de un puntero. Una vez más, el operador de desreferencia existe tanto en GDB como en C ".

funciones " algunas adiciones al código addressof.c (mostrado en addressof2.c) se demostrar todos estos conceptos. El printf añadido() utilizan el formato parámetros, que explicaré en la siguiente sección Por ahora, solo concéntrese en la salida de los programas ".

#include <stdio.h> 

int main() 
{ 
    int int_var = 5; 
    int *int_ptr; 
    int_ptr = &int_var; // Put the address of int_var into int_ptr. 
    printf("int_ptr = 0x%08x\n", int_ptr); 
    printf("&int_ptr = 0x%08x\n", &int_ptr); 
    printf("*int_ptr = 0x%08x\n\n", *int_ptr); 
    printf("int_var is located at 0x%08x and contains %d\n", &int_var, int_var); 
    printf("int_ptr is located at 0x%08x, contains 0x%08x, and points to %d\n\n", &int_ptr, int_ptr, *int_ptr); 
} 

" Cuando los operadores unarios se usan con los punteros, el operador de dirección se puede considerar como un movimiento hacia atrás, mientras que el operador eliminar la referencia se mueve hacia delante en la dirección del puntero está apuntando".

Para saber más acerca de punteros y asignación de memoria

profesor Dan Hirschberg, Departamento de Ciencias de la Computación de la Universidad de California en la memoria del ordenador https://www.ics.uci.edu/~dan/class/165/notes /memory.html

http://cslibrary.stanford.edu/106/

http://www.programiz.com/c-programming/c-dynamic-memory-allocation

Arrays

Hay una explicación sencilla sobre matrices multidimensionales por un tipo llamado Alex Allain disponible aquí http://www.cprogramming.com/tutorial/c/lesson8.html

información Theres en matrices por un tipo llamado Todd A Gibson disponible aquí http://www.augustcouncil.com/~tgibson/tutorial/arr.html

Iterar una matriz

#include <stdio.h> 

int main() 
{ 

    int i; 
    char char_array[5] = {'a', 'b', 'c', 'd', 'e'}; 
    int int_array[5] = {1, 2, 3, 4, 5}; 
    char *char_pointer; 
    int *int_pointer; 
    char_pointer = char_array; 
    int_pointer = int_array; 

    for(i=0; i < 5; i++) { // Iterate through the int array with the int_pointer. 
     printf("[integer pointer] points to %p, which contains the integer %d\n", int_pointer, *int_pointer); 
     int_pointer = int_pointer + 1; 
    } 

    for(i=0; i < 5; i++) { // Iterate through the char array with the char_pointer. 
     printf("[char pointer] points to %p, which contains the char '%c'\n", char_pointer, *char_pointer); 
     char_pointer = char_pointer + 1; 
    } 

} 

listas enlazadas vs matrices

Las matrices no son la única opción disponible, información sobre la lista enlazada.

http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_linklist.aspx

Conclusión

Esta información fue escrito simplemente para pasar en algunos de lo que he leído en toda mi investigación sobre el tema que podría ayudar a otros.

Cuestiones relacionadas