2009-03-03 12 views

Odpowiedz

19

Prawie, ale nie do końca. Prawidłowa odpowiedź brzmi:

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

bo trzeba najpierw de-odniesienie do jednego z rzeczywistych wskaźników strunowych, a potem do de-reference że wybrany ciąg wskaźnik w dół do żądanej postaci. (Zauważ, że dodałem dodatkowy nawias dla jasności w kolejności operacji tam).

Alternatywnie, to wyrażenie:

a[1][2] 

będzie również działać .... a może byłoby korzystne, ponieważ intencją, co chce zrobić, to bardziej oczywiste, a sam zapis jest bardziej zwięzły! . Ta forma może nie być od razu oczywista dla osób, które są nowe w tym języku, ale rozumieją, że powodem, dla którego działa notacja tablicowa, jest fakt, że w C, operacja indeksowania tablicy jest tak naprawdę skrótem dla równoważnej operacji wskaźnika. tj .: * (a + x) jest takie samo jak [x]. Tak więc, rozszerzając tę ​​logikę na pierwotne pytanie, istnieją dwie oddzielne operacje usuwania odniesień, kaskadowane razem, przy czym wyrażenie a [x] [y] jest równoważne ogólnej formie * ((* (a + x)) + y).

+0

Więc wskaźniki do wskaźników mogą być używane jako tablica bez jakichkolwiek innych deklaracji? – sdellysse

+0

Chciałbym dać +1, jeśli wyjaśnisz dlaczego. –

+0

za kulisami, a [b] przekłada się na * (a + b) –

1

Wypróbuj a[1][2]. Lub *(*(a+1)+2).

Zasadniczo odniesienia do tablicy to cukier syntaktyczny do dereferencji wskaźnika. a [2] to to samo co + 2, a także to samo co 2 [a] (jeśli naprawdę chcesz nieczytelnego kodu). Tablica łańcuchów jest taka sama jak podwójny wskaźnik. Możesz więc wyodrębnić drugi ciąg za pomocą [1] lub *(a+1). Następnie możesz znaleźć trzeci znak w tym ciągu (nazywaj go teraz "b") albo b [2], albo *(b + 2). Zastępując oryginalny drugi ciąg dla "b", otrzymujemy albo [1] [2] albo *(*(a+1)+2).

2

IIRC, ciąg jest faktycznie tablicę znaków, więc to powinno działać:

a[1][2] 
3

Nie trzeba użyć wskaźników.

Int główne (Int ARGC char ** argv) {

printf ("trzecią postać argv [1] [% C]. \ N" argv [1] [2 ]);

}

Następnie:

$ ./main cześć Trzecia postać argv [1] [l].

To jeden i jeden.

Możesz użyć wskaźników, jeśli chcesz ...

* (argv [1] + 2)

nawet

* ((* (A + 1)) + 2)

jak ktoś wskazano powyżej.

Jest tak, ponieważ nazwy tablic są wskaźnikami.

+0

Wiszący: argv [1] [3] jeśli czwarty znak z argv [1] ... – dmckee

+0

Tak. Właśnie to zmieniłem. To właśnie dostaję za próbę szybkiego wysłania, a nie sprawdzania moich indeksów. –

2

Cytat z wikipedii article na C wskaźniki -

w C, indeksowanie tablicy jest formalnie zdefiniowany w kategoriach wskaźnika arytmetyki; tj. specyfikacja języka wymaga, aby tablica [i] była równoważna * (tablica + i). Zatem w C tablice mogą być uważane za wskaźniki do kolejnych obszarów pamięci (bez przerw), , a składnia dostępu do tablic jest identyczna dla tej, która może być użyta do wskaźników dereferencyjnych . Na przykład tablica może zostać uznane i stosowane w sposób następujący:

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 */ 

tak, to w odpowiedzi na swoje pytanie - tak, wskaźniki do wskaźników może być używany jako tablica bez jakiejkolwiek innej deklaracji w wszystko!

1

Oto genialne wyjaśnienie programowania C w książce Hacking the art of exploitation 2nd Edition autorstwa Jona Ericksona, która omawia wskaźniki, napisy, warte uwagi tylko w sekcji wyjaśniania programowania https://leaksource.files.wordpress.com/ 2014/08/hacking-the-art-of-exploitation.pdf.

Mimo że na to pytanie już udzielono odpowiedzi, ktoś inny, który chce wiedzieć więcej, może znaleźć poniższe informacje z książki Ericksons, pomocne w zrozumieniu niektórych elementów kryjących się za pytaniem.

