How do I access a single bit?

Поделиться
HTML-код
  • Опубликовано: 9 окт 2023
  • Patreon ➤ / jacobsorber
    Courses ➤ jacobsorber.thinkific.com
    Website ➤ www.jacobsorber.com
    ---
    How do I access a single bit? // Bitwise operations can be a bit frustrating for new programmers. This video shows you a few ways to access a single bit, usually within a bitfield or bitvector.
    Related Videos:
    Bit Fields: • Bit Fields in C. What ...
    ***
    Welcome! I post videos that help you learn to program and become a more confident software developer. I cover beginner-to-advanced systems topics ranging from network programming, threads, processes, operating systems, embedded systems and others. My goal is to help you get under-the-hood and better understand how computers work and how you can use them to become stronger students and more capable professional developers.
    About me: I'm a computer scientist, electrical engineer, researcher, and teacher. I specialize in embedded systems, mobile computing, sensor networks, and the Internet of Things. I teach systems and networking courses at Clemson University, where I also lead the PERSIST research lab.
    More about me and what I do:
    www.jacobsorber.com
    people.cs.clemson.edu/~jsorber/
    persist.cs.clemson.edu/
    To Support the Channel:
    + like, subscribe, spread the word
    + contribute via Patreon --- [ / jacobsorber ]
    Source code is also available to Patreon supporters. --- [jsorber-youtube-source.heroku...]

