18.7. Inherently Nonportable Features18.7. 固有的不可移植的特征One of the hallmarks of the C programming language is the ability to write low-level programs that can be readily moved from one machine to another. The process of moving a program to a new machine is referred to as "porting," so C programs are said to be portable. 编写可以容易从一个机器移到其他机器的低级程序是 C 程序设计语言的一个特点。将程序移到新机器的过程称为“移植”,所以说 C 程序是可移植的。 To support low-level programming, C defines some features that are inherently nonportable. The fact that the size of the arithmetic types vary across machines (Section 2.1, p. 34) is one such nonportable feature that we have already encountered. In this section we'll cover two additional nonportable features that C++ inherits from C: bit-fields and the volatile qualifier. These features make it easier to interface directly to hardware. 为了支持低级编程,C 语言定义了一些固有不可移植的特征。算术类型的大小随机器不同而变化的事实(第 2.1 节),就是我们已经遇到过的一个这样的不可移植特征。本节将讨论 C++ 的另外两个从 C 语言继承来的不可移植特征:位域和 volatile 限定符。这些特征可使与硬件接口的直接通信更容易。 C++ adds another nonportable feature to those that it inherits from C: linkage directives, which make it possible to link to programs written in other languages. C++ 还增加了另一个不可移植特征(从 C 语言继承来的):链接指示,它使得可以链接到用其他语言编写的程序。 18.7.1. Bit-fields18.7.1. 位域A special class data member, referred to as a bit-field, can be declared to hold a specified number of bits. Bit-fields are normally used when a program needs to pass binary data to another program or hardware device. 可以声明一种特殊的类数据成员,称为位域,来保存特定的位数。当程序需要将二进制数据传递给另一程序或硬件设备的时候,通常使用位域。
A bit-field must be an integral data type. It can be either signed or unsigned. We indicate that a member is a bit-field by following the member name with a colon and a constant expression specifying the number of bits: 位域必须是整型数据类型,可以是 signed 或 unsigned。通过在成员名后面接一个冒号以及指定位数的常量表达式,指出成员是一个位域: typedef unsigned int Bit; class File { Bit mode: 2; Bit modified: 1; Bit prot_owner: 3; Bit prot_group: 3; Bit prot_world: 3; // ... }; The mode bit-field has two bits, modified only one, and the other members each have three bits. Bit-fields defined in consecutive order within the class body are, if possible, packed within adjacent bits of the same integer, thereby providing for storage compaction. For example, in the preceding declaration, the five bit-fields will be stored in the single unsigned int first associated with the bit-field mode. Whether and how the bits are packed into the integer is machine-dependent. mode 位域有两个位,modified 只有一位,其他每个成员有三个位。(如果可能)将类定义体中按相邻次序定义的位域压缩在同一整数的相邻位,从而提供存储压缩。例如,在前面的声明中,5 个位域将存储在一个首先与位域 mode 关联的 unsigned int 中。位是否压缩到整数以及如何压缩与机器有关。
Using Bit-fields使用位域A bit-field is accessed in much the same manner as the other data members of a class. For example, a bit-field that is a private member of its class can be accessed only from within the definitions of the member functions and friends of its class: 用与类的其他数据成员相同的方式访问位域。例如,作为类的 private 成员的位域只能从成员函数的定义和类的友元访问:
void File::write()
{
modified = 1;
// ...
}
void File::close()
{
if (modified)
// ... save contents
}
Bit-fields with more than one bit are usually manipulated using the built-in bitwise operators (Section 5.3, p. 154): 通常使用内置按位操作符(第 5.3 节)操纵超过一位的位域: enum { READ = 01, WRITE = 02 }; // File modes int main() { File myFile; myFile.mode |= READ; // set the READ bit if (myFile.mode & READ) // if the READ bit is on cout << "myFile.mode READ is set\n"; } Classes that define bit-field members also usually define a set of inline member functions to test and set the value of the bit-field. For example, the class File might define the members isRead and isWrite: 定义了位域成员的类通常也定义一组内联成员函数来测试和设置位域的值。例如,File 类可以定义成员 isRead 和 isWrite: inline int File::isRead() { return mode & READ; } inline int File::isWrite() { return mode & WRITE; } if (myFile.isRead()) /* ... */ With these member functions, the bit-fields can now be declared as private members of class File. 有了这些成员函数,现在就可以将位域声明为 File 类的私有成员了。 The address-of operator (&) cannot be applied to a bit-field, so there can be no pointers referring to class bit-fields. Nor can a bit-field be a static member of its class. 地址操作符(&)不能应用于位域,所以不可能有引用类位域的指针,位域也不能是类的静态成员。 18.7.2. volatile Qualifier18.7.2. volatile 限定符
Programs that deal directly with hardware often have data elements whose value is controlled by processes outside the direct control of the program itself. For example, a program might contain a variable updated by the system clock. An object should be declared volatile when its value might be changed in ways outside either the control or detection of the compiler. The volatile keyword is a directive to the compiler that it should not perform optimizations on such objects. 直接处理硬件的程序常具有这样的数据成员,它们的值由程序本身直接控制之外的过程所控制。例如,程序可以包含由系统时钟更新的变量。当可以用编译器的控制或检测之外的方式改变对象值的时候,应该将对象声明为 volatile。关键字 volatile 是给编译器的指示,指出对这样的对象不应该执行优化。 The volatile qualifier is used in much the same way as is the const qualifier. It is an additional modifier to a type: 用与 const 限定符相同的方式使用 volatile 限定符。volatile 限定符是一个对类型的附加修饰符: volatile int display_register; volatile Task *curr_task; volatile int ixa[max_size]; volatile Screen bitmap_buf; display_register is a volatile object of type int. curr_task is a pointer to a volatile Task object. ixa is a volatile array of integers. Each element of the array is considered to be volatile. bitmap_buf is a volatile Screen object. Each of its data members is considered to be volatile. display_register 是 int 类型的 volatile 对象;curr_task 是 volatile 对象的指针;ixa 是整数的 volatile 数组,该数组的每个元素都认为是 volatile 的;bitmap_buf 是 volatile Screen 对象,它的每个成员都认为是 volatile 的。 In the same way that a class may define const member functions, it can also define member functions as volatile. Only volatile member functions may be called on volatile objects. 用与定义 const 成员函数相同的方式,类也可以将成员函数定义为 volatile,volatile 对象只能调用 volatile 成员函数。 Section 4.2.5 (p. 126) described the interactions between the const qualifier and pointers. The same interactions exist between the volatile qualifier and pointers. We can declare pointers that are volatile, pointers to volatile objects, and pointers that are volatile that point to volatile objects: 第 4.2.5 节介绍了 const 限定符与指针的相互作用,volatile 限定符与指针之间也存在同样的相互作用。可以声明 volatile 指针、指向 volatile 对象的指针,以及指向 volatile 对象的 volatile 指针: volatile int v; // v is a volatile int int *volatile vip; // vip is a volatile pointer to int volatile int *ivp; // ivp is a pointer to volatile int // vivp is a volatile pointer to volatile int volatile int *volatile vivp; int *ip = &v; // error: must use pointer to volatile *ivp = &v; // ok: ivp is pointer to volatile vivp = &v; // ok: vivp is volatile pointer to volatile As with const, we may assign the address of a volatile object (or copy a pointer to a volatile type) only to a pointer to volatile. We may use a volatile object to initialize a reference only if the reference is volatile. 像用 const 一样,只能将 volatile 对象的地址赋给指向 volatile 的指针,或者将指向 volatile 类型的指针复制给指向 volatile 的指针。只有当引用为 volatile 时,我们才可以使用 volatile 对象对引用进行初始化。 Synthesized Copy Control Does Not Apply to Volatile Objects合成的复制控制不适用于 volatile 对象One important difference between the treatment of const and volatile is that the synthesized copy and assignment operators cannot be used to initialize or assign from a volatile object. The synthesized copy-control members take parameters that are const references to the class type. However, a volatile object cannot be passed to a plain or const reference. 对待 const 和 volatile 的一个重要区别是,不能使用合成的复制和赋值操作符从 volatile 对象进行初始化或赋值。合成的复制控制成员接受 const 形参,这些形参是对类类型的 const 引用,但是,不能将 volatile 对象传递给普通引用或 const 引用。 If a class wants to allow volatile objects to be copied or to allow assignment from or to a volatile operand, it must define its own versions of the copy constructor and/or assignment operator: 如果类希望允许复制 volatile 对象,或者,类希望允许从 volatile 操作数或对 volatile 操作数进行赋值,它必须定义自己的复制构造函数和/或赋值操作符版本: class Foo { public: Foo(const volatile Foo&); // copy from a volatile object // assign from a volatile object to a non volatile objet Foo& operator=(volatile const Foo&); // assign from a volatile object to a volatile object Foo& operator=(volatile const Foo&) volatile; // remainder of class Foo }; By defining the parameter to the copy-control members as a const volatile reference, we can copy or assign from any kind of Foo: a plain Foo, a const Foo, a volatile Foo, or a const volatile Foo. 通过将复制控制成员的形参定义为 const volatile 引用,我们可以从任何各类的 Foo 对象进行复制或赋值:普通 Foo 对象、const Foo 对象、volatile Foo 对象或 const volatile Foo 对象。
18.7.3. Linkage Directives: extern "C"18.7.3. 链接指示 extern "C"C++ programs sometimes need to call functions written in another programming language. Most often, that other language is C. Like any name, the name of a function written in another language must be declared. That declaration must specify the return type and parameter list. The compiler checks calls to external-language functions in the same way that it handles ordinary C++ functions. However, the compiler typically must generate different code to call functions written in other languages. C++ uses linkage directives to indicate the language used for any non-C++ function. C++ 程序有时需要调用用其他程序设计语言编写的函数,最常见的一语言是 C 语言。像任何名字一样,必须声明用其他语言编写的函数的名字,该声明必须指定返回类型和形参表。编译器按处理普通 C++ 函数一样的方式检查对外部语言函数的调用,但是,编译器一般必须产生不同的代码来调用用其他语言编写的函数。C++ 使用链接指示指出任意非 C++ 函数所用的语言。 Declaring a Non-C++ Function声明非 C++ 函数A linkage directive can have one of two forms: single or compound. Linkage directives may not appear inside a class or function definition. The linkage directive must appear on the first declaration of a function. 链接指示有两种形式:单个的或复合的。链接指示不能出现在类定义或函数定义的内部,它必须出现在函数的第一次声明上。 As an example, let's look at some of the C functions declared in the cstdlib header. Declarations in that header might look something like 作为例子,看看头文件 cstdlib 中声明的一些 C 函数。该头文件中声明形如: // illustrative linkage directives that might appear in the C++ header <cstring> // single statement linkage directive extern "C" size_t strlen(const char *); // compound statement linkage directive extern "C" { int strcmp(const char*, const char*); char *strcat(char*, const char*); } The first form consists of the extern keyword followed by a string literal, followed by an "ordinary" function declaration. The string literal indicates the language in which the function is written. 第一种形式由关键字 extern 后接字符串字面值,再接“普通”函数声明构成。字符串字面值指出编写函数所用的语言。 We can give the same linkage to several functions at once by enclosing their declarations inside curly braces following the linkage directive. These braces serve to group the declarations to which the linkage directive applies. The braces are otherwise ignored, and the names of functions declared within the braces are visible as if the functions were declared outside the braces. 通过将几个函数的声明放在跟在链接指示之后的花括号内部,可以给它们设定相同的链接。花括号的作用是将应用链接指示的声明聚合起来,忽略了花括号,花括号中声明的函数名就是可见的,就像在花括号之外声明函数一样。 Linkage Directives and Header Files链接指示与头文件The multiple-declaration form can be applied to an entire header file. For example, the C++ cstring header might look like 可以将多重声明形式应用于整个头文件。例如,C++ 的 cstring 头文件可以像这样: // compound statement linkage directive extern "C" { #include <string.h> // C functions that manipulate C-style strings } When a #include directive is enclosed in the braces of a compound linkage directive, all ordinary function declarations in the header file are assumed to be functions written in the language of the linkage directive. Linkage directives can be nested, so if the header contained a function with a linkage directive the linkage of that function is unaffected. 当将 #include 指示在复合链接指示的花括号中的时候,假定头文件中的所有普通函数声明都是用链接指示的语言编写的函数。链接指示可以嵌套,所以,如果头文件包含了带链接指示的函数,该函数的链接不受影响。
Exporting Our C++ Functions to Other Langauges导出 C++ 函数到其他语言By using the linkage directive on a function definition, we can make a C++ function available to a program written in another language: 通过对函数定义使用链接指示,使得用其他语言编写的程序可以使用 C++ 函数: // the calc function can be called from C programs extern "C" double calc(double dparm) { /* ... */ } When the compiler generates code for this function, it will generate code appropriate to the indicated language. 当编译器为该函数产生代码的时候,它将产生适合于指定语言的代码。
Languages Supported by Linkage Directives链接指示支持的语言A compiler is required to support linkage directives for C. A compiler may provide linkage specifications for other languages. For example, extern "Ada", extern "FORTRAN", and so on. 要求编译器支持对 C 语言的链接指示。编译器可以为其他语言提供链接说明。例如,extern "Ada"、extern "FORTRAN" 等。
Overloaded Functions and Linkage Directives重载函数与链接指示The interaction between linkage directives and function overloading depends on the target language. If the language supports overloaded functions, then it is likely that a compiler that implements linkage directives for that language would also support overloading of these functions from C++. 链接指示与函数重载之间的相互作用依赖于目标语言。如果语言支持重载函数,则为该语言实现链接指示的编译器很可能也支持 C++ 的这些函数的重载。 The only language guaranteed to be supported by C++ is C. The C language does not support function overloading, so it should not be a surprise that a linkage directive can be specified only for one C function in a set of overloaded functions. It is an error to declare more than one function with C linakage with a given name: C++ 保证支持的唯一语言是 C。C 语言不支持函数重载,所以,不应该对下面的情况感到惊讶:在一组重载函数中只能为一个 C 函数指定链接指示。用带给定名字的 C 链接声明多于一个函数是错误的: // error: two extern "C" functions in set of overloaded functions extern "C" void print(const char*); extern "C" void print(int); In C++ programs, it is fairly common to overload C functions. However, the other functions in the overload set must all be C++ functions: 在 C++ 程序中,重载 C 函数很常见,但是,重载集合中的其他函数必须都是 C++ 函数: class SmallInt { /* ... */ }; class BigNum { /* ... */ }; // the C function can be called from C and C++ programs // the C++ functions overload that function and are callable from C++ extern "C" double calc(double); extern SmallInt calc(const SmallInt&); extern BigNum calc(const BigNum&); The C version of calc can be called from C programs and from C++ programs. The additional functions are C++ functions with class parameters that can be called only from C++ programs. The order of the declarations is not significant. 可以从 C 程序和 C++ 程序调用 calc 的 C 版本。其余函数是带类型形参的 C++ 函数,只能从 C++ 程序调用。声明的次序不重要。 Pointers to extern "C" Functionsextern "C" 函数和指针The language in which a function is written is part of its type. To declare a pointer to a function written in another programming language, we must use a linkage directive: 编写函数所用的语言是函数类型的一部分。为了声明用其他程序设计语言编写的函数的指针,必须使用链接指示: // pf points to a C function returning void taking an int extern "C" void (*pf)(int); When pf is used to call a function, the function call is compiled assuming that the call is to a C function. 使用 pf 调用函数的时候,假定该调用是一个 C 函数调用而编译该函数。
When there is such a mismatch, a compile-time error message is issued: 存在这种不匹配的时候,会给出编译时错误: void (*pf1)(int); // points to a C++ function extern "C" void (*pf2)(int); // points to a C function pf1 = pf2; // error: pf1 and pf2 have different types
Linkage Directives Apply to the Entire Declaration应用于整个声明的链接指示When we use a linkage directive, it applies to the function and any function point-ers used as the return type or as a parameter type: 使用链接指示的时候,它应用于函数和任何函数指针,作为返回类型或形参类型使用: // f1 is a C function; its parameter is a pointer to a C function extern "C" void f1(void(*)(int)); This declaration says that f1 is a C function that doesn't return a value. It has one parameter, which is a pointer to a function that returns nothing and takes a single int parameter. The linkage directive applies to the function pointer as well as to f1. When we call f1, we must pass it the name of a C function or a pointer to a C function. 这个声明是说,f1 是一个不返回值的 C 函数,它有一个形参,该形参是不返回值并接受单个形参的函数的指针。链接指示应用于该函数指针以及 f1。调用的时候,必须将 C 函数名字或 C 函数指针传递给它。 Because a linkage directive applies to all the functions in a declaration, we must use a typedef to pass a pointer to a C function to a C++ function: 因为链接指示应用于一个声明中的所有函数,所以必须使用类型别名,以便将 C 函数的指针传递给 C++ 函数: // FC is a pointer to C function extern "C" typedef void FC(int); // f2 is a C++ function with a parameter that is a pointer to a C function void f2(FC *); |