Główki

Przykłady plików nagłówkowych dostępnych dla zmiennej manipulacji prawdopodobnie będziesz używać.

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

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

ciąg. H - http://www.cplusplus.com/reference/cstring/

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

Funkcje

Przykłady funkcji ogólnego przeznaczenia, z których prawdopodobnie będziesz korzystać.

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

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

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

pamięci

" pamięć skompilowanego programu jest podzielony na pięć segmentów. tekstu, danych, BSS, kupa, i stos Każdy segment reprezentuje specjalną część pamięci Oddzielny fragment tekstu jest czasem nazywany segmentem kodu, w którym znajdują się instrukcje dotyczące języka maszynowego programu: ".

" Wykonanie instrukcji w tym segmencie jest nieliniowa, dzięki wspomnianych struktur kontroli wysokiego szczebla i funkcji, które kompilacji do oddziału, skakać i instrukcje w asemblerze telefonować. W programie zostanie uruchomiony, EPI jest ustawiony na pierwszej instrukcji segmentu tekstu procesor potem następuje pętli wykonania, że ​​działanie jest następujące.

1. odczytuje polecenie, które EPI jest skierowany do

2. Dodaje długość bajt instrukcji do EIP "

" 3. Wykonuje instrukcję że został odczytany w kroku 1 "

" 4. wraca do kroku 1 "

" Czasami instrukcją będzie skok lub instrukcja wywołania, która zmienia adres EIP na inny adres pamięci. Procesor nie zwraca uwagi na zmianę, ponieważ spodziewa się, że wykonanie będzie nieliniowe . Jeśli EIP zostanie zmieniony w kroku 3, procesor powróci do kroku 1 i przeczyta instrukcję znalezioną pod adresem dowolnego, co zmieniło EIP na ".

" Uprawnienia do zapisu są wyłączone w segmencie tekstowym, ponieważ nie służy do przechowywania zmiennych, tylko kod. Zapobiega to rzeczywistym modyfikacjom kodu programu; każda próba zapisu do tego segmentu pamięci spowoduje, że program ostrzeże użytkownika, że ​​stało się coś złego, a program zostanie zabity. Inną zaletą tego segmentu, który jest tylko do odczytu, jest to, że może on być współużytkowany przez różne kopie programu, umożliwiając jednocześnie wykonywanie wielu uruchomionych programów w tym samym czasie bez żadnych problemów. Należy również zauważyć, że ten segment pamięci ma stały rozmiar, ponieważ nigdy nic zmiany to

Dane i segmenty BSS są używane do przechowywania globalny program i statycznych zmienne. Segment danych jest wypełniany zainicjowanymi zmiennymi globalnymi i statycznymi, natomiast segment bss jest wypełniany niezainicjowanymi odpowiednikami. Mimo że te segmenty są zapisywalne, mają również stały rozmiar. Pamiętaj, że zmienne globalne nadal istnieją pomimo kontekstu funkcjonalnego (jak zmienna j w poprzednich przykładach). Obie zmienne globalne i statyczne są w stanie utrzymywać, ponieważ są one przechowywane w swoich segmentach pamięci

Segment sterty jest segmentem pamięci programista może bezpośrednio kontrola. Bloki pamięci w tym segmencie mogą być przydzielane i używane dla niezależnie od potrzeb programisty.Jedną z istotnych punkt o segmencie sterty jest to, że nie stanowi stałej wielkości, więc może rosnąć większe lub mniejsze w zależności od potrzeb

Wszystkie pamięci w stercie jest zarządzany przez algorytmy podzielnik i dealokator , które odpowiednio rezerwują region pamięci w stercie do użytku i usuwają rezerwacje, aby ta część pamięci mogła zostać ponownie użyta dla późniejszych rezerwacji. Sterty będą rosnąć i zmniejszać się w zależności od tego, jak dużo pamięci jest zarezerwowane do użycia. Oznacza to, że programator korzystający z funkcji przydzielania sterty może rezerwować i zwalniać pamięć w locie. Wzrost sterty przesuwa się w dół ku wyższym pamięci rozwiązuje

