Overview

 

 

   Bitsets and bitwise operators
   Buffered IO
   function overloading
 
 
 

Bitsets and bitwise operators

=============================
 

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
 
 

Bitsets

-------

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...
 
 

Shift operators

 Shift move a bit string up or down
 "shifting up" -- multiply by 2

  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"); }
 
 

Buffer IO

=========
 

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.
 

Reading


| ----------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.
}
 
 

Writing

--------

| ----------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.
}
 
 
 
 

Reading/Writing

----------------

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.
 
 
 
 
 
 

Function overloading

====================
 

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;
}