B. Stroustrup - The C++ Programming Language (794319), страница 38
Текст из файла (страница 38)
Note the dramatic difference in the meaning of a U prefix for a character (unsigned) and for a string UTF-32encoding (§7.3.2.2).In addition, a user can define new suffixes for user-defined types. For example, by defining auser-defined literal operator (§19.2.6), we can get"foo bar"s123_km// a literal of type std::string// a literal of type DistanceSuffixes not starting with _ are reserved for the standard library.6.2.7 voidThe type void is syntactically a fundamental type. It can, however, be used only as part of a morecomplicated type; there are no objects of type void.
It is used either to specify that a function doesnot return a value or as the base type for pointers to objects of unknown type. For example:void x;void& r;void f();void∗ pv;// error: there are no void objects// error: there are no references to void// function f does not return a value (§12.1.4)// pointer to object of unknown type (§7.2.1)When declaring a function, you must specify the type of the value returned. Logically, you wouldexpect to be able to indicate that a function didn’t return a value by omitting the return type. However, that would make a mess of the grammar (§iso.A). Consequently, void is used as a ‘‘pseudoreturn type’’ to indicate that a function doesn’t return a value.6.2.8 SizesSome of the aspects of C++’s fundamental types, such as the size of an int, are implementationdefined (§6.1). I point out these dependencies and often recommend avoiding them or taking stepsto minimize their impact.
Why should you bother? People who program on a variety of systems oruse a variety of compilers care a lot because if they don’t, they are forced to waste time finding andfixing obscure bugs. People who claim they don’t care about portability usually do so because theyuse only a single system and feel they can afford the attitude that ‘‘the language is what my compiler implements.’’ This is a narrow and shortsighted view. If your program is a success, it will beported, so someone will have to find and fix problems related to implementation-dependent features. In addition, programs often need to be compiled with other compilers for the same system,and even a future release of your favorite compiler may do some things differently from the currentSection 6.2.8Sizes149one.
It is far easier to know and limit the impact of implementation dependencies when a programis written than to try to untangle the mess afterward.It is relatively easy to limit the impact of implementation-dependent language features. Limiting the impact of system-dependent library facilities is far harder. Using standard-library facilitieswherever feasible is one approach.The reason for providing more than one integer type, more than one unsigned type, and morethan one floating-point type is to allow the programmer to take advantage of hardware characteristics. On many machines, there are significant differences in memory requirements, memory accesstimes, and computation speed among the different varieties of fundamental types. If you know amachine, it is usually easy to choose, for example, the appropriate integer type for a particular variable.
Writing truly portable low-level code is harder.Here is a graphical representation of a plausible set of fundamental types and a sample stringliteral (§7.3.2):char'a'bool1shortintlonglong long75610000000012345678901234567890int∗&c1double1234567e34long doublechar[14]1234567e34Hello, world!\0On the same scale (.2 inch to a byte), a megabyte of memory would stretch about 3 miles (5 km) tothe right.Sizes of C++ objects are expressed in terms of multiples of the size of a char, so by definitionthe size of a char is 1. The size of an object or type can be obtained using the sizeof operator(§10.3).
This is what is guaranteed about sizes of fundamental types:•••••1 ≡ sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long)1 ≤ sizeof(bool) ≤ sizeof(long)sizeof(char) ≤ sizeof(wchar_t) ≤ sizeof(long)sizeof(float) ≤ sizeof(double) ≤ sizeof(long double)sizeof(N) ≡ sizeof(signed N) ≡ sizeof(unsigned N)≤ sizeof(long long)150Types and DeclarationsChapter 6In that last line, N can be char, short, int, long, or long long. In addition, it is guaranteed that a charhas at least 8 bits, a short at least 16 bits, and a long at least 32 bits. A char can hold a character ofthe machine’s character set. The char type is supposed to be chosen by the implementation to bethe most suitable type for holding and manipulating characters on a given computer; it is typicallyan 8-bit byte.
Similarly, the int type is supposed to be chosen to be the most suitable for holdingand manipulating integers on a given computer; it is typically a 4-byte (32-bit) word. It is unwiseto assume more. For example, there are machines with 32-bit chars. It is extremely unwise toassume that the size of an int is the same as the size of a pointer; many machines (‘‘64-bit architectures’’) have pointers that are larger than integers. Note that it is not guaranteed thatsizeof(long)<sizeof(long long) or that sizeof(double)<sizeof(long double).Some implementation-defined aspects of fundamental types can be found by a simple use ofsizeof, and more can be found in <limits>.
For example:#include <limits>// §40.2#include <iostream>int main(){cout << "size of long " << sizeof(1L) << '\n';cout << "size of long long " << sizeof(1LL) << '\n';cout << "largest float == " << std::numeric_limits<float>::max() << '\n';cout << "char is signed == " << std::numeric_limits<char>::is_signed << '\n';}The functions in <limits> (§40.2) are constexpr (§10.4) so that they can be used without run-timeoverhead and in contexts that require a constant expression.The fundamental types can be mixed freely in assignments and expressions.
Wherever possible,values are converted so as not to lose information (§10.5).If a value v can be represented exactly in a variable of type T, a conversion of v to T is valuepreserving. Conversions that are not value-preserving are best avoided (§2.2.2, §10.5.2.6).If you need a specific size of integer, say, a 16-bit integer, you can #include the standard header<cstdint> that defines a variety of types (or rather type aliases; §6.5). For example:int16_t x {0xaabb};int64_t xxxx {0xaaaabbbbccccdddd};int_least16_t y;int_least32_t yyint_fast32_t z;// 2 bytes// 8 bytes// at least 2 bytes (just like int)// at least 4 bytes (just like long)// the fastest int type with at least 4 bytesThe standard header <cstddef> defines an alias that is very widely used in both standard-library declarations and user code: size_t is an implementation-defined unsigned integer type that can hold thesize in bytes of every object.
Consequently, it is used where we need to hold an object size. Forexample:void∗ allocate(size_t n); // get n bytesSimilarly, <cstddef> defines the signed integer type ptrdiff_t for holding the result of subtracting twopointers to get a number of elements.Section 6.2.9Alignment1516.2.9 AlignmentAn object doesn’t just need enough storage to hold its representation.
In addition, on somemachine architectures, the bytes used to hold it must have proper alignment for the hardware toaccess it efficiently (or in extreme cases to access it at all). For example, a 4-byte int often has to bealigned on a word (4-byte) boundary, and sometimes an 8-byte double has to be aligned on a word(8-byte) boundary. Of course, this is all very implementation specific, and for most programmerscompletely implicit. You can write good C++ code for decades without needing to be explicitabout alignment.
Where alignment most often becomes visible is in object layouts: sometimesstructs contain ‘‘holes’’ to improve alignment (§8.2.1).The alignof() operator returns the alignment of its argument expression. For example:auto ac = alignof('c');auto ai = alignof(1);auto ad = alignof(2.0);// the alignment of a char// the alignment of an int// the alignment of a doubleint a[20];auto aa = alignof(a);// the alignment of an intSometimes, we have to use alignment in a declaration, where an expression, such as alignof(x+y) isnot allowed. Instead, we can use the type specifier alignas: alignas(T) means ‘‘align just like a T.’’For example, we can set aside uninitialized storage for some type X like this:void user(const vector<X>& vx){constexpr int bufmax = 1024;alignas(X) buffer[bufmax];// uninitializedconst int max = min(vx.size(),bufmax/sizeof(X));uninitialized_copy(vx.begin(),vx.begin()+max,buffer);// ...}6.3 DeclarationsBefore a name (identifier) can be used in a C++ program, it must be declared.