2016-08-05 29 views
1

Ich kann dieses Stück Code in C geschrieben scheinen zu verstehen, ++:erzeugen, um die Elementwerte eines konstanten Array von Integer-Logarithmen

static const char LogTable256[256] = 
{ 
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n 
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), 
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) 
}; 

wie kann ich das gleiche Array mit Schleifen erzeugen?

+0

Sie kopieren diesen viel Code in eine leere Datei & run (falls vorhanden) dieser Befehl "' g ++ -E file.cpp> file_preprocessed.cpp'", können Sie sehen, dass die' LT (n) ' Makro hätte es in jedem Fall mit 16 ns ersetzt. Es scheint, dass jemand 'char' mit solchen ASCII-basierten Werten speichert. Bei Schleifen gibt es keinen systematischen Weg, diese Sequenz zu erzeugen, daher ist es besser links wie oben. – iammilind

+0

LT (n) wird für jeden Wert durch 8 ersetzt. Sie können also einfach for-Schleifen für i> 4 verwenden; besser Sie versuchen es selbst und zeigen Sie uns Ihre Fortschritte –

+0

@RamandeepPunia 16 mal, eigentlich. Sie erhalten am Ende -1, dann eine Liste von 255 Werten, wo Sie immer 2^n Instanzen von n für n von 0 bis 7 finden. – starturtle

Antwort

1

#define Zeilen können überall in Ihrem Code vorkommen, solange ihre Verwendung ihnen folgt. Da sie Präprozessordirektiven sind, gibt es keinen Unterschied zwischen ist:

static const char LogTable256[256] = 
{ 
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n 
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), 
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) 
}; 

und

#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n 

static const char LogTable256[256] = 
{ 
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), 
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) 
}; 

Wenn die zweite Sie sinnvoll ist, dann sicher sein, dass die erste wie die so weit ist nur so gut Compiler und Verhalten des Programms betroffen ist. Der erste könnte menschliche Leser dazu verleiten zu denken, dass das Makro im Bereich der Deklaration der Variablen definiert ist, was nicht wahr ist.

+0

"das erste ist genauso gut", nein, ist es nicht. Es verbirgt die Makrodefinition in anderem Text und zeigt an, dass der Programmierer fälschlicherweise denkt, dass ein Makro auf den Bereich beschränkt ist, in dem es definiert ist. Im Quellcode geht es darum, mit Menschen zu kommunizieren. –

+0

@ Cheersandthth.-Alf, Punkt gut gemacht. –

+0

@RSahu Dies hat mir geholfen zu verstehen, wie das Array erstellt wurde, aber ich muss auch den gleichen Code in Python reproduzieren und ich weiß nicht, wie das gemacht würde. –

2

Wie kann ich das gleiche Array mit Schleifen erzeugen?

Sie können das nicht mit Schleifen tun, weil das char-Array const ist.

Was der Algorithmus tut, ist, füllt es ein Char-Array mit Zahlen von -1 bis 7, wobei, beginnend bei 1, jede Zahl doppelt so oft wie die vorher eingegeben wird. Dies kann mit zwei einfachen verschachtelten Schleifen oder einer Schleife, pow und memset erfolgen. Damit das funktioniert, darf das char-Array nicht const sein.

+1

** - 1 ** "Das kann man nicht mit Loops machen, weil das Char-Array" const "ist, ist falsch (im Sinne der gerechten Interpretation) oder richtig, aber bedeutungslos (im vollständig wörtlichen Sinne). Die letzte Aussage "Damit das funktioniert, muss das char-Array nicht const sein" ist Just Wrong ™. –

0

Die Position der #define Präprozessordirektive nicht technisch außer egal, dass es Makro Verwendung der LT kommt vor, dass es definiert. Herkömmliche Formatierung würde es vor der Array-Definition platzieren. Es ist nicht auf den Bereich beschränkt, in dem es definiert ist: Makros respektieren keine Bereiche.

Um das Array ohne Verwendung eines Makros zu definieren, können Sie ein Singleton verwenden, z. B. ein Meyers Singleton.

Einmalige Initialisierung der Job eines Konstruktor ist, das ist so, wie ich es tun würde, ein Singleton Meyers mit einem Objektklasse Typ, dessen Konstruktor berechnet die Array-Elementwerte:

auto logarithms() 
    -> std::array<signed char, 256> const& 
{ 
    struct Wrapper 
    { 
     std::array<signed char, 256> items_; 

     static void append(int const n, int const value, signed char*& p) 
     { 
      for(int i = 1; i <= n; ++i) { *p++ = value; } 
     } 

     Wrapper() 
     { 
      items_[0] = -1; 
      signed char* p = &items_[1]; 
      for(int i = 0; i < 8; ++i) { append(1 << i, i, p); } 
      assert(p == &*items_.end()); 
     } 
    }; 

    static Wrapper const the_array; 
    return the_array.items_; 
} 

