The syntax you are using is somewhat forced by the way the data fields are defined in the header file:
#define UCSRB _SFR_IO8(0x0A)
#define TXB8 0
#define RXB8 1
#define UCSZ2 2
#define TXEN 3
#define RXEN 4
#define UDRIE 5
#define TXCIE 6
#define RXCIE 7
In this case each bit is defined by its position in the byte, so you need to create a byte with the bit in the proper position before doing anything. Hence the "(1<<RXEN)" that creates a byte (actually just a constant, not a byte) with a 1-bit in the proper place.
This makes a header file that documents the bit positions in a very clear fashion, but forces you to code the (1<<RXEN), which is resolved at compile time.
There are several other ways that the header file could have been coded. All are valid, but force the use of different syntax when using the bit fields.
For instance, it could have been coded something like this:
#define UCSRB _SFR_IO8(0x0A)
#define TXB8 0x01
#define RXB8 0x02
#define UCSZ2 0x04
#define TXEN 0x08
#define RXEN 0x10
#define UDRIE 0x20
#define TXCIE 0x40
#define RXCIE 0x80
Or
#define UCSRB _SFR_IO8(0x0A)
#define TXB8 1
#define RXB8 2
#define UCSZ2 4
#define TXEN 8
#define RXEN 16
#define UDRIE 32
#define TXCIE 64
#define RXCIE 128
In this case you could simply OR the fields together because each bit field is already defined with a 1 bit in the proper position. I think this format of bit definitions is the most common on various platforms with the code using +, ANDs and ORs to set, clear and test the bits. You can test for two bits by coding (TXB8 + RXB8), which results in a 3, which can be or'ed with the data field to test either bit.
This is less clear in the definition, but quick to code and bit a bit unclear in the code in that you are doing OR and ANDs to test, set, and clear bits. That is the assembler way though, so most programmers find it clear.
Another technique is to define C structures for each data byte that contains flags. For Instance:
struct {
int TXB8 : 1;
int RXB8 : 1;
int UCSZ2 : 1;
int TXEN : 1;
int RXEN : 1;
int UDRIE : 1;
int TXCIE : 1;
int RXCIE : 1;
} S_UCSRB;
The ":1" defines a bit field of length 1.
A variable must then be defined at the appropriate memory location (probably called UCSRB in this case). Positioning at a specific location in memory generally uses a C language extension or PRAGMA statement supported by your compiler. It can also be referenced using a pointer. Once the structure and variable are defined, bits can be referred to by their actual name.
For instance:
UCSRB.RXCIE=1;
if (UCSRB.RXCIE) { /* some code */ );
pUCSRB->S_UCSRB.RXCIE = 1;
I don't think this is as commonly used, but I think it both makes the header file clear and makes the code very clear. Maybe it is less common because it moves fully into a high-level language way of doing things, instead of an assembly language way, which coders seem to like.
I have seen all three techniques used and I think they are all fine. the key is that you need to look at the definition in the header file to see how the bits are defined and how to use them for your platform. You really need to do that for every bit field you use, because the header files may not be consistent.
Edit: Fix the examples I gave in the second technique.