《Beginning C++17》-學習筆記-Chapter 03-Working with Fundamental Data Types
The XOR operator is used less frequently than the & and | operators. Important applications arise, however, in for instance cryptography, random number generation, and computer graphics. XOR is also used for the backup of hard disk data by certain RAID technologies. Suppose you have three similar hard drives, two with data and one to serve as backup. The basic idea is to ensure that the third drive at all times contains the XOR’ed bits of all contents of the two other drives, like so:
Drive one ... 1010 0111 0110 0011 ...
Drive two ... 0110 1100 0010 1000 ...
XOR drive (backup) ... 1100 1011 0100 1011 ...
//不借助第三個變數,利用exclusive or運算,交換兩個變數的值 //給兩個int變數賦值,交換這兩個int變數的數值後輸出這兩個int變數的值;可用3, 14 兩個整數驗證 #include <iostream> int main() { int first{}, second{}; std::cout << "Enter two integers separated by a space: "; std::cin >> first >> second; first ^= second; second ^= first; first ^= second; std::cout << "In reverse order they are " << first << " and " << second << std::endl; }
#include <iostream> int main() { std::cout << sizeof(unsigned short) << std::endl;//輸出2,說明unsigned short佔2個byte, 即16 bit; std::cout << (0b0000000000000001 << 2) << std::endl;//輸出結果為4,即0b0000000000000100 std::cout << (0b0000000000000001 | 0b0000000000000010) << std::endl;//輸出結果為3,即0b0000000000000011 std::cout << ~(~0b0000000000000001);//數值不變,輸出結果仍為1,即0b0000000000000001 }
/*It will be convenient to display the data as hexadecimal values, and inserting std::hex in the output
stream does this. The hex is modal, so all subsequent integer output will be in hexadecimal format. It will be
easier to compare output values if they have the same number of digits and leading zeros. You can arrange
for this by setting the fill character as 0 using the std::setfill() manipulator and ensuring the field width
for each output value is the number of hexadecimal digits, which is 8(其中一項的設為了16,以示區別). The setfill() manipulator is modal,
so it remains in effect until you reset it. The std::setw() manipulator is not modal; you have to insert it into
the stream before each output value.*/
#include <iostream>
#include <iomanip>
int main()
{
std::cout << std::hex // Hexadecimal output
<< std::setfill('0'); // Fill character 0
unsigned int flags{ 0xFF }; // Flags variable
unsigned int bit1mask{ 0x1 }; // Selects bit 1
unsigned int bit6mask{ 0b100000 }; // Selects bit 6
unsigned int bit20mask{ 1u << 19 }; // Selects bit 20
std::cout << "Use masks to select or set a particular flag bit:";
std::cout << "\nSelect bit 1 from flags : " << std::setw(16) << (flags & bit1mask);
std::cout << "\nSelect bit 6 from flags : " << std::setw(8) << (flags & bit6mask);
std::cout << "\nSwitch off bit 6 in flags: " << std::setw(8) << (flags &= ~bit6mask);
std::cout << "\nSwitch on bit 20 in flags: " << std::setw(8) << (flags |= bit20mask)/*The parentheses around the expressions are necessary here because the precedence of << is higher than
& and |.*/
<< std::endl;
}
輸出結果:
#include <iostream>
#include <iomanip>
int main()
{
enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };/*When you define an enumeration, you’re creating a new type, so it’s also referred to as an enumerated data type.This defines an enumerated data type called Day, and variables of this type can only have values from the set that appears between the braces. The symbolic names between the braces are called enumerators.*/
Day yesterday{ Day::Monday };/*You use type Day just like any of the fundamental types. When you reference an enumerator, it must be qualified by the type name.*/
const Day poets_day{ Day::Friday };
enum class Punctuation : char { Comma = ',', Exclamation = '!', Question = '?' };/*The type specification for the enumerators goes after the enumeration type name and is separated from it by a colon. */
Punctuation ch{ Punctuation::Comma };
std::cout << "yesterday's value is " << static_cast<int>(yesterday) /*To output the value of today, you must cast it to a numeric type because the standard output stream will not recognize the type Day*/
<< static_cast<char>(ch) << " but poets_day's is " << static_cast<int>(poets_day)
<< static_cast<char>(Punctuation::Exclamation) << std::endl;
}
#include <iostream>
int main()
{
using my_type_new = unsigned long long; /* The using keyword enables you to specify a type alias, which is your own data type name that serves as an alternative to an existing type name.*/
typedef short int my_type_old;/*older syntax for defining an alias for a type name。在以前的程式碼中存在,不建議繼續使用*/
my_type_new new_type_val{ 1034 };
my_type_old old_type_val{ 231 };
std::cout << new_type_val << std::endl;
std::cout << old_type_val << std::endl;
}
All variables have a finite lifetime. They come into existence from the point at which you define them, and at some point they are destroyed—at the latest, when your program ends. How long a particular variable lasts is determined by its storage duration. There are four different kinds of storage duration:
- Variables defined within a block that are not defined to be static have automatic storage duration. They exist from the point at which they are defined until the end of the block, which is the closing curly brace of the block that encloses its
definition , }. They are referred to as automatic variables or local variables. Automatic variables are said to have local scope or block scope. - Variables defined using the static keyword have static storage duration. They are called static variables. Static variables exist from the point at which they are defined and continue in existence until the program ends.
- Variables for which you allocate memory at runtime have dynamic storage duration. They exist from the point at which you create them until you release their memory to destroy them.
- Variables declared with the thread_local keyword have thread storage duration.
Another property that variables have is scope. The scope of a variable is the part of a program in which the variable name is valid. Within a variable’s scope, you can refer to it, set its value, or use it in an expression. Outside of its scope, you can’t refer to its name. Any attempt to do so will result in a compiler error message.
The lifetime and scope of a variable are different things. Lifetime is the period of execution time over which a variable survives. Scope is the region of program code over which the variable name can be used.
You can define variables outside all of the functions in a program. Variables defined outside of all blocks and classes are also called globals and have global scope (which is also called global namespace scope).
Global variables have static storage duration by default, so they exist from the start of the program until execution of the program ends. If you don’t initialize a global variable, it will be zero-initialized by default. This is unlike automatic variables, which contain garbage values when uninitialized.
Only global variables have default initial values, which is 0, not automatic variables.
Global variables have a scope that extends from the point at which they’re defined to the end of the file.
Variables with global scope are accessible from anywhere within the program file that contains them, following the point at which they’re defined, except where a local variable exists with the same name as the global variable. Even then, they can still be reached by using the scope resolution operator (::).
#include <iostream>
long count1{ 999L }; // Global count1
int count3; // Global count3 - default intialization, = 0
int main()
{
int count1{ 10 }; // local count1, hides global count1
std::cout << "Value of outer count1 = " << count1 << std::endl;
std::cout << "Value of global count1 = " << ::count1 << std::endl;//you must use the scope resolution operator :: to access the global count1 in the output statement
std::cout <<"Value of outer count3 = " << count3 << std::endl;//
}
// Exercise 3-1. Output integer and its 1's complement in decimal and hexadecimal.
// The setw() manipulator only applies to the next output value so you must use it preceding each output value.
// std::internal causes the fill character, '0', to be used internal to the hexadecimal values.
// Because the program computes the complement of signed integers,
// the output of this program is strictly speaking undefined.
// That is: it depends on which binary representation of negative integers your computer uses.
// Nearly all computers employ the two's complement encoding though,
// which means that flipping all bits and adding one should negate your number.
#include <iostream>
#include <iomanip>
int main()
{
int value{};
std::cout << "Enter any integer: ";
std::cin >> value;
int inverted_value{ ~value };
unsigned int hex_digits{ 2 * sizeof(int) }; /*Hex digits in value;每個int型別的數值佔4 bytes, 32 bits, 每個16進位制的數字可以代表4個bit,所以, 1個32位的int型別數值可以用8個16進位制數字表示*/
unsigned int hex_value_width{ hex_digits + 2 }; // Add 2 for 0x prefix
unsigned int column_width{ hex_value_width + 4 }; // Output column width (add 4 for padding)
// Output column headings
std::cout << std::right << std::setw(column_width) << "value"
<< std::setw(column_width) << "~value"
<< std::setw(column_width) << "~value+1" << std::endl;
// Output hexadecimal values
std::cout << std::hex << std::showbase << std::internal << std::setfill('0')
<< " " << std::setw(hex_value_width) << value
<< " " << std::setw(hex_value_width) << inverted_value
<< " " << std::setw(hex_value_width) << inverted_value + 1 << std::endl;
// Output decimal values
std::cout << std::dec << std::right << std::setfill(' ')
<< std::setw(column_width) << value
<< std::setw(column_width) << inverted_value
<< std::setw(column_width) << inverted_value + 1 << std::endl;
}
// casting from double to long (using static_cast<>()) ensures that the fractional part of the double value is omitted.
#include <iostream>
int main()
{
double double_num_1{ 12.345 };
double double_num_2{ 12.567 };
std::cout<< static_cast<long>(double_num_1) << std::endl;//輸出12
std::cout << static_cast<long>(double_num_2) << std::endl;//輸出12
}
#include <iostream>
int main()
{
auto j { ~(~0u << 3) };/*~0 is composed of all 1s; shifting that three places to the left and complementing the result will leave 111, which is 7*/
std::cout << j << std::endl;//輸出7
}
// This program will only work if a single byte is 8 bit (virtually always the case),
// and the fundamental type int counts at least 4 bytes (true for most modern systems).
#include <iostream>
#include <iomanip>
int main()
{
unsigned int two_char_word{};
unsigned char ch{};//用來臨時存貯字元
//輸入兩個字元,存到two_char_word
std::cout << std::left << std::setw(26) << "Enter a character: ";
std::cin >> ch;
two_char_word |= ch;
std::cout << std::setw(26) << "Enter a second character: ";
std::cin >> ch;
two_char_word <<= 8; // Shift left 1 byte
two_char_word |= ch;
std::cout << "The hexadecimal number of the word containing 2 characters is " << std::right
<< std::hex << std::showbase << std::internal << std::setfill('0') << two_char_word << std::endl;
std::cout << std::setfill(' ');
// 將剛才的兩個字元倒序輸出
unsigned int mask{ 0x000000FF }; // Keep low order byte
ch = mask & two_char_word; // Low order byte
std::cout << std::setw(4) << ch;
ch = mask & (two_char_word >> 8); // 2nd byte
std::cout << std::setw(4) << ch;
}
#include <iostream>
#include <iomanip>
int main()
{
const unsigned R{ 0x00FF0000 };
const unsigned G{ 0x0000FF00 };
const unsigned B{ 0x000000FF };
enum class Color : unsigned
{
Red = R,
Green = G,
Blue = B,
Black = 0,
White = R | G | B,
Yellow = R | G,
Purple = R | B,
};
const Color color1{ Color::Yellow };
auto color{ static_cast<unsigned>(color1) }; // Get the enumerator value,這一步將color1的型別由Color轉換成auto型別,然後進行下面的&運算
std::cout << std::setw(38) << "The components of color1 (yellow) are:"
<< " Red:" << std::setw(3) << ((color & R) >> 16) //不能直接用color1 和 R做 & 運算,因為型別不一致,編譯時會報錯
<< " Green:" << std::setw(3) << ((color & G) >> 8)
<< " Blue:" << std::setw(3) << (color & B) << std::endl;
}