Segment stosu posiada również zmienną wielkość i służy jako tymczasowy notatniku do przechowywania zmiennych lokalnych funkcji i kontekstu podczas wywołań funkcji. Oto, na co wygląda polecenie śledzenia wstecznego GDB. Gdy program wywoła funkcję, funkcja ta będzie miała własny zestaw przekazywanych zmiennych, a kod funkcji będzie znajdować się w innym miejscu pamięci w segmencie tekstowym (lub kodowym). Ponieważ kontekst i EIP muszą się zmieniać po wywołaniu funkcji, stos jest używany do zapamiętywania wszystkich przekazanych zmiennych, lokalizacji, do której EIP powraca po zakończeniu funkcji, oraz wszystkich lokalnych zmiennych używanych przez tę funkcję. Wszystkie te informacje są przechowywane razem na stosie w tak zwanej ramie stosu. Stos zawiera wiele ramek stosów. "

" Zasadniczo w dziedzinie informatyki stos jest abstrakcyjną strukturą danych, która jest często używana. Ma on pierwsze i ostatnie (FILO) porządkowanie , co oznacza, że ​​pierwszy element, który jest wkładany do stosu, jest ostatnim elementem, który się z niego wydostanie. Pomyśl o tym, jak umieszczasz koraliki na kawałku sznurka, który ma węzeł na jednym końcu - nie możesz zdjąć pierwszej kulki, dopóki nie usuniesz wszystkich pozostałych koralików. Kiedy przedmiot jest umieszczany w stosie, jest znany jako pchający, a gdy przedmiot jest usuwany ze stosu, nazywa się popping ".

" Jak sama nazwa wskazuje, segment stosu pamięci jest w rzeczywistości , struktura danych stosu, która zawiera ramki stosu. Rejestr ESP służy do śledzenia adresu końca stosu, który ciągle się zmienia, gdy elementy są wsuwane i wysuwane z niego. Ponieważ jest to zachowanie bardzo dynamiczne, ma sens, że stos nie ma stałego rozmiaru. Przeciwnie do dynamicznego wzrostu stercie, jak zmiany stosu S w rozmiarze, to rośnie w górę w wizualnym listę pamięci, ku dolnej pamięci rozwiązuje

FILO charakter stosu może wydawać się dziwne, , ale ponieważ stos jest używany do przechowywania kontekstu, jest bardzo przydatny. Kiedy funkcja jest wywoływana, kilka rzeczy jest popychanych do stosu razem w ramce stosu. Rejestr EBP - czasami nazywany wskaźnikiem ramki (FP) lub lokalnym wskaźnikiem bazowym (LB) - jest używany do odniesienia lokalnych zmiennych funkcji w bieżącej ramce stosu. Każda ramka stosu zawiera parametry do funkcji, jej zmienne lokalne i dwa wskaźniki, które są niezbędne, aby przywrócić rzeczy takie, jakimi były: zapisany wskaźnik ramki (SFP) i adres zwrotny. Kostka SFP służy do przywracania EBP do poprzedniej wartości, a adres powrotu jest używany do przywrócenia EIP do następnej instrukcji znalezionej po wywołaniu funkcji. Przywraca ten kontekst funkcjonalny poprzedniego stosu ramy

Struny

WC, tablica jest po prostu listę N elementów określonego typu danych. 20-znakowa tablica to po prostu 20 sąsiednich znaków znajdujących się w pamięci.Tablice są również określane jako bufory

#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); 
} 

w programie poprzednim tablica postaci 20 elementu jest zdefiniowane jako str_a, a każdy element tablicy są zapisywane do jednego po drugim. Zauważ, że liczba zaczyna się od 0, w przeciwieństwie do 1. Zwróć też uwagę, że ostatni znak to 0 ".

" (Jest również nazywany bajtem zerowym.) Tablica znaków została zdefiniowana, więc 20 bajtów są do niego przydzielone, ale tylko 12 z tych bajtów jest rzeczywiście używanych. Programowanie z pustym bajtem na końcu jest używane jako znak ogranicznika do informowania o dowolnej funkcji, która ma do czynienia z ciągiem, aby zatrzymać operacje tam. Pozostałe dodatkowe bajty są po prostu śmieciami i zostaną zignorowane. Jeśli zerowy bajt jest wstawiany w piątym elemencie tablicy znaków, tylko znaki Witam byłyby drukowane przez funkcję printf() „

Ponieważ ustawienie każdego znaku w tablicy znaków jest żmudna i łańcuchy są dość często używany, utworzono zestaw standardowych funkcji do manipulacji ciągami. Na przykład funkcja strcpy() skopiuje ciąg ze źródła do miejsca docelowego, iterując przez ciąg źródłowy i kopiując każdy bajt do miejsca docelowego (i zatrzymując się po skopiowaniu bajtu kończącego zero).. "

