SiYu

积少成多 聚沙成塔

欢迎来到我的个人站~


C++Review

  进程调度是确保进程能有效工作的一个内核子系统

extern

  可以声明变量a是一个整数,这符合上面的逻辑。但这就产生了一个矛盾:这段代码有足够的信息让编译器为整数a分配空间,而且编译器也确实给整数a分配了空间。要解决这个矛盾,对于C/C++需要一个关键字来说明“这只是一个声明,它的定义在别的地方”。这个关键字就是extern,它表示变量是在文件以外定义的,或在文件后面部分才定义。   在变量定义前加extern关键字表示声明一个变量但不定义它,例如:

extern int a;   extern也可用于函数声明。例如: extern int func1(int length, int width);   这种声明方式和先前的func1()声明方式一样。因为没有函数体,编译器必定把它作为声明而不是函数定义。extern关键字对函数来说是多余的、可选的。C语言的设计者并不要求函数声明使用extern,这可能有些令人遗憾;如果函数声明也要求使用extern,那么在形式上与变量声明更加一致,从而减少了混乱(但这就需要更多的输入,这也许能解释为什么不要求函数使用extern的原因)。

  当程序达到一定规模之后,会遇到的一个问题是我们“用完了”函数名和标识符。当然,并非我们真正用完了所有函数名和标识符,而是简单地想出一个新名称就不太容易了。更重要的是,当程序达到一定的规模之后,通常分成许多块,每一块由不同的人或小组来构造和连接。由于所有的函数名和标识符都在同一程序里,这就要求所有的开发人员都必须非常小心,不要碰巧使用了相同的函数名和标识符,导致冲突。这种情况很快变得令人烦躁,而且浪费时间,最终导致高昂的代价。 标准的C++有预防这种冲突的机制:namespace关键字。库或程序中的每一个C++定义集被封装在一个名字空间中,如果其他的定义中有相同的名字,但它们在不同的名字空间,就不会产生冲突。

  可以用一个关键字来声明:“我要使用这个名字空间中的声明和(或)定义”。这个关键字是“using”。所有的标准C++库都封装在一个名字空间中,即“std”(代表“standard”)。由于本书常使用到这些标准的库文件,几乎在每个程序中都有如下的使用指令(using指令): using namespace std;

  这意味着打开std名字空间,使它的所有名字都可用。有了这条语句,就不用担心特殊的库组件是在一个名字空间中,因为在使用using指令地方,它使名字空间在整个文件中都是可用的。

指针的概念

  不管什么时候运行一个程序,都是首先把它装入(一般从磁盘装入)计算机内存。因此,程序中的所有元素都驻留在内存的某处。内存一般被布置成一系列连续的内存位置;我们通常把这些位置看做是8位字节,但实际上每一个空间的大小取决于具体机器的结构,一般称为机器的字长(word size)。每一个空间可按它的地址与其他空间区分。为了便于讨论,我们认为所有机器都使用有连续地址的字节从零开始,一直到该计算机的内存的上限。

  因为程序运行时驻留内存中,所以程序中的每一个元素都有地址。假设我们从一个简单的程序开始

  程序运行的时候,程序中的每一个元素在内存中都占有一个位置。甚至函数也占用内存。我们将会看到,定义什么样的元素和定义元素的方式通常决定元素在内存中放置的地方。

  C和C++中有一个运算符会告诉我们元素的地址。这就是‘&’运算符。只要在标识符前加上‘&’,就会得出标识符的地址。

  这个有趣的实验显示了怎样分配内存,那么利用地址能干什么呢?能做的最重要的事就是,把地址存放在别的变量中以便以后使用。C和C++有一个专门的存放地址的变量类型。这个变量叫做指针(pointer)

  定义指针的运算符和用于乘法的运算符‘*’是一样的。正如我们将看到的那样,编译器会根据它所在的上下文知道它表示的不是乘法。

  定义一个指针时,必须规定它指向的变量类型。可以先给出一个类型名,然后不是立即给出变量的标识符,而是在类型和标识符之间插入一个星号,这就是说“等一等,它是一个指针”。一个指向int的指针如下所示: int* ip; // ip points to an int variable

int a = 47; int* ipa = &a;   现在已经初始化了a和ipa, ipa存放a的地址。

  一旦有一个初始化了的指针,我们能做的最基本的事就是利用指针来修改它指向的值。要通过指针访问变量,可以使用以前定义指针使用的同样的运算符来间接引用这个指针,如像: *ipa = 100; 现在a的值是100而不是47。

  这些是指针基础:可以保存地址,可以使用地址去修改原先的变量。

  我们可以用引用传递参数地址。引用和指针的不同之处在于,带引用的函数调用比带指针的函数调用在语法构成上更清晰(在某种情况下,使用引用实质上的确只是语法构成上不同)。

