2015-07-03 17 views
5

Ho un array di (puntatori a) array di lunghezze differenti, che appresi potuto definire usando letterali composti:Clang lamenta: "puntatore viene inizializzato da un array temporaneo"

const uint8_t *const minutes[] = { 
    (const uint8_t[]) {END}, 
    (const uint8_t[]) {1, 2, 3, 4, 5 END}, 
    (const uint8_t[]) {8, 9, END}, 
    (const uint8_t[]) {10, 11, 12, END}, 
    ... 
}; 

gcc accetta questo solo bene, ma clang dice: pointer is initialized by a temporary array, which will be destroyed at the end of the full-expression. Cosa significa questo? Il codice sembra funzionare, ma poi ancora un sacco di cose sembrano funzionare quando puntano alla memoria che non è più assegnata. È qualcosa di cui ho bisogno di preoccuparmi? (In definitiva ho solo bisogno di lavorare con gcc.)

Aggiornamento: Sta succedendo qualcosa di strano. Si dice che here:

codice

Compound literals yield lvalues. This means that you can take the address of a compound literal, which is the address of the unnamed object declared by the compound literal. As long as the compound literal does not have a const-qualified type, you can use the pointer to modify it.

`struct POINT *p; 
    p = &(struct POINT) {1, 1}; 

Questo esempio sembra fare esattamente quello che sto cercando di fare: un puntatore a qualcosa di definito da un composto letterale. Quindi il messaggio di errore clang è legittimo? Questo finirà per indicare la memoria non allocata quando compilata con clang o gcc?

Aggiornamento 2: Trovato alcuni documentation: "In C, un letterale composto designa un oggetto senza nome con durata di conservazione statica o automatica In C++, un composto letterale designa un oggetto temporaneo, che vive solo fino alla fine. della sua piena espressione "Quindi sembra che clang abbia ragione ad avvisare di questo, e probabilmente anche gcc dovrebbe, ma non lo fa, anche con -Wall -Wextra.

Non riesco a indovinare perché una funzionalità C utile è stata rimossa dal C++ e non è stato fornito alcun modo alternativo elegante per realizzare la stessa cosa.

+0

Stai compila con '-W' flag quando si utilizza gcc?Potrebbe apparire se non sei – GeoffreyB

+0

Sì. -Wall -Wextra, che è apparentemente la stessa cosa. – Josh

+0

hai provato a rimuovere l'inutile '(const uint8_t [])' all'inizio di ogni riga? –

risposta

3

Beh, clang è giusto, e questo deve essere fatto in questo modo:

namespace elements 
{ 
    const uint8_t row1[] = {END}; 
    const uint8_t row2[] = {1, 2, 3, 4, 5, END}; 
    ... 
} 
const uint8_t *const minutes[] = { 
    elements::row1, 
    elements::row2, 
    ... 
}; 

si può pensare di più soluzione di C++, come l'utilizzo di std::tuple:

#include <tuple> 

constexpr auto minutes = std::make_tuple(
    std::make_tuple(), 
    std::make_tuple(1,2,3,4,5), 
    std::make_tuple(8,9,10)); 

#include <iostream> 
#include <type_traits> 
int main() { 
    std::cout << std::tuple_size<decltype(minutes)>::value << std::endl; 
    std::cout << std::tuple_size<std::remove_reference_t<decltype(std::get<1>(minutes))>>::value << std::endl; 
} 
+0

Perché non mi piace la tua prima opzione: è disordinata. È incline agli errori di battitura e, se riordino le righe o aggiungo una riga in un secondo momento, è necessario eseguire molte ridenominazioni variabili o lasciare i numeri di riga fuori ordine. Detto questo, sembra essere il modo più diretto per ottenere la giusta rappresentazione in memoria dei dati. – Josh

+0

Dovrò esaminare la seconda opzione, con std :: tuple e constexpr. Sono più a mio agio con C che con C++ (se non hai indovinato - ma sto usando alcune librerie C++ per questo progetto), e sto codificando per un sistema embedded con * tiny * memoria, quindi non lo faccio voglio usare qualsiasi funzione se non so esattamente cosa sta succedendo dietro le quinte.(Sì, mi rendo conto che la filosofia del design dietro C++, o OOP in generale, è che non dovresti * avere * bisogno di conoscere l'implementazione interna, ma ... :) – Josh

+0

@Josh Se hai paura di un ordine sbagliato - tu puoi usare 'BOOST_PP_ENUM' - vedi http://www.boost.org/doc/libs/1_39_0/libs/preprocessor/doc/ref/enum.html. – PiotrNycz

1

Bene che significa questa espressione

(const uint8_t[]) {1, 2, 3, 4, 5 END}, 

crea un oggetto temporaneo — temporanea, perché non ha alcun nome che può durare oltre la cui espressione è una parte — che viene distrutto alla fine della piena espressione, che significa che questo:

definisce "la piena espressione", a quel punto tutti gli oggetti temporanei avranno distrutto, un e la serie di puntatori minutes contiene puntatori che puntano a oggetti danneggiati, motivo per cui il compilatore sta dando un avvertimento.

Spero che questo aiuti.

+0

Sì, lo sospettavo. Quindi cosa devo fare * su di esso? :) – Josh

+0

Definirli prima come array. O meglio riprogettarlo. Prova a usare 'std :: array'. – Nawaz

+0

Puoi fare un esempio di cosa intendi? Deve esserci un modo per farlo che non richiede la creazione di dozzine di variabili nominate individualmente che in realtà non voglio o non intendo usare ... giusto? Questo metodo letterale composto era uno che ho ottenuto da una risposta qui su SO: è davvero sbagliato? – Josh

2
aggiornamento

: grazie a deniss per indicare la falla nella soluzione originale.

static constexpr uint8_t END = 0xff; 

template<uint8_t...x> 
const uint8_t* make() 
{ 
    static const uint8_t _[] = { x..., END }; 
    return _; 
} 

const uint8_t* const array[] = { 
    make<>(), 
    make<1, 2, 3, 4, 5>(), 
    make<8, 9>(), 
    make<10, 11, 12>() 
}; 
+0

Questo compila, anche se rende l'app compilata più grande, e ci vuole un po 'di memoria in più - non è l'ideale per un sistema embedded. Qualche idea del perché? Inoltre, non sono sicuro di cosa stia facendo. A quanto ho capito, una variabile statica dovrebbe allocare spazio una volta che persiste tra le chiamate alla funzione. Quindi, com'è questa funzione che alloca nuovi spazi ogni volta che viene chiamata? – Josh

+0

Sta generando una funzione statica per una diversa riga di numeri. Ogni funzione statica conterrà una matrice statica (e un mutex nascosto). Quando dici più memoria, quanto stiamo parlando? Dopo l'ottimizzazione, alcune centinaia di byte potrebbero valerne la pena per facilità di manutenzione. –

+0

Nota: per lo stesso numero di parametri, questa funzione genera solo 1 array (riempito con i valori della prima chiamata). – deniss