2010-10-22 6 views
6

Wszyscy znać podstawowe zasady tablicy statycznej:Czy możemy utworzyć statyczną tablicę o rozmiarze, który jest stałą czasu wykonywania?

int size = 20; 
char myArray[size]; 

nie jest legalne. .

const int size = 20; 
char myArray[size]; 

jest OK.

Ale co z tego?

int f(const int size) 
{ 
    char myArr[size]; 
} 

void main() 
{ 
    f(2); 
    f(1024); 
} 

MSVC mówi, że to jest błąd, gcc wydaje się skompilować i wykonać go dobrze.

Oczywiście nie jest przenośny, ale czy powinien zostać zaakceptowany?

Który kompilator robi to, co trzeba w tej sytuacji?

Ponadto, jeśli jest to dozwolone przez kompilator, czy powinno być dozwolone przez dobre standardy programowania/praktyki?

EDYTOR: Pomysł polega na tym, że chciałbym przydzielić alokację dla prędkości, ale nie wiedziałbym, w czasie kompilacji, rozmiaru tablicy. Wiem, że istnieje kilka innych rozwiązań, a ten podział na stosy prawdopodobnie nie byłby optymalizacją znaczącą, ale myślę, że jest to interesujące zastosowanie.

+0

Twoje pierwsze dwa przykłady są identyczne dla postaci. – AlcubierreDrive

Odpowiedz

9

Nie. C++ nie ma tablic o zmiennej długości. C99 ma, a gcc pozwala na to poprzez rozszerzenie.

Użyj std::vector.


Zakładając, że profilowane aplikacji i stwierdzono, że jest to wąskie gardło, napisać niestandardowy przydzielania że przydziela ze stosu i używać. Jeśli nie, nie ma problemu.

przydział Stack jest bardzo szybki, ale to prawdopodobnie nie będzie głównym problemem w rzeczywistej aplikacji. (Powinieneś mieć plan zarządzania zwyczaj pamięci w miejscu, które będzie blisko wydajność prędkości stos alokacji).

+0

Nie jest to tablica o zmiennej długości, wyrażenie, które naprawia rozmiar tablicy, jest const. – jslap

+0

@jslap: Co masz na myśli? Czy możesz podać bardziej konkretny przykład tego, co próbujesz zrobić? W twoim kodzie 'myArr' jest tablicą o zmiennej długości. Typ * może być const, co nie oznacza, że ​​wartość jest wyrażeniem stałym. Rozważ: 'f (rand());'. – GManNickG

+0

Jak już powiedziałem w mojej edycji, celem pytania nie jest optymalizacja konkretnego fragmentu kodu. Chcę tylko wiedzieć, czy jest to akrobowalny sposób optymalizacji, jeśli obserwuję, że to optymalizuje. Jestem pewien, że wczesna optymalizacja jest przekleństwem. Rzeczywiście, jestem znany w moim zespole za powtarzanie tego przez cały czas. – jslap

0

Co naprawdę chcesz nie jest całkiem co pan zapytał. Wygląda na to, że naprawdę potrzebujesz mapy liczb do liczb, na przykład indeks 2042 ma wartość 23 itd.

Ponieważ w rzeczywistości nie znasz górnej granicy najwyższej liczby, której możesz użyć, prawdopodobnie musisz zrestrukturyzować swój kod w taki sposób, że używa mapy (matematycznej), gdzie zamiast brać pod uwagę 2042 indeks tablicy trzymającej, bierzesz pod uwagę 2042 klucz dostępu do wartości 23.

---- Odpowiedz do puszki, do której możemy przydzielić tablicę statyczną ze stałą czasu wykonywania poniżej ---

Jeśli chcesz wykonać tablicę statyczną czasu wykonania, najlepszą opcją jest zadeklarowanie tablicy przy użyciu alternatywnej składni wskaźnika, a następnie inicjalizacja ze to w niestatyczną funkcję podobną na początku wykonywania programu.

Jeśli spróbujesz przydzielić tablicę przed wykonaniem main(...), możesz nie wiedzieć, które statyczne bloki kodu będą wywoływane w jakiej kolejności.Czasami robi to niewielką różnicę; ale w twoim przypadku wydaje się, że potrzebujesz liczby do obliczenia w czasie wykonywania, więc kolejność modułów staje się ważna.

Korzystając z metody niestatycznej, użytkownik zasadniczo gwarantuje, że cały kod statyczny zostanie wykonany przed przydzieleniem tablicy.

+0

To naprawdę nie jest to, za co chcę go użyć. Chcę tymczasowej tablicy w mojej funkcji, która jest na stosie. Na przykład dla algorytmu programowania dynamicznego. Zobacz także moje wyjaśnienia dotyczące przydziału stosu. – jslap

+0