Hinweis des Verwendung von expliziten signed char: der Typ char ist nicht garantiert signiert werden, dh in der Lage sein, dass -1 Wert darstellen.

Jedoch wird in Berechnungen jeder signed char Wert gefördert und das kann beeinträchtigen, anstatt die Effizienz zu verbessern. Die Verwendung von signed char ist auch nur eine Komplikation. Wenn dies nicht für ein eingebettetes System mit extrem wenig verfügbarem Speicher ist, würde ich nur int anstelle von signed char verwenden.

+0

Alf Dies ist eine gute Antwort auf mein Problem. Aber wäre es nicht besser, wenn ich das Array nur dynamisch zuweisen würde, anstatt das eingebaute std :: array zu verwenden? –

+0

@MuhammadAliQadri: Für ein Array konstanter Größengröße, fast unabhängig von der Größe, ist die dynamische Zuweisung (d. H. Unter Verwendung von "std :: vector") nur ein nutzloser Overhead. Das ist die Art von "std :: array", für die es da ist. Platz für dieses Array ist im geladenen Bild des Programms. –

+0

okay, danke! –

0

Sie können sogar constexpr verwenden, die aufgerufen werden kann, wenn ein constexpr Objekt initialisiert wird. Füllen Arrays ist möglich, aber std::array fehlt einige constexpr Deklarationen, die mit C++17 eingeführt werden.Im Moment müssen wir auf ein einfaches Array zurückgreifen und dieses dann in eine mit einer Hilfsfunktion to_array konvertieren, die auch zum Standard/tr2 hinzugefügt wird.

#include <array> 
#include <algorithm> 
#include <cassert> 

// this is from cppreference 

namespace detail { 
template <class T, std::size_t N, std::size_t... I> 
constexpr std::array<std::remove_cv_t<T>, N> 
    to_array_impl(T (&a)[N], std::index_sequence<I...>) 
{ 
    return { {a[I]...} }; 
} 
} 

template <class T, std::size_t N> 
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]) 
{ 
    return detail::to_array_impl(a, std::make_index_sequence<N>{}); 
} 

constexpr auto make() 
{ 
    int a[256] = {-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,}; 

    for(size_t i=4, n=16, start=16; i <= 7; ++i, n *= 2) 
    { 
     for(size_t j=0; j != n; ++j) 
     { 
      a[start + j] = i; 
     } 

     start += n; 
    } 

    return to_array(a); 
} 

int main() { 
    static const int LogTable256[256] = 
{ 
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n 
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), 
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) 
}; 

    auto constexpr a = make(); 
    assert(std::equal(std::begin(a), std::end(a), std::begin(LogTable256)));  
    return 0; 
} 

auto constexpr a = make() stellt sicher, dass die Tabelle kompiliert Zeitwert ist. A auto const a = make() funktioniert auch, aber möglicherweise nicht zur Kompilierzeit ausgewertet.

Eine einfachere Implementierung besteht darin, eine Klasse mit einem costexpr-Konstruktor zu verwenden, der die Tabelle enthält. Die Berechnung ist vollständig kompiliert.

class LogTable 
{ 
public: 
    constexpr LogTable() 
    { 
     for(size_t i=4, n=16, start=16; i <= 7; ++i, n *= 2) 
     { 
      for(size_t j=0; j != n; ++j) 
      { 
       a[start + j] = i; 
      } 

      start += n; 
     } 
    } 

    constexpr int operator[](size_t i) const 
    { 
     return a[i]; 
    } 
private: 
    int a[256] = {-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,}; 
}; 

int main() { 
    static const int LogTable256[256] = 
    { 
    #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n 
    -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 
    LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), 
    LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) 
    }; 

    LogTable constexpr l; 
    for(auto i=0; i != 256; ++i) 
    { 
     assert(LogTable256[i] == l[i]); 
    } 

    return 0; 
} 
+0

Es gibt zwei Hauptprobleme hier: (1) dass der Code nicht mit Visual C++ 2015 kompiliert, weil es sehr C++ 14-spezifischen Code ist, und (2) das Kopieren, das ich nicht sicher bin, wird notwendigerweise sein optimiert weg. Mit diesem Ansatz können die Logarithmen in Kompilierungszeitausdrücken verwendet werden. Ich glaube nicht, dass ich diesen Handel gemacht hätte. ;-) –

+0

@ Cheersandhth.-Alf Ich habe VC++ nicht geprüft, nur gcc 6.1. Ich denke, das Kopieren kann vermieden werden, indem man eine "constexpr" -Klasse erstellt, die die Tabelle im Konstruktor initialisiert. Dies kann auch mit VC++ kompilieren. – Jens

+0

Nun, ich freue mich über die Bemühungen, diese Art von Code zu präsentieren. Ich wünschte nur, Visual C++ würde es zu diesem Zeitpunkt akzeptieren. Vielleicht, ich bin nur bei Update 2 und ich denke, ich habe gelesen, dass es bereits ein Update 5 gibt. –