" Kolejność argumentów funkcji jest podobna do miejsca docelowego składni złożenia Intela, a następnie źródła. Program char_array.c można przepisać przy użyciu metody strcpy(), aby osiągnąć to samo, używając biblioteki ciągów. Kolejna wersja programu char_array przedstawionym poniżej zawiera string.h ponieważ wykorzystuje funkcję ciąg”.

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

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

Znajdź więcej informacji na smyczki C

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

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

Wskaźniki

" Rejestr EIP jest wskaźnikiem, który" wskazuje "bieżącą instrukcję podczas wykonywania programów, zawierając jej adres pamięci. Pomysł wskaźników jest również używany w języku C. Ponieważ pamięć fizyczna nie może zostać przeniesiona, informacje w niej zawarte muszą zostać skopiowane. Kopiowanie dużych porcji pamięci do różnych funkcji lub w różnych miejscach może być bardzo kosztowne. Jest to również drogie z punktu widzenia pamięci, ponieważ miejsce na kopię docelową musi zostać zapisane lub przydzielone przed skopiowaniem źródła. Wskaźniki są rozwiązaniem tego problemu. Zamiast kopiować duży blok pamięci, znacznie łatwiej jest omijać adres początku tego bloku pamięci ".

" Wskaźniki w C można definiować i stosować tak jak każdy inny typ zmiennej. Ponieważ pamięć w architekturze x86 używa 32-bitowego adresowania, wskaźniki mają również 32 bity (4 bajty). Wskaźniki definiuje się przez dodanie gwiazdki do nazwy zmiennej. Zamiast definiowania zmiennej tego typu, wskaźnik jest definiowany jako coś, co wskazuje na dane tego typu. Program pointer.c jest przykładem wskaźnika używanego z typem danych char, który ma tylko 1 bajt rozmiaru ".

#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. 
} 

" W komentarzach w kodzie wskazują, pierwszy wskaźnik jest ustawiony na początku tablicy znaków. Kiedy tablica znaków jest określany tak, to jest rzeczywiście sam wskaźnik. To jak to bufor został przekazany jako wskaźnik do funkcji printf() i strcpy() wcześniej Drugi wskaźnik jest ustawiony na pierwszy adres wskaźników plus dwa, a następnie niektóre rzeczy są drukowane (pokazane na wyjściu poniżej) ".

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

" Adres-operatora jest często stosowany w połączeniu ze wskaźnikami, ponieważ wskaźniki zawierają adresy pamięci. Program addressof.c pokazuje adres-operatora jest stosowane do wprowadzenia adresu zmiennej całkowitej do wskaźnika. Ta linia jest pogrubiona poniżej ".

#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 
} 

" Dodatkowym operator jednoargumentowy nazywany istnieje operator dereference do stosowania wskaźników. Ten operator zwróci dane znalezione w adresie wskaźnik jest wskazując, zamiast sam adres. To przybiera formę Gwiazdka przed nazwą zmiennej, podobna do deklaracji wskaźnika Ponownie operator dereferencji istnieje zarówno w GDB, jak iw C ". Funkcje

" Kilka uzupełnień do kodu addressof.c (pokazanej na addressof2.c) będzie wykazać wszystkie z tych pojęć. Dodawana printf() używają formatu parametry, które wytłumaczę w następnym rozdziale Na razie skup się tylko na wynikach programów ".

#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); 
} 

Kiedy unarne operatorzy są używane ze wskaźnikami, adres, wykonawcy mogą być traktowane jako ruchu do tyłu, podczas gdy operator dereference porusza się do przodu w kierunku wskaźnik jest wskazując”.

Dowiedz się więcej o Pointers i pamięci alokacji

Profesor Dan Hirschberg, Wydział Informatyki, Uniwersytet Kalifornijski w pamięci komputera 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

Tablice

Tam prosty tutorial na tablicach wielowymiarowych przez jegomościa o imieniu Alex Allain dostępne tutaj http://www.cprogramming.com/tutorial/c/lesson8.html

Theres informacje na tablicach przez jegomościa o imieniu Todd A Gibson dostępny tutaj http://www.augustcouncil.com/~tgibson/tutorial/arr.html

iteracyjne Array

#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; 
    } 

} 

Linked Listy vs tablice

Tablice nie są jedynym dostępnym rozwiązaniem, informacje o połączonej listy.

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

Wnioski

Informacja ta została napisana po prostu przekazać niektóre z tego co czytałem w całym moich badań nad temat, który może pomóc innym.