Arithmetik auf uintptr_t vs unsigned char*



  • Ich schreibe gerade einen simplen Allokator, bei dem ich offenbar viel mit Pointern hantieren muss. Momentan gehe ich davon aus, dass gilt

    unsigned char*  ptr = something;
    size_t s = something;
    unsigned char* end_ptr = ptr + s;
    uintptr_t end = (uintptr_t)ptr + s;
    
    assert(end_ptr == (unsigned char*)end);
    

    (solange natürlich bei den Addidionen keine Überläufe sind). Ist das UB oder darf ich davon ausgehen, dass ich auf uintptr_t so rechnen kann wie auf unsigned char*?
    Zusätzlich verwende ich auf den uintptr_t noch einige binäre Operatoren, um Zeiger auf das gewünschte Alignment zu biegen z.B.:

    // alignment must be power of 2
    
    uintptr_t round_down(uintptr_t x, size_t alignment)
    {
        return x & ~(alignment - 1);
    }
    
    uintptr_t round_up(uintptr_t x, size_t alignment)
    {
        return (x | (alignment - 1)) + 1;
    }
    

    Zählt sowas auch zur Pointerarithmetik?



  • Ich kann dir nur empfehlen: lass die Zeigercasterei sein und erst recht Versuche, manuell irgendwelche Zeigerwerte für Alignments zurechtzurechnen.
    void*,char*,signed char* und unsigned char* haben immer das "beste" Alignment, das sollte einleuchtend sein, da malloc void* liefert und schließlich alle anderen Objektzeigertypen bedienen muss; alles weitere solltest du davon ableiten können.

    Verkappte Zeigercasts ähnlich wie dein Beispiel sind dann natürlich auch immer UB:

    int *pi; double *di; void *p;
    p = pi;
    di = p;
    *di; UB
    


  • @Wutz Ich denke es ist ziemlich klar dass @Biolunar nicht einfach malloc verwendet und/oder einen Fall hat wo das Alignment das er von malloc bekommt nicht ausreicht (SSE/...).



  • @wutz damit wurden 0 Fragen beantwortet.

    void*,char*,signed char* und unsigned char* haben immer das "beste" Alignment

    ist im Allgemeinen falsch. Ein unsigned char* kann auf einen int zeigen um dessen Objektrepräsentation auszulesen oder zu verändern. Oder er kann auf einen long double zeigen, welcher auf vielen Platformen ein strengeres Alignment hat als ein int.
    Es ist aber wahr, dass malloc immer das beste Alignment zurückliefert, praktisch also mindestens alignof(maxalign_t). Das reicht aber nicht immer aus, daher gibt es seit C11 void *aligned_alloc(size_t alignment, size_t size); als Ersatz für malloc.

    Da ich momentan ohne libc arbeite, gibt es sowieso keine Speicherverwaltungsfunktionen und ich muss selbst zusehen wie ich damit hantiere.

    Mein Interface sieht derzeit wie folgt aus:

    typedef void fs_memory_pool_t;
    fs_memory_pool_t* fs_memory_pool_init(void* ptr, size_t size);
    void* fs_alloc(fs_memory_pool_t* pool, size_t size, size_t alignment);
    void fs_free(fs_memory_pool_t* pool, void const* mem);
    

    Hier übergebe ich der ersten Funktion einen Block von Speicher, der fortan als memory pool gehandhabt wird. Von diesem Pool kann ich dann kleinere Speicherstücke anfordern und freigeben. Primitiv und vor allem kein Ersatz für einen general purpose allocator, aber funktionabel.

    Ich hoffe das erklärt warum ich die Adressen von Zeigern modifiziere. Ich glabe aber inzwischen, dass meine Methode UB ist, denn ich finde keine Stelle im Standard, die für void *p, *q; aus p < q auch (uintptr_t)p < (uintptr_t)q folgern würde.

    § 6.3.2.3 sagt

    5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. 67)
    6 Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
    7 A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned 68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

    Wenn ich 7 richtig verstehe, müsste ich also den Pointer byteweise inkrementieren und dann zu einem integer konvertieren um auf das Alignment zu prüfen. Das klingt dämlich und unnötig rechenintensiv.


Anmelden zum Antworten