//: C03:PassReference.cpp
#include <iostream>
using namespace std;

void f(int& r) {
  cout << "r = " << r << endl;
  cout << "&r = " << &r << endl;
  r = 5;
  cout << "r = " << r << endl;
}

int main() {
int x = 47;
  cout << "x = " << x << endl;
  cout << "&x = " << &x << endl;
  f(x); // Looks like pass-by-value,
        // is actually pass by reference
  cout << "x = " << x << endl;
} ///:~

  在函数f()的参数列表中,不用int*来传递指针,而是用int&来传递引用。在f()中,如果仅仅写‘r’(如果r是一个指针,会产生一个地址值)会得到r引用的变量值。如果对r赋值,实际上是给r引用的变量赋值。事实上,得到r中存放的地址值的惟一方法是用‘&’运算符。 在函数main()中,我们能看到引用在调用函数f()中的重要作用,其语法形式还是f(x)。尽管这看起来像是一般的按值传递,但是实际上引用的作用是传递地址,而不是值的一个拷贝。

  输出结果是: x = 47 &x = 0065FE00 r = 47 &r = 0065FE00 r = 5 x = 5 这里有和指针一起工作的另一种类型:void。如果声明指针是void,它意味着任何类型的地址都可以间接引用那个指针(而如果声明int,则只能对int型变量的地址间接引用那个指针)。

静态变量

  关键字static有一些独特的意义。通常,函数中定义的局部变量在函数作用域结束时消失。当再次调用这个函数时,会重新创建该变量的存储空间,其值会被重新初始化。如果想使局部变量的值在程序的整个生命期里仍然存在,我们可以定义函数的局部变量为static(静态的),并给它一个初始值。初始化只在函数第一次调用时执行,函数调用之间变量的值保持不变。用这种方式,函数可以“记住”函数调用之间的一些信息片断。

常量

  C++引入了命名常量的概念,命名常量就像变量一样,只是它的值不能改变。修饰符const告诉编译器这个名字表示常量。不管是内部的还是用户定义的数据类型都可以定义为const。 必须用下述方式说明一个常量类型: const int x = 10;

volatile变量

  限定词const告诉编译器“这是不会改变的”(这就允许编译器执行额外的优化);而限定词volatile则告诉编译器“不知道何时会改变”,防止编译器依据变量的稳定性作任何优化。

#####sizeof   sizeof给我们提供对有关数据项目所分配的内存大小。

typedef命名别名

  typedef表示“类型定义”,但用“别名”来描述可能更精确,因为这正是它真正的作用。它的语法是: typedef原类型名别名

typedef unsigned long ulong;现在如果写ulong,则编译器知道意思是unsigned long。

用struct把变量结合在一起

  struct(结构)是把一组变量组合成一个构造的一种方式。一旦创建了一个struct,就可以生成所建立的新类型变量的许多实例。例如:

struct Structure1 {
  char c;
  int i;
  float f;
  double d;
};

int main() {
  struct Structure1 s1, s2;
  s1.c = 'a'; // Select an element using a '.'
  s1.i = 1;
  s1.f = 3.14;
  s1.d = 0.00093;
  s2.c = 'a';
  s2.i = 1;
  s2.f = 3.14;
  s2.d = 0.00093;
} ///:~
指针和struct

  为了选择一个特定struct对象中的元素,应当使用‘.’。但是,如果有一个指向struct对象的指针,可以使用一个不同的运算符‘->’来选择对象中的元素。下面是一个例子:

typedef struct Structure3 {
  char c;
  int i;
  float f;
  double d;
} Structure3;

int main() {
  Structure3 s1, s2;
  Structure3* sp = &s1;
  sp->c = 'a';
  sp->i = 1;
  sp->f = 3.14;
  sp->d = 0.00093;
  sp = &s2; // Point to a different struct object
  sp->c = 'a';
  sp->i = 1;
  sp->f = 3.14;
  sp->d = 0.00093;
} ///:~

  在main()中,struct指针sp最初指向s1,用‘->’选择s1中的成员来初始化它们。随后sp指向s2,以同样的方式初始化那些变量。所以可以看到指针的另一个好处是可以动态地重定向它们,指向不同的对象,使编程更灵活。

用union节省内存

  union把所有的数据放进一个单独的空间内,它计算出放在union中的最大项所必需的空间数,并生成union的大小。使用union可以节省内存。

C语言assert()宏

  当使用assert()时,给它一个参数,即一个表示断言为真的表达式。预处理器产生测试该断言的代码。如果断言不为真,则在发出一个错误信息告诉断言是什么以及它失败之后,程序会终止。