Комментарии • 122

  • @filips7158
    @filips7158 7 месяцев назад +83

    A passer-by note : in C, you are only technically suppesed to do bitwise operations on unsigned integer types. Signed integer type behavior is signedness implementation specific. Most coding standards will shed a tear if you do this. They will even make you specifically declare your constants as unsigned. I personally allways fall back to unsigned types and use signed types only when necessary. Just saves me a lot of trouble in general.

    • @esdel
      @esdel 7 месяцев назад +1

      I think with C23 negative number representation gets standardized to 2s-complement and with that a lot of signed value behavior should get standardized as well, but I might be wrong.

    • @anon_y_mousse
      @anon_y_mousse 7 месяцев назад

      @@esdel I sure hope you're wrong. That would reduce C's portability because then the behavior will have to be explicitly redefined for 1's complement machines.

    • @xhivo97
      @xhivo97 7 месяцев назад +3

      @@anon_y_mousse 2's complement is in C23. 1's complement machines exist? (I realize you might have been sarcastic on that one lol)

    • @anon_y_mousse
      @anon_y_mousse 7 месяцев назад

      @@xhivo97 They're old and rare, but they do exist. It's just that this will be a constant craw in my beak when I try to tout the portability of C and proponents of every other language will point this out with glee even though C will still compile for such computers and many more that their language of choice won't work on and it'll irritate me. It's tough being one of a small handfuls of C proponents. Only makes it worse that now I hear they're adding constexpr to C. If they're just going to copy every C++ feature then I can't finish my own language fast enough.

    • @xhivo97
      @xhivo97 7 месяцев назад

      @@anon_y_mousse IDK what to tell, other than use an older compiler or -std=c89 if you must... Could you explain _why_ those are bad? Backwards compatibility with archaic machines is a not a good reason to hold on from having good features. I don't mean to be rude, but after some light searching I couldn't find any such machine, do you mind naming one for my own curiosity? I would be more concerned about potential UB when subtracting when on 2's complement.
      Like it or not the majority of C's issues could be made less bad with language features that play well with static analyzers and runtime sanitizers; VLA syntax which has been a (optional?) thing since c99 gives you a bit better bounds checking when using a sanitizer, constexpr would also provide some static checking benefits I imagine but I've never used such a feature so am not quite sure. But I'm sure the C standard is in good hands and you gotta move on with the times at some point.

  • @hampus23
    @hampus23 7 месяцев назад +6

    Keep up the excellent work with your educational content! 🙌

  • @osamaadil231
    @osamaadil231 7 месяцев назад +5

    Very informative! Keep up the good work ❤

  • @cusematt23
    @cusematt23 7 месяцев назад +5

    I’ve watched like 50 of your videos this week. Great stuff.

  • @deepakr8261
    @deepakr8261 7 месяцев назад +2

    How about the union method Dr Sorber? So assume the example of a 32 bit register with each bit having specific purposes.
    So you can define something as below:
    union {
    uint32_t num;
    struct {
    uint8_t a:1; // bit indicates something else
    uint8_t b:1; //1 bit indicating something
    uint8_t c:2; //2 bits indicating something
    ...
    }fields;
    }reg;
    So say you read the 32 bit register into num as
    reg.num = (volatile uint32_t *) (address)
    and then to access individual bits you can simply do
    reg.fields.a
    or
    reg.fields.b etc

    • @edgarbonet1
      @edgarbonet1 7 месяцев назад

      The order of bit fields is not specified by the C standard. This method may work on one compiler, fail on another one, and break on the next version of the compiler where it used to work.

    • @deepakr8261
      @deepakr8261 7 месяцев назад

      @@edgarbonet1 From the c99 standard "An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit." In this case, the storage unit is uint32_t as set by the union

    • @edgarbonet1
      @edgarbonet1 7 месяцев назад

      @@deepakr8261 The bit fields are indeed adjacent. The order of allocation, however, is implementation defined. You could expect a consistent order only if the platform's ABI specifies it.

  • @godnyx117
    @godnyx117 7 месяцев назад

    Top tier content! You are a true OG!

  • @bozhidarivaylov5611
    @bozhidarivaylov5611 7 месяцев назад

    wow that was a goood one. THANKS!!!!

  •  7 месяцев назад +5

    Would be interesting what the compiler does.
    If I write x & (0x1 n) & 0x1 with the example above.

    • @metroid031993
      @metroid031993 7 месяцев назад +1

      it depends on the compiler, really. I've seen both happen, in different scenarios with the same compiler even.

    • @edgarbonet1
      @edgarbonet1 7 месяцев назад +1

      Any half-decent compiler knows how to do constant folding. Thus, if n is a compile-time constant, 0x1

  • @Psychx_
    @Psychx_ 5 месяцев назад

    What's the advantage/disadvantage of using defines to create the bitmasks? Can't that also be done using an enum?

  • @savantshuia
    @savantshuia 5 месяцев назад +1

    What's the typeface you've used for the text on screen?

  • @TreeLuvBurdpu
    @TreeLuvBurdpu 7 месяцев назад

    Do you recommend that method of looping from the end, rather than starting at the end and then i--?

  • @walkero
    @walkero 7 месяцев назад

    Great video as always. Thank you

  • @h_bra
    @h_bra 7 месяцев назад

    Very useful video and good explanation. But i would have liked to get into more detail, for example explain how to change just one bit.
    Bit still great video and thanks for the content

    • @pumpkinhead002
      @pumpkinhead002 7 месяцев назад +2

      To set a bit to 1 you would create a mask of all zero and the bit to set equal to 1. Then you would logically OR "|" the mask with whichever variable that you want to set.
      If you want to set the bit to 0, then you would invert the mask you made above, such that the but to set is a 0 and the bits you want to keep are 1, then you AND the mask with your variable to set the bit to zero.

    • @Spielix
      @Spielix 7 месяцев назад

      Creel has a video "Bit Hacks from Beginner to Advanced" with some nice visualizations.

  • @shoyur
    @shoyur 7 месяцев назад +1

    I made a function to return a string of that binary, and the result was always empty, cause it was composing the string of only 0 or 1 as int, so empty characters, took me 15 min to find the bug.
    It made me learn that you can convert a 0 or 1 (as an int) to a char very easily by adding 48 cause 48+0=48 which is '0' in ASCII and 48+1=49 which is '1' in ASCII.

    • @edgarbonet1
      @edgarbonet1 7 месяцев назад +1

      Don't add 48, add '0'. Yes, “'0'” is just another way of writing “48”, but writing it as a character constant makes the programmer's intent clearer, which is important in the long run.

  • @matiasm.3124
    @matiasm.3124 7 месяцев назад

    Nice video as always..but i don't get why you have to finish with & 0x1 in the printf or the macro when you do the bitwise..

    • @marwan7614
      @marwan7614 7 месяцев назад +7

      Let's say you have :
      00001010
      ^
      To check the second bit you first shift so to the right by one so >> 1 then it becomes:
      00000101
      ^
      But then you have an extra bit so to get rid of it you do an AND(&) operation so
      00000101
      &
      00000001
      =
      00000001
      Then only the first bit is left if was 0 the the result would be 00000000
      Btw 0x1 = 00000001

    • @matiasm.3124
      @matiasm.3124 7 месяцев назад +1

      @@marwan7614 very nice explained.. thanks for your time.

  • @zacharymiller7573
    @zacharymiller7573 7 месяцев назад

    Which editor do you use for programming?

    • @errodememoria
      @errodememoria 7 месяцев назад +2

      The one in the video is VSCode

  • @artem.pirkhal
    @artem.pirkhal 7 месяцев назад

    Hi Jacob. Many thanks for your work.
    Could you explain, how does delete operator knows exactly how many memory we allocated by new operator, and how we can repeat this logic for malloc/free functions in C?
    I tried to get shifted pointer to sizeof(size_t) bytes before actual pointer and find that we have strange number which is pretty much allows us to know how big chunk was allocated but for my machine it was never lower than 33 bytes even if I requested single sizeof(int ) memory. So I have question: why this allocated block is always has size so much bigger than was requested and why this size always follows condition (size % 2 == 1)? I mean why this lower bit is always(?) equals 1. Short code example below
    void *p = malloc(sizeof(void));
    size_t *ph = (size_t *)p - 1;
    printf("size: %lu
    ", *ph);

    • @DiThi
      @DiThi 7 месяцев назад

      As far as I know, there's no official way in C or C++ to get the amount of allocated memory. Each standard library in each OS can do it a different way. What you do here is undefined behaviour (i.e. it may work for you but it can probably fail for someone else). If you want to ensure you always know the allocated length, you have to write replacements of malloc and free yourself which allocate one extra size_t where you store the length, where you return the pointer next to this number, and where you calculate the previous one before calling the actual free.

    • @artem.pirkhal
      @artem.pirkhal 7 месяцев назад

      @@DiThi Yep. I know it. But I still want to know why memory header ((size_t *)ptr - 1) contains this weird number 33, 49, 65, 81, 129? For what purpose this lower bit set in 1? Does it means that next 32, 48, 64, 80, 128 bytes was allocated? Then why it doesn't switch to 0 after we free this memory? I just want to know what this header value means. I tried to find this information but I'm still here... I hope you, Or Jacob could help me?

    • @DiThi
      @DiThi 7 месяцев назад

      @@artem.pirkhal operating systems don't allocate ranges of bytes, they allocate entire pages (in most systems that's 4kb). So malloc and friends keep track of used memory within those pages in some other way, like storing the size of the allocated memory at the beginning of each chunk. I just checked that code in my system (linux) and I get the same result, some odd number multiple of 16 + 16 + 1. My guess is that it's actually not the size, but a combination of the size and some flags. That extra 1 is probably some flag with a different meaning. Since it's always aligned to 16 bytes, it doesn't need the lower 4 bits, so it's zeroed out and used for other purposes. I accidentally corroborated this by adding more parameters than arguments to printf, which prints internal CPU registers and one of them is the length without the extra +1.
      It's probably not set to zero because it's likely keeping small freed buffers around to give when requesting another malloc, so it doesn't waste time calculating stuff.

    • @artem.pirkhal
      @artem.pirkhal 7 месяцев назад

      @@DiThi Make sense. But using overloading of new/delete we are able to track memory allocation in C++. Sad that we don't have this abilities in C. Only by making custom allocation/freeing macros or function overloading. Interesting to know, how that implemented in C++

    • @DiThi
      @DiThi 7 месяцев назад

      @@artem.pirkhal We can certainly do that in C as well. There's many custom allocators, but the simplest way to do it in your application is just by doing e.g. #define malloc(x) my_malloc(x), and from your malloc you can call the original one. Of course the macros must either come after their definition or not be present at all in its own .c file, otherwise you can't call the original malloc. Unless you use their alternate names. E.g. in linux glibc we have __glibc_malloc and __glibc_free.

  • @user-ke7yn6ru5e
    @user-ke7yn6ru5e 7 месяцев назад

    Could you make a video about nan in c?

  • @rian0xFFF
    @rian0xFFF 7 месяцев назад +1

    Nice

  • @baguettedad
    @baguettedad 7 месяцев назад +7

    But instead of a macro, wouldn't it be easier to use enums for the same purpose?
    Great video btw!

    • @MechPaul
      @MechPaul 7 месяцев назад

      Why not both?

    • @baguettedad
      @baguettedad 7 месяцев назад +3

      @@MechPaul macros can be a pain to debug

    • @maxaafbackname5562
      @maxaafbackname5562 7 месяцев назад +1

      You mean to define the bit constants?

    • @zokalyx
      @zokalyx 7 месяцев назад

      not really, since the idea of bitfields is encompassing 8 independent properties of something. there are 2^8 states possible, which you would have to define individually as an enum. It's just not the appropiate structure.

    • @rustycherkas8229
      @rustycherkas8229 7 месяцев назад

      @@zokalyx enum { foo = 0x1, bar = 0x2, gib = 0x4, gab = 0x8, bob = 0x10..., comboX = foo | bar, comboY = foo | gib | gab, };
      Simply specifying tokens and their values.
      One doesn't need to fill in 256 (or more) using the default 'auto increment' feature of enum's.

  • @jarlfenrir
    @jarlfenrir 7 месяцев назад

    my guess before watching: bit shift so your desired bit is at 0th position, AND with 1 and then you can check if your value is 0 or 1?

  • @edgeeffect
    @edgeeffect 7 месяцев назад +1

    Considering that most ISAs have a simple bit set/reset instruction... it always annoys me how complex and unintuitive it is in nearly all high level languages... but I AM a huge assembly language enthusiast and snob.

    • @edgarbonet1
      @edgarbonet1 7 месяцев назад

      The AVR instruction set has the instructions “Set Bits in Register” and “Clear Bits in Register”. However, if you look at their binary encodings, you realize that these are merely aliases for “Logical OR with Immediate” and “Logical AND with Immediate”. These aliases are just hiding the bitwise boolean operations in the same manner as the macro used in this video.

  • @bsdooby
    @bsdooby 7 месяцев назад

    A bit of bikesheding here: why are you (still) using `void` in an otherwise empty parameter list (here of the main function)?

    • @JacobSorber
      @JacobSorber  7 месяцев назад +1

      old habits? 🤔

    • @bsdooby
      @bsdooby 7 месяцев назад

      ​@@JacobSorber No offense intended, I was just curious. Thx for your reply!

    • @JacobSorber
      @JacobSorber  7 месяцев назад +1

      @@bsdooby none taken. it's a good reminder.

  • @etiennepretorius1993
    @etiennepretorius1993 7 месяцев назад +10

    What about a bit field in a struct?

    • @bloom945
      @bloom945 7 месяцев назад +5

      I like this a lot more since you can make it super clear what everything is. For the same reason I tend to prefer enums over macros for bitflags.

    • @rexjuggler19
      @rexjuggler19 7 месяцев назад +10

      You beat me to it. You can address bits using a structure which might be a bit easier. struct {
      unsigned readonly : 1;
      unsigned hidden : 1;
      unsigned system : 1;
      unsigned volumelabel : 1;
      unsigned subdirectory : 1;
      unsigned archive : 1;
      unsigned bitSix : 1;
      unsigned bitSeven : 1;
      } fatByte

      fatByte.readonly = 1
      fatByte.hidden = 1
      fatByte.system = 0
      fatByte.volumelabel = 0
      fatByte.subdirectory = 1
      fatByte.archive = 0
      A good example of using bit fields would be for GPIO access on devices like raspberry Pis to activate LEDs etc.

    • @hoffiee123
      @hoffiee123 7 месяцев назад +3

      @@rexjuggler19 I agree, and that together with unions you can easily pass it between interfaces which doesn't use bit fields without having to do some type casting

    • @gosnooky
      @gosnooky 7 месяцев назад +3

      This is the way

    • @maxaafbackname5562
      @maxaafbackname5562 7 месяцев назад +1

      The problem with bitfields is that the order is undefined.
      As in that is undefined if the first bit is bit zero or not

  • @jarlfenrir
    @jarlfenrir 7 месяцев назад

    When you provided ARCHIVE value, shouldn't it be a 0xA instead of 0x10?

    • @edgarbonet1
      @edgarbonet1 7 месяцев назад

      No:
      - 0xA is decimal 10, binary 1010
      - 0x10 is decimal 16, binary 10000.

    • @jarlfenrir
      @jarlfenrir 7 месяцев назад

      @@edgarbonet1Right, 0xA would set two flags at once. My bad.

  • @blameItleaveit
    @blameItleaveit 7 месяцев назад

    It would be great if we could purchase your C course from somewhere instead of juggling from youtube videos

  • @itsdrdy5551
    @itsdrdy5551 7 месяцев назад +1

    instead of 0x just use 0b

  • @zxuiji
    @zxuiji 7 месяцев назад +12

    Shouldn't teach people to hard code values like 8 bits, we have CHAR_BIT, should use every time to teach people to be in the habit of using adaptable macros instead of hard coded values. The people who learnt to hard code 8 bits as the length of bytes will surely in the future encounter situations where they will have to go through their whole code base replacing that value because of that bad assumption (granted 9 times outa 10 it will be because of old systems that they need to but there's surely research systems etc that use bigger values too)

    • @casperes0912
      @casperes0912 7 месяцев назад +1

      Find me a machine made after the 486 where the value is different

    • @anon_y_mousse
      @anon_y_mousse 7 месяцев назад +1

      @@casperes0912 Does that stop the older machines from existing?

    • @zxuiji
      @zxuiji 7 месяцев назад +1

      @@casperes0912 I'm not a researcher (well not a professional one anyways) so I don't know what machines there are with it different but then there are dumbass data modals like SILP64, what's to stop some research facility ordering a custom computer where it's different? What's to stop them then using any of the software available to the public on said machine? The art of programming is to throw out every assumption you can, no ifs, buts or whats about it, just throw them out.

    • @Hauketal
      @Hauketal 7 месяцев назад +1

      ​@@casperes0912Lots of signal processing CPUs aka DSP are not byte oriented. They often have 12 or 16 as CHAR_BIT. And yes, even new ones.

    • @casperes0912
      @casperes0912 7 месяцев назад

      I’ll use avx without any runtime checks

  • @thebirdhasbeencharged
    @thebirdhasbeencharged 7 месяцев назад

    Drop the shirt link

  • @rammrras9683
    @rammrras9683 7 месяцев назад +1

    How to toggle a single bit ?

  • @mowinckel10
    @mowinckel10 7 месяцев назад

    A thing I like to do, is instead of writing the bitmasks in HEX, I often write them in binary. Basically anytime if I feel the need to comment the binary after

    • @pumpkinhead002
      @pumpkinhead002 7 месяцев назад

      C doesn't support binary literals. That is a GCC implementation.

    • @ajtan2607
      @ajtan2607 7 месяцев назад

      @@pumpkinhead002 C23 includes binary literals that works similarly to the one in C++. Apostrophes (') can also be used as digit separators. Here's an example:
      0b'0000'0000'0000'0000

    • @Spielix
      @Spielix 7 месяцев назад

      And especially if you are using 64 bit types, writing out all bits on their own will be less useful IMO because counting the positions is harder than learning how to read hex.

  • @grimvian
    @grimvian 7 месяцев назад +1

    Not many youtubers are making C videos these days, so I really, really appreciate you are spending time to make them.
    However for a C learner like me, the videos are made in a way that are very zappy to me and the you are talking very, very fast. But maybe the audience you aim are more experienced and much better to English, than me. From a pedagogical view, I think, there are to much information on the screen. The code is only using about half of the screen space and the music is just noise and totally irrelevant to me. Prerequisites knowledge for the video will also be very fine.
    The best learning videos for me, just start immediately.

    • @TreeLuvBurdpu
      @TreeLuvBurdpu 7 месяцев назад

      I agree mostly, but these are level 200 and expect some familiarity. He is publishing for his students and similar. There are other introductory C Lang videos available.

    • @grimvian
      @grimvian 7 месяцев назад

      ​@@TreeLuvBurdpu We all have our preferences and I consider myself at a medium level. The best C Teacher I know is Kris Jordan: ruclips.net/p/PLKUb7MEve0TjHQSKUWChAWyJPCpYMRovO

    • @grimvian
      @grimvian 7 месяцев назад

      @@TreeLuvBurdpu More than two hours of exellent C talk with no noice and from level 0 to at least level 200, I think. How I program C with Eskild Steeberg: ruclips.net/video/443UNeGrFoM/видео.html

    • @edgarbonet1
      @edgarbonet1 7 месяцев назад

      As a non-native English speaker, I do find the speech a bit fast at times. However, his articulation is very clear, so it is easy to follow nevertheless. You may try slowing down the video if you are struggling.

    • @grimvian
      @grimvian 7 месяцев назад

      @@edgarbonet1 I agree, but I also oppose irrelevant noises e.g. music and fancy video editing. Also I don't need to know, which IDE that is used, but just the code.

  • @danielrhouck
    @danielrhouck 7 месяцев назад

    In many cases it’s more accurate to say computers represent everything in sexaquinquabicentesrmal, not binary. But nobody wants to say that partly because *simplified* computers do use binary and partly because, well, do *you* want to regularly pronounce that?

    • @maxaafbackname5562
      @maxaafbackname5562 7 месяцев назад +2

      No idea what numberingsystem you are referring to.
      Google can't find anything about it.
      Is it something like 64?
      In some sense you are correct, there are 64 wordsizes, but also other wordsizes, like 32 or even not a power of two.
      So saying that computers are "that" is already incorrect.
      But a "computer" can do more than operating on a whole word, like the arithmetic instructions.
      Enough instructions operatate on a single bit or on separate bits in a word.
      Further more: at the hardware level, only two levels are used.
      These levels are encoded as 0 and 1.
      Another way to say that computers use binary.

    • @danielrhouck
      @danielrhouck 7 месяцев назад

      @@maxaafbackname5562 Base 256. Pretty much everything operates on a byte at a time and we have byte-addressable RAM not bit-addressable RAM.

    • @HansLemurson
      @HansLemurson 7 месяцев назад

      It's a good thing that bases that are powers of 2 are trivial to convert between!
      Imagine dealing with base 256, but you have to use a unique symbol for each value...

    • @danielrhouck
      @danielrhouck 7 месяцев назад

      @@HansLemurson Yeah, often binary is easier to work with because of that. Just it sometimes helps to think of what the computer is doing as operating on a byte at a time.

    • @HansLemurson
      @HansLemurson 7 месяцев назад

      @@danielrhouck I like how Hexadecimal strikes a balance between not having to use too many digits, but also not having too many distinct values. 16 values is easy to wrap your brain around.
      How computers actually process the data though can vary from machine to machine. I remember building a redstone calculator in minecraft using comparators which was actually _natively_ hexadecimal, since there are 16 different signal strength values.
      In modern electronic computers, memory-access is byte-aligned, but data is read into the processor in multi-byte words, so when you just want to look at a single byte you have to load 4, and then ignore 3 of them.

  • @hansibull
    @hansibull 7 месяцев назад

    I would usually do [sharp]define BITVALUE(X, N) !!((X) & 1

  • @TheMeaningofHaste
    @TheMeaningofHaste 7 месяцев назад

    Can combine the #defines like this? -> READONLY | HIDDEN

    • @maxaafbackname5562
      @maxaafbackname5562 7 месяцев назад +4

      Yes, because there are no overlapping bits in the definitions.

    • @JacobSorber
      @JacobSorber  7 месяцев назад +1

      Definitely. Thanks!

    • @rustycherkas8229
      @rustycherkas8229 7 месяцев назад +2

      Yes, but you need to take care.
      #define b0 (1u