Suppose that we wish to pack single byte unsigned numbers into a single unsigned long variable.
a.) pow(2, sizeof (unsigned long) * std::numeric_limits<unsigned char>::digits) independent values can be stored in a single unsigned long variable.
b.) Each individual packed machine byte has a range of integers in the interval [0, pow(2, sizeof (unsigned char) * std::numeric_limits<unsigned char>::digits) - 1].
c.) It is not necessarily more efficient. In the general case, packing data is an exercise in trading time for memory. This is usually the case, especially if the size of an individual packed datum isn't an integer power of (or is coprime with) the byte size. However, there are many cases IRL where processor-specific optimizations (e.g., SSE) can be applied for speed increases-- for example, when operating on byte strings.
See https://arxiv.org/pdf/1209.2137v6.pdf