Bitsets and bitwise operators
Buffered IO
function overloading
A machine word
int flag;
31 <-------------> 0
char f;
2 nibbles of bits
0000 0000
Binary representation of 0x01
0000 0001
Binary representation of 0x02
0000 0010
Binary representation of 0x04
0000 0100
Binary representation of 0x08
0000 1000
Operators available in C (&, |, ^, ~, <<, >> )
Manipulating bits
Test --> test if a bit is set
if (f & 0x04 != 0) { printf ("set"); }
f --> xxxx xxxx
0x4 --> 0000 0100
-----------------
& --> 0000 0x00 --> either 0 or non zero
Set --> Set a particular bit to 1
f |= 0x01
Reset --> Set a bit to 0
f &= ~(0x01)
Toggle --> Toggle a bit from 1->0 or 0->1
f ^= 0x01
Keep track of kinds of pets in a house.
{ cat, mouse, dog, rabbit, fish, }
0000 0000
^^^^ ^^^^
|||| |||+- cat
|||| ||+-- mouse
|||| |+--- dog
|||| +---- rabbit
|||+------ fish
||+-------
|+--------
enum { cat =0x01, mouse = 0x02, dog = 0x04, rabbit = 0x08, fish
= 0x10 }
int pets = cat|dog|fish;
if (pets & dog) printf ("woof");
pets &= ~dog; // Goodbye rover...
0000 0001 = 1
0000 0010 = 2
0000 0100 = 4
"Shifting down" -- divide by 2
Counting out the bit positions
1 << 0 == 1
1 << 1 == 2
1 << 2 == 4
1 << 3 == 8
Test if bit number 4 is set
if (flag & (1 << 4)) { printf ("yup"); }
Why use buffered IO
Disk block size
Disk blocks range in size from 512 to 8192 bytes
Must read at least one whole block
memory access speed
Memory is fast
System call overhead.
ssize_t read(int fildes, void *buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte);
off_t lseek(int fildes, off_t offset, int whence);
Ideas
Buffer expensive read/write with a block of data allow
user
to use cheap memory operation.
| ----------1024------------|
v
v
-----------------------------
|xxxx
|
-----------------------------
^ ^
| +-- ptr
+-------base
int cnt; // how many are left to read
char*ptr; // Last charcter read
char*base; // Base of buffer
int File::fgetc ()
// if we already have filled in the buffer return that
if (--cnt >= 0) { return *ptr++ }
// otherwise fill the buffer and return the first char.
return filbuf();
}
int File::fillbuf() {
// Same as in book.
}
| ----------1024------------|
v
v
-----------------------------
|xxxx
|
-----------------------------
^ ^
| +-- ptr
+-------base
int cnt; // how many are left to read
char*ptr; // Next open spot in buffer
char*base;
void File::fputc (char x)
// is there room in the buffer for this character.
if (--cnt >= 0) { *ptr++ = x}
// otherwise fill the buffer and return the first char.
flushbuf(x);
}
// Flush any outstanding data and place argument as first char
// in empty buffer.
int File::flushbuf(int x) {
// Check the a write is legal.
// Allocate a write buffer if needed.
// Flush outstanding data.
// Add first char.
}
char hello[] = "hello";
File f ("hello.txt", "w+")
--> call File::File (const char *name, const char *)
f.fwrite (hello, sizeof (hello));
--> write each character into file
| ----------1024------------|
v
v
-----------------------------
|hello
|
-----------------------------
^ ^
| +--ptr
+-------base
f.fseek (0, SEEK_SET)
--> Move the buffer to an initial state
----> if writing write out unwritten data
----> lseek to proper position in file.
In C
int move (struct point*, int dx, intdy) {
}
tranformed int --> point_move (struct point*, int dx, intdy)
Why...If we have another move it would clash with the fuirst definition.
move (struct rect*, int dx, int dy);
But C++ allow 'overloading'
---> Use the same name for more than one definition
Distuingished by the arguments (not by return value).
class File {
public:
void write (char); // a single char
void write (int); // The ascii of the
number
void write (char*); // a string
void write (bool); // 'true' or 'false'
}
File f(...)
f.write ('a');
f.write ("Hello");
f.write (123);
f.write (true);
Operators are 'built-in' functions with special syntax
1 << 3 --> Compiler calculate shift
int x, y;
x + y --> call routine that calculate x+y
File f;
f << "Hello" --> ?? What to call
Use can 'overload' C++ operator with new definitions (but can't
change syntax).
void operator << (File&, char *);
Why didn;t I write (more efficient way to do things in C)
void operator << (File*, char *);
I can but then I would need to say
&f << "hello";
References are like pointers in many cases but easier on the eyes.
But what about
f << "hello " << 123;
(f << "hello ") << 123;
operator << (void, int) XXXX WRONG XXXXX
so we need to return the argument
File& operator << (File&f, int x) {
f.write (x);
return f;
}