2013-05-14 11 views
19

Czy w ten sposób można używać pamięci "extra" przydzielonej podczas używania haka C struct?C struct hack at work

Pytania:

Mam implementację C Hack Struktur poniżej. Moje pytanie brzmi: jak mogę użyć "dodatkowej" pamięci, którą przydzieliłem z hackerem. Czy ktoś może podać mi przykład użycia dodatkowej pamięci?

#include<stdio.h> 
#include<stdlib.h> 

int main() 
{ 

    struct mystruct { 

     int len; 
     char chararray[1]; 

    }; 

    struct mystruct *ptr = malloc(sizeof(struct mystruct) + 10 - 1); 
    ptr->len=10; 


    ptr->chararray[0] = 'a'; 
    ptr->chararray[1] = 'b'; 
    ptr->chararray[2] = 'c'; 
    ptr->chararray[3] = 'd'; 
    ptr->chararray[4] = 'e'; 
    ptr->chararray[5] = 'f'; 
    ptr->chararray[6] = 'g'; 
    ptr->chararray[7] = 'h'; 
    ptr->chararray[8] = 'i'; 
    ptr->chararray[9] = 'j'; 


} 
+2

Czy możesz podać nam trochę więcej kontekstu? Co to jest c struct hack? Jakie jest twoje prawdziwe pytanie? –

+1

@RobertHarvey Uważam, że rozszerzenie tablicy na 9 elementów z 1 elementu. –

+0

@RobertHarvey Niezależnie od tego, co powiedział Lews. Rozszerzanie tablicy do 10 z 1. – abc

Odpowiedz

14

Tak, to jest (i było) standardowy sposób w C do tworzenia i przetwarzania zmiennego rozmiaru struct.

Ten przykład jest nieco gęsty. Większość programistów będzie go obsługiwać więcej zręcznie:

struct mystruct { 
     int len; 
     char chararray[1]; // some compilers would allow [0] here 
    }; 
    char *msg = "abcdefghi"; 
    int n = strlen (msg); 

    struct mystruct *ptr = malloc(sizeof(struct mystruct) + n + 1); 

    ptr->len = n; 
    strcpy (ptr->chararray, msg); 
} 
+0

Ale dlaczego ktoś miałby to kiedykolwiek robić? Czy nie powinien mieć wskaźników struct do zmiennej pamięci? – djechlin

+3

To ** było **. Standardowym sposobem jest teraz użycie elastycznego elementu matrycowego. –

+0

@ DanielFischer Nie jestem pewien, dlaczego mnie to obchodzi .. ale jaki jest elastyczny element tablicy? Czy mówisz o realloc? –

1

radziłbym przeciwko, że z powodu możliwych problemów wyrównania zamiast rozważyć to:

struct my_struct 
{ 
    char *arr_space; 
    unsigned int len; 
} 

struct my_struct *ptr = malloc(sizeof(struct my_struct) + 10); 
ptr->arr_space = ptr + 1; 
ptr->len = 10; 

to daje lokalizację, a także bezpieczeństwo i uniknąć :) dziwne problemy wyrównania.

Przez problemy z wyrównaniem rozumiałem możliwe opóźnienia w dostępie do dostępu do niewyrównanej pamięci.

W oryginalnym przykładzie, jeśli dodasz element wyrównany do bajtu lub bez słowa (bajt, znak, krótki), kompilator może rozszerzyć rozmiar struktury, ale jeśli chodzi o twój wskaźnik, czytasz pamięć bezpośrednio po koniec struktury (bez wyrównania). Oznacza to, że jeśli masz tablicę typu wyrównanego, takiego jak int, każdy dostęp da ci hit wydajności na procesorach, które podejmują działania związane z odczytywaniem niewyrównanej pamięci.

struct 
{ 
    byte_size data; 
    char *var_len; 
    some_align added by compiler; 
} 

w pierwotnym postępowaniu będzie czytanie z regionu some_align który jest tylko wypełniaczem, ale w moim przypadku można odczytać z wyrównanym dodatkowej pamięci potem (co traci trochę miejsca, ale to zwykle w porządku).

