2015-02-14 16 views
21

Mam następujący minimalny kod C11, który definiuje strukturę zawierającą uint16_t (co oznacza, że ​​powinna być wyrównana do 2 bajtów) i chcę rzucić bufor wskaźnik do tej struktury.C11 alignas vs. clang -Wcast-align

Po ostrzeżeniu wszystko zaczęło się głośno narzekać, że wymagania wyrównania struktury nie są spełnione. Dodałem więc do bufora specyfikator C11 alignas, aby upewnić się, że bufor jest wystarczająco wyrównany, ale to nie zamknęło klang.

Moje pytanie brzmi: czy robię coś nie tak z alignas? Czy tylko to, że diagnostyka -Wcast-align patrzy tylko na typ argumentów, a nie na ręcznie określone wyrównanie? (Rozumiem, że mogę rzucić do void*, aby wyciszyć diagnostykę, ale ponieważ ten fragment kodu ma być przenośny, nie chcę wykonywać diagnostyki bocznej, chyba że jestem pewien, że jest fałszywy alarm.)

#include <stdint.h> 
#include <stdalign.h> 

struct foo { 
    uint16_t field1; 
}; 


int main(void) { 
    alignas(struct foo) char buffer[122] = {0}; 
    struct foo *foo = (struct foo*)buffer; 
    return foo->field1; 
} 

Opcje kompilatora i komunikat o błędzie: wersja

$ clang -ggdb -O3 foo.c -Weverything -Werror -Wno-c++98-compat -Wno-c11-extensions 
foo.c:11:23: error: cast from 'char *' to 'struct foo *' increases required alignment from 1 to 2 [-Werror,-Wcast-align] 
    struct foo *foo = (struct foo*)buffer; 
         ^~~~~~~~~~~~~~~~~~~~~~~~~ 

Compiler:

$ clang -v 
clang version 3.5.1 (tags/RELEASE_351/final) 
Target: x86_64-pc-linux-gnu 
Thread model: posix 
Selected GCC installation: /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4 

Aktualizacja: Nie ma żadnego ostrzeżenia po przesunięciu bufora i jego wyrównania do struktury. Interpretuję to jako wskazówkę, że klang rzeczywiście tylko patrzy na typy tego ostrzeżenia.

#include <stdint.h> 
#include <stdalign.h> 

struct foo { 
    uint16_t field1; 
}; 

struct bar { 
    alignas(struct foo) char buffer[122]; 
}; 


int main(void) { 
    struct bar bar = {{0}}; 
    struct foo *foo = (struct foo*)&bar; 
    return foo->field1; 
} 
+5

* A może to po prostu, że -Wcast wyrównać diagnostyczny patrzy tylko na rodzaj argumentów, a nie także na ręczne określone wyrównanie * to także moje przypuszczenie. – ouah

+0

Czy to nie jest - http://stackoverflow.com/a/15912208/1150918 istotne? – Kamiccolo

+0

@Kamiccolo: clang and clang ++ na moim komputerze nie kompiluje żadnej z "poprawnych" wersji tego postu. Komunikat o błędzie to "alignas" atrybut dotyczy tylko zmiennych, elementów danych i typów tagów "i" oczekiwano "; na końcu deklaracji ". – handschuhfach

Odpowiedz

2

Z brzękiem źródła, w SemaChecking.cpp: ~ 7862, wydaje się, że są tylko patrząc na typy jak ty wymienić:

CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee); 
    if (SrcAlign >= DestAlign) return; 

    // else warn... 

Wygląda mi na to, dzyń przygotowuje się do C- stylowy rzut, który z kolei sprawdzi wyrównanie rzutu.

void CastOperation::CheckCStyleCast() 
    -> Kind = CastKind Sema::PrepareScalarCast(...); 
     -> if (Kind == CK_BitCast) 
       checkCastAlign(); 

void checkCastAlign() { 
    Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange); 
} 

Oto metoda trochę więcej kontekstu:

/// CheckCastAlign - Implements -Wcast-align, which warns when a 
/// pointer cast increases the alignment requirements. 
void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) { 
    // This is actually a lot of work to potentially be doing on every 
    // cast; don't do it if we're ignoring -Wcast_align (as is the default). 
    if (getDiagnostics().isIgnored(diag::warn_cast_align, TRange.getBegin())) 
    return; 

    // Ignore dependent types. 
    if (T->isDependentType() || Op->getType()->isDependentType()) 
    return; 

    // Require that the destination be a pointer type. 
    const PointerType *DestPtr = T->getAs<PointerType>(); 
    if (!DestPtr) return; 

    // If the destination has alignment 1, we're done. 
    QualType DestPointee = DestPtr->getPointeeType(); 
    if (DestPointee->isIncompleteType()) return; 
    CharUnits DestAlign = Context.getTypeAlignInChars(DestPointee); 
    if (DestAlign.isOne()) return; 

    // Require that the source be a pointer type. 
    const PointerType *SrcPtr = Op->getType()->getAs<PointerType>(); 
    if (!SrcPtr) return; 
    QualType SrcPointee = SrcPtr->getPointeeType(); 

    // Whitelist casts from cv void*. We already implicitly 
    // whitelisted casts to cv void*, since they have alignment 1. 
    // Also whitelist casts involving incomplete types, which implicitly 
    // includes 'void'. 
    if (SrcPointee->isIncompleteType()) return; 

    CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee); 
    if (SrcAlign >= DestAlign) return; 

    Diag(TRange.getBegin(), diag::warn_cast_align) 
    << Op->getType() << T 
    << static_cast<unsigned>(SrcAlign.getQuantity()) 
    << static_cast<unsigned>(DestAlign.getQuantity()) 
    << TRange << Op->getSourceRange(); 
} 

static const Type* getElementType(const Expr *BaseExpr) { 
    const Type* EltType = BaseExpr->getType().getTypePtr(); 
    if (EltType->isAnyPointerType()) 
    return EltType->getPointeeType().getTypePtr(); 
    else if (EltType->isArrayType()) 
    return EltType->getBaseElementTypeUnsafe(); 
    return EltType; 
}