W takim przypadku wydaje się, że ostatecznie zostaniemy popchnięci do użycia alternatywnej składni wskaźnika dla tablicy i przydzielenia jej na stercie. Kompilator C był dość rygorystyczny, jeśli chodzi o to, aby wiedzieć, ile pamięci musi ułożyć w stosie przed czasem (aby sprawdzić, czy trzeba go ułożyć w stosie, ponieważ może nie pasować do dodatkowej pamięci stosu podczas wykonywania rama). C++ ma te same ścisłe standardy dla tablic, a ponieważ jest to kontrola czasu kompilacji, nie da się tego zrobić w 100% w czasie wykonywania bez jakiejś głębokiej, strasznej magii. –

+0

@jslap: Tak jak powiedziałem, jest to rozszerzenie adoptowane z C99. – GManNickG

3

Możesz użyć do tego tablicy std ::. std :: array została dodana w rozszerzeniach TR1 i jest dostępna w przestrzeni nazw std lub std :: tr1 w zależności od używanej wersji kompilatora/standardowej biblioteki.

#incldue <array> 
int main() 
{ 
    std::tr1::array<int,25> myArray; 
    //etc... 
    myArray[2] = 42; 
} 

ponownie odczytać pytanie dotyczące alokacji stosu ...

Jeśli chcesz stos alokacji, można użyć alloca przeznaczyć na stosie zamiast malloc (zastosować wszystkie zwykłe ostrzeżenia).

Jeśli chcesz bardziej przyjazny interfejs, możesz zaimplementować niestandardowy przydział statystyczny oparty na alloca i użyć std :: vector z tym (you should read up before implementing).

np .:

#include <vector> 
template <class T> 
class MyStackAllocator 
{ // implemented per std::allocator spec 
... 
} 
int main() 
{ 
//allocate a vector on the stack and reserve n items 
vector<int, MyStackAllocator<T>> vecOnStack(25); 
... 
} 
+0

Dzięki, ale nie wiem, jakiego rozmiaru potrzebuję przed wykonaniem. – jslap

+0

zobacz moją ostatnią zmianę. – Rick

+0

+1 dla 'alloca'. Niestety nie jest to część standardu C++, ani nie jest bezpieczne, ale jest bardzo przenośne, ponieważ zostało zestandaryzowane przez BSD, a następnie przyjęte przez praktycznie wszystkie kompilatory. Jak jednak zaproponować owijanie 'alloca' przyjaznym interfejsem, aby pamięć była nadal ważna po zwrocie opakowania? –

1

Zmienna tablice długości (VLA) są obsługiwane przez C99, ale nie przez C++. gcc zezwala na to przez rozszerzenie.

std :: vector robi w C++, co robi w VLA C99

+0

Jak to się różni od mojej odpowiedzi? Nawet liczba zdań i układ są takie same. (Chociaż niepoprawne jest powiedzenie "wektor" jest wstawką dla VLA, "wektor" dynamicznie alokuje, VLA nie.) – GManNickG

+1

@GMan: Bardziej słuszne, aby powiedzieć "wektor" dynamicznie przydziela z sterty, VLA dynamicznie alokować z pamięci automatycznej (która na większości platform oznacza stos wywołań, ale tak naprawdę nie jest wymagana). –

+0

@Ben: Ah, racja. Tracę ten czas. – GManNickG

1

Poprawna odpowiedź została faktycznie dostarczone w tym wątku, więc po prostu chcą, aby zapewnić dodatkowy kontekst do niego.

Musisz utworzyć niestandardowy przydział, który używa funkcji alloca() (lub _alloca() w systemie Windows) do dynamicznego przydzielania stosów. Tworzenie go jest bardzo łatwe, można użyć typowego przydziału przydziału alokatora, zmienić funkcję alokatora allocate() na return (pointer)(alloca(size * sizeof(T))); i wyłączyć funkcję deallocate(), ponieważ nie ma ręcznej dealokacji stosu. Następnie możesz dostarczyć swój alokator do standardowych kontenerów, np. vector<T, stack_allocator<T>>.

Są jednak dwa zastrzeżenia. Możliwy rozmiar stosu przy alokacji może się znacznie różnić, często masz możliwość ustawienia go w czasie kompilacji. Visual Studio w aplikacjach 32-bitowych domyślnie limituje go do 1 MB. Inne kompilatory mogą mieć różne ograniczenia. W aplikacjach 64-bitowych nie ma żadnych problemów, ponieważ stos może być tak duży, jak sterty. Prawdopodobnie będziesz musiał złapać wyjątek strukturalny na przepełnieniu stosu i przekonwertować go na wyjątek C++.

Drugie zastrzeżenie, nie powinieneś kopiować wskaźników stosu poza twoją funkcją, więc przeniesienie semantyki na przykład nie zadziała, jeśli przekażesz przydzielone do stosu obiekty do/z funkcji.

Istnieje również jedna niedogodność, nie można kopiować kontenerów z niezgodnymi alokatorami, ale można kopiować je element po elemencie. Na przykład.

vector<int> vint; 
vector<int, static_allocator<int>> vsint; 

vint = vsint; // won't compile, different allocators 
std::copy(vsint.begin(), vsint.end(), vint.begin()); // fine 
Powiązane problemy