2017-07-05 9 views
0

Ich habe einen Zeiger auf eine Struktur, die ich über einen Adressbereich byteweise verschieben möchte. Ich habe einen Ansatz, der funktioniert, aber ist es hässlich in meinen Augen. Leider funktioniert der "nette" Weg nicht. Hier ist ein Mindest Beispiel:Gibt es eine "nette" Möglichkeit, einen Zeiger auf eine Struktur um ein Byte zu erweitern?

#include <stdint.h> 

typedef struct { 
    uint32_t a; 
    uint32_t b; 
} s_t; 

int main(int argc, char** argv) 
{ 
    uint32_t address = 17; 
    s_t* ps = (s_t*) address; 

    // ugly 
    uint8_t* gna = (uint8_t*) ps; 
    ++gna; 
    ps = (s_t*) gna; 

    // nice 
    ++((uint8_t*) ps); 
} 

Der Compiler meldet einen Fehler auf dem "schönen" Teil:

% gcc test.c -o test.bin 
test.c: In function 'main': 
test.c:17:5: error: lvalue required as increment operand 
    ++((uint8_t*) ps); 
    ^

ich den Fehler zu verstehen, aber ich dachte an eine uint8_t * Gießen würde einen L-Wert erstellen. Offensichtlich liege ich falsch.

Gibt es eine Möglichkeit, es besser zu machen?

+0

Nicht sicher, was Sie damit zu erreichen versuchen - aber das ist sehr wahrscheinlich Ergebnis in unausgerichtetem Speicherzugriff ... – Aconcagua

+0

Sie haben bereits einen Zeichenzeiger 'gna', warum verwenden Sie ihn nicht wie vorgesehen? Das Inkrementieren eines Strukturzeigers um ein Byte ist bedeutungslos. –

Antwort

4

Das ist nicht viel Sinn macht. Wenn Sie den Struct-Zeiger um 1 Byte erhöhen würden, würden Sie mit einer falsch ausgerichteten Adresse enden, was auf den meisten Systemen problematisch wäre. Ich muss davon ausgehen, dass Ihr System ist nicht einer der Mainstream 32/64 Bit-CPUs (nicht x86, ARM, PowerPC usw.) oder sonst Ihre Frage macht keinen Sinn.

Um die Adresse um ein Byte zu erhöhen, dies einfach tun:

ps = (s_t*) ((uintptr_t)ps + 1); 
+0

Es macht Sinn, aber da es nicht Teil des Problems ist, habe ich mich nicht darum gekümmert. Danke für den Hinweis "uintptr_t" - ich habe es zu selten benutzt, um mich daran zu erinnern ... – Markus

+0

@Markus Der Vorteil von 'uintptr_t' gegenüber' uint8_t' ist, dass Sie keine unerwarteten Probleme mit dem Pointer-Aliasing bekommen. Wenn Sie einen Strukturzeiger in den Zeiger 'uint8_t' konvertieren, ist das kein Problem, aber wenn Sie diesen Zeiger ändern, kennt der Compiler nicht mehr den "effektiven Typ" (Variablentyp) der Daten, die an diesem Ort gespeichert sind. Es wird angenommen, dass die Daten "uint8_t" sind. Und dann, wenn Sie zurück zu 's_t *' konvertieren, erhalten Sie eine strenge Zeiger-Aliasing-Verletzung. Wenn du nicht zurück auf 's_t *' wirfst, dann ist das egal. – Lundin

1

Als Lösung kann ein char* Zeiger auf die Struktur verweisen. Dann könnten Sie durch Hinzufügen dieses Zeigers auf die Bytes der spitzen Struktur zugreifen.

s_t a; 
unsigned char *b = (unsigned char*)&a; 
unsigned char next_byte = *(++b); 

Dann b durch Hinzufügen können Sie den Bytes der a zu bekommen. Die Knappheit dieser Lösung ist möglicherweise Sie erhalten Fehler oder Warnung in einigen Compilern, um den Zeiger auf Struktur durch einen Char-Zeiger übergeben.

+1

Sie * müssen * sogar eine Diagnose sehen, die Zuweisung eines solchen Zeigers ist eine Constraint-Verletzung. Aber das Werfen dieses Zeigers (in diesem speziellen Fall) sollte tun. Mit solch einer Besetzung ist Ihre Lösung in Ordnung, und die * Möglichkeit, jedes Byte der Struktur zu untersuchen. –

+1

'unsigned char * b = & a;' ist in der Tat nicht gültig C. Ändern Sie dies in 'unsigned char * b = (unsigniertes Zeichen *) & a;'. – Lundin

-2

Offensichtlich Typ Casting wie (uint8_t*) führt nicht zu einem Lvalue. Da Ihr Code in C++ ist, können Sie dieses Casting (uint8_t*&) verwenden.

+7

C++ ??? Datei ist test.c und Frage ist nicht einmal markiert C++ ... – Aconcagua

0

Wie wäre:

ps = (s_t*) (1 + (uint8_t*) ps); 
+1

Ich glaube, das wird eine strenge Aliasing-Verletzung verursachen, da der Compiler nicht den effektiven Typ der Daten wissen kann, die bei '1 + (uint8_t *) ps' gespeichert sind. Wenn daher davon ausgegangen wird, dass die Daten vom Typ "uint8_t" sind, erhalten Sie eine strikte Alias-Verletzung, wenn Sie versuchen, darauf als Struktur zuzugreifen. – Lundin

0

in Bezug auf den entsandten Code:

#include <stdint.h> 

// for flexability, do not overlap the 'typedef' with the struct definition 
// (and always use a struct tag name) 
typedef struct { 
    uint32_t a; 
    uint32_t b; 
} s_t; 

int main(int argc, char** argv) <-- causes 2 compiler warning messages 
// due to unused parameters 
// suggest: int main(void) 
{ 
    uint32_t address = 17; 
    s_t* ps = (s_t*) address; <-- 17 is not a valid address for a struct. 

    // ugly <-- however, it actually works 
    uint8_t* gna = (uint8_t*) ps; 
    ++gna; 
    ps = (s_t*) gna; <-- don't do this, 
    // use 'gna' to access the individual bytes 

    // nice 
    ++((uint8_t*) ps); <-- does not compile 
} 
Verwandte Themen