1 / 29

Manipulating Bit Fields in C

COMP 40: Machine Structure and Assembly Language Programming (Spring 2014). Manipulating Bit Fields in C. Noah Mendelsohn Tufts University Email: noah@cs.tufts.edu Web: http://www.cs.tufts.edu/~noah. Goals for this presentation. Learn to use C language to pack and extract bit fields

rian
Télécharger la présentation

Manipulating Bit Fields in C

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. COMP 40: Machine Structure and Assembly Language Programming (Spring 2014) Manipulating Bit Fields in C Noah Mendelsohn Tufts UniversityEmail: noah@cs.tufts.edu Web: http://www.cs.tufts.edu/~noah

  2. Goals for this presentation • Learn to use C language to pack and extract bit fields • Also: learn exact width C integer types

  3. Warning: • Because it makes the examples easier to understand, some of the code in these slides uses the syntax:0b11001001for literals. This is a GNU extension to the C language that is not allowed in COMP 40 code. You may use hex literals like this: 0xC9 as they are standard.

  4. Exact Width Integer Types

  5. Why exact width integer types? • The problem: C integer types aren’t fixed size • Size of char, int, long, etc. depends on platform and compiler • Sometimes we need to get a known size • The solution: stdint.h defines fixed size integers • int32_t: 32 bit signed integer • uint32_t: 32 bit unsigned integer • int16_t: 16 bit signed integer • uint64_t: 64 bit unsigned integer • Etc. • When writing bit packing code, you need to know or account for the size of the integers you’re manipulating

  6. Why Bother with Bit Fields?

  7. Why use bit fields? • Save space: • Storing 10 million values each ranging from 0-10 • If each is a 32 bit int: 40 megabytes • If each is a 4 bit int: 5 megabytes • Manipulate standard file formats and network packets

  8. How can we extract this 8 bit protocol number from the 32 bit field? Example: Internet Protocol Packet 32 bits LENGTH SVC TYPE V HDLN ID FLGS FRAG OFFSET TTL PROTOCOL HDR CHECKSUM SOURCE ADDRESS DESTINATION ADDRESS OPTIONS THE TCP OR UDP DATA (VARIABLE LEN)

  9. Extracting Bit Fields

  10. Extracting a bit field * uint16_t i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; * Note that extra spaces have been used in these boolean literals to make them, easier to read. Such spaces are not allowed in code (and, as previously noted, the 0b1001 syntax is a GNU extension not allowed in COMP 40 submissions anyway).

  11. Create maskto select bits we need Extracting a bit field uint16_t i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t mask = 0b 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0; “and” together i and mask (i & mask) == 0b 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0;

  12. Extracting a bit field uint16_t i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t mask = 0b 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0; “and” together i and mask? (i & mask) == 0b 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0; … and shift to finish the job (i & mask) >> 5 == 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1;

  13. Be careful with signed shifts uint16_tu = 0b 1 0 1 1 1 1 0 0 1 0 1 0 1 1 1 0; int16_t i = 0b 1 0 1 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t mask = 0b 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0; In this example, masking leaves on high order bit (u &= mask) == 0b 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0; (i &= mask) == 0b 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0; Signed shifts usually propagate that bit! u >> 13 == 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1; i >> 13 == 0b 1 1 1 1 1 1 1 1 1 1 1 1 1 10 1;

  14. Be careful with signed shifts uint16_tu = 0b 1 0 1 1 1 1 0 0 1 0 1 0 1 1 1 0; int16_t i = 0b 1 0 1 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t mask = 0b 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0; Unsigned is correctSigned has unwanted leading 1’s In this example, masking leaves on high order bit (u &= mask) == 0b 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0; (i &= mask) == 0b 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0; Signed shifts usually propagate that bit! u >> 13 == 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1; i >> 13 == 0b 1 1 1 1 1 1 1 1 1 1 1 1 1 10 1;

  15. Huh? Be careful with signed shifts Turns out that right shifting of a signedinteger is implementation define…but why? C wants these operations to be super-efficient, and on some hardware there’s no efficient signed shift. On our systems, the sign willpropagate and you may count on that in your homework submissions. uint16_tu = 0b 1 0 1 1 1 1 0 0 1 0 1 0 1 1 1 0; int16_t i = 0b 1 0 1 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t mask = 0b 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0; In this example, masking leaves on high order bit u &= mask: 0b 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1; i &= mask == 0b 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1; Signed shifts usually propagate that bit! u >> 13 == 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1; i >> 13 == 0b 1 1 1 1 1 1 1 1 1 1 1 1 1 10 1;

  16. How can we choose the bits to extract at runtime? unsigned short i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; int width = 3; int offset = 5; This time we’ll have to compute the mask /* mask is now 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 */ unsigned short mask = ~0; /* mask is now 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 */ mask = mask >> (16 - width) << (offset); Now, finish the job as before (i & mask) >> offset == 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1;

  17. We can adapt to integer size at runtime unsigned short i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; int width = 3; int offset = 5; This time we’ll have to compute the mask /* mask is now 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 */ unsigned short mask = ~0; /* mask is now 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 */ mask = mask >> (number_of_bits- width) << (offset); Now, finish the job as before (i & mask) >> offset == 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1; constnumber_of_bits= sizeof(unsigned short) * 8;

  18. Setting Bit Fields

  19. Putting new value in a bit field uint16_t i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t new_value = 0b 0 1 0; /* value we want * uint16_t mask = 0b 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1; “and” together i and mask (i & mask) == 0b 1 0 0 1 1 1 0 0 0 0 0 0 1 1 1 0;

  20. Important: we have zeros where new value is going Putting new value in a bit field uint16_t i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t new_value = 0b 0 1 0; /* value we want * uint16_t mask = 0b 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1; “and” together i and mask… (i & mask) == 0b 1 0 0 1 1 1 0 0 0 0 0 0 1 1 1 0;

  21. Putting new value in a bit field uint16_t i = 0b 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0; uint16_t new_value = 0b 0 1 0; /* value we want * uint16_t mask = 0b 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1; “and” together i and mask… (i & mask) == 0b 1 0 0 1 1 1 0 0 0 0 0 0 1 1 1 0; … shift the new value and use “or” to combine (i | (new_value << 5)) == 0b 1 0 0 1 1 1 0 0 0 1 0 0 1 1 1 0;

  22. Flag Bits

  23. Very common idiom – single bit flags One bit each /* Flag definitions */ const uint16_t HI_PRIORITY = 0x8000; const uint16_t SECURE = 0x4000; const uint16_t ARCHIVED = 0x2000; … const uint16_t BEAUTIFUL = 0x0002; constuint16_t SPECTACULAR = 0x0001;

  24. Can use binary instead of hex, but hard to count the zeros! Very common idiom – single bit flags /* Flag definitions */ const uint16_t HI_PRIORITY = 0b1000000000000000; const uint16_t SECURE = 0b0100000000000000; const uint16_t ARCHIVED = 0b0010000000000000; … const uint16_t BEAUTIFUL = 0b0000000000000010; constuint16_t SPECTACULAR = 0b0000000000000001;

  25. Use | to combine bits Initializing flags /* Flag definitions */ const uint16_t HI_PRIORITY = 0b1000000000000000; const uint16_t SECURE = 0b0100000000000000; const uint16_t ARCHIVED = 0b0010000000000000; … const uint16_t BEAUTIFUL = 0b0000000000000010; constuint16_t SPECTACULAR = 0b0000000000000001; uint16_t myflags = SECURE | BEAUTIFUL; /* secure and beautiful */

  26. Test flags with &  remember C treats anything != 0 as true! Testing flags /* Flag definitions */ const uint16_t HI_PRIORITY = 0b1000000000000000; const uint16_t SECURE = 0b0100000000000000; const uint16_t ARCHIVED = 0b0010000000000000; … const uint16_t BEAUTIFUL = 0b0000000000000010; constuint16_t SPECTACULAR = 0b0000000000000001; if (myflags & BEAUTIFUL) {…}; /* if beautiful */

  27. Testing flags Testing multiple flags on /* Flag definitions */ const uint16_t HI_PRIORITY = 0b1000000000000000; const uint16_t SECURE = 0b0100000000000000; const uint16_t ARCHIVED = 0b0010000000000000; … const uint16_t BEAUTIFUL = 0b0000000000000010; constuint16_t SPECTACULAR = 0b0000000000000001; if ((myflags& (BEAUTIFUL | SECURE)) == (BEAUTIFUL | SECURE)){…}; /* if beautiful and secure */

  28. Us | to turn on additional flags Turning flags on /* Flag definitions */ const uint16_t HI_PRIORITY = 0b1000000000000000; const uint16_t SECURE = 0b0100000000000000; const uint16_t ARCHIVED = 0b0010000000000000; … const uint16_t BEAUTIFUL = 0b0000000000000010; constuint16_t SPECTACULAR = 0b0000000000000001; myflags |= ARCHIVED; /* now it’s archived too */

  29. To turn off, & with all the other flags! Turning flags off /* Flag definitions */ const uint16_t HI_PRIORITY = 0b1000000000000000; const uint16_t SECURE = 0b0100000000000000; const uint16_t ARCHIVED = 0b0010000000000000; … const uint16_t BEAUTIFUL = 0b0000000000000010; constuint16_t SPECTACULAR = 0b0000000000000001; myflags &= ~BEAUTIFUL; /* but not beautiful anymore  */

More Related