Inną zaletą tego rozwiązania jest to, że jest to możliwe, aby uzyskać więcej lokalizację z odpisów przeznaczając całą przestrzeń dla zmiennych członków długość struct w jednej alokacji zamiast przeznaczając je oddzielnie (pozwala uniknąć wielu kosztów stałych połączeń przydział i daje pewne cache lokalizacja, a nie odbijanie całej pamięci).

+4

Tablice 'char' nie mają żadnych wymagań wyrównania - mogą być wyrównane na dowolnej granicy. –

+1

@AdamRosenfield Miałem na myśli, aby inne pola w strukturze mogły być wyrównane do rozmiaru struktury, a otrzymasz trafienie podczas uzyskiwania dostępu do niewyrównanej pamięci w niektórych procesorach –

+1

+1 dla problemów z wyrównaniem ... Adam, myślę, że Jezus mówi, że * arr_space może być dowolnie wpisany. –

5

Odkąd przeczytać ten artykuł (http://blogs.msdn.com/b/oldnewthing/archive/2004/08/26/220873.aspx), mam ochotę użyć struct włamać tak:

#include<stdio.h> 
    #include<stdlib.h> 

    int main() 
    { 

     struct mystruct { 

      int len; 
      char chararray[1]; 
     }; 

     int number_of_elements = 10; 

     struct mystruct *ptr = malloc(offsetof(struct mystruct, chararray[number_of_elements])); 
     ptr->len = number_of_elements; 

     for (i = 0; i < number_of_elements; ++i) { 
     ptr->chararray[i] = 'a' + i; 
     } 

    } 

uważam, że nie trzeba pamiętaj, czy 1 należy odjąć (lub dodać lub cokolwiek) jest miłe. Ma to również zaletę pracy w sytuacjach, w których 0 jest używany w definicji tablicy, którą nie wszystkie kompilatory obsługują, ale niektóre robią. Jeśli alokacja jest oparta na offsetof(), nie musisz się martwić o te ewentualne szczegóły, które powodują, że twoja matematyka jest zła.

Działa również bez zmian, a struktura to elastyczny element tablicy C99.

+1

Trudno odejść od stwierdzenia, że ​​C99 pozwala na tablice o rozmiarze 0 w tym poście na blogu (C99 ma kluczowe słowo "nonempty" w 6.2.5: 20). Zestandaryzowano również, że rozmiar tablicy o wielkości n jest równy n razy wielkości elementu, a standard domyślnie zakłada, że ​​rozmiar obiektu nigdy nie jest zerowy w kilku miejscach (chociaż GCC podjął ryzyko, aby pozwolić na zero -rozszerzone tablice jako rozszerzenie.Nie rozumiem, w jaki sposób akceptują to ryzyko, ale być może, jeśli jest to tylko na końcu struktury, jest w porządku) –

+0

Myślę, że wzmianka w artykule o tablicach o zerowej długości nie jest legalna C99 jest dość nieprecyzyjną kombinacją wyjaśniającą, że nie jest to legalne w C90 i że C99 pozwala na niekompletne typy tablic na końcu struktury (która nie jest zbyt rozciągliwa od tablic o zerowej wielkości, nawet jeśli to nie jest dokładnie to). –

1

Jest "poprawny", ale trzeba mieć dobry powód, aby to zrobić w rozsądniejszym rozwiązaniu. Częściej może użyjesz tej techniki do "nałożenia" jakiejś istniejącej tablicy, aby nałożyć na nią jakąś strukturę nagłówka.

Należy zauważyć, że GCC przez rozszerzenie pozwala na zero length array member dokładnie w tym celu, podczas gdy ISO C99 "legitymizuje" praktykę, dopuszczając członka z pustymi nawiasami (tylko jako ostatni członek).

Zauważ, że istnieją pewne problemy semantyczne - rozmiar struktury nie będzie oczywiście uwzględniał "elastycznego" rozmiaru końcowego elementu, a przekazanie struktury "według wartości" przejdzie tylko przez nagłówek i pierwszy element (lub nie element używający rozszerzenia GCC lub elastycznego elementu tablicy C99). Podobnie bezpośrednie przypisanie do struktury nie spowoduje skopiowania wszystkich danych.