(1)编译单元(模块)
  在VC或VS上编写完代码,点击编译按钮准备生成exe文件时,编译器做了两步工作:
  第一步,将每个.cpp(.c)和相应的.h文件编译成obj文件;
  第二步,将工程中所有的obj文件进行LINK,生成终.exe文件。
  那么,错误可能在两个地方产生:
  一个,编译时的错误,这个主要是语法错误;
  一个,链接时的错误,主要是重复定义变量等。
  编译单元指在编译阶段生成的每个obj文件。
  一个obj文件是一个编译单元。
  一个.cpp(.c)和它相应的.h文件共同组成了一个编译单元。
  一个工程由很多编译单元组成,每个obj文件里包含了变量存储的相对地址等。
  (2)声明与定义
  函数或变量在声明时,并没有给它实际的物理内存空间,它有时候可保证你的程序编译通过;
  函数或变量在定义时,它在内存中有了实际的物理空间。
  如果你在编译单元中引用的外部变量没有在整个工程中任何一个地方定义的话,那么即使它在编译时可以通过,在连接时也会报错,因为程序在内存中找不到这个变量。
  函数或变量可以声明多次,但定义只能有一次。
  (3) extern作用
  作用一:当它与"C"一起连用时,如extern "C" void fun(int a, int b);,则编译器在编译fun这个函数名时按C的规则去翻译相应的函数名而不是C++的。
  作用二:当它不与"C"在一起修饰变量或函数时,如在头文件中,extern int g_nNum;,它的作用是声明函数或变量的作用范围的关键字,其声明的函数和变量可以在本编译单元或其他编译单元中使用。
  即B编译单元要引用A编译单元中定义的全局变量或函数时,B编译单元只要包含A编译单元的头文件即可,在编译阶段,B编译单元虽然找不到该函数或变量,但它不会报错,它会在链接时从A编译单元生成的目标代码中找到此函数。
  (4)全局变量(extern)
  有两个类都需要使用共同的变量,我们将这些变量定义为全局变量。比如,res.h和res.cpp分别来声明和定义全局变量,类ProducerThread和ConsumerThread来使用全局变量。(以下是QT工程代码)
  /**********res.h声明全局变量************/
  #pragma once
  #include <QSemaphore>
  const int g_nDataSize = 1000; // 生产者生产的总数据量
  const int g_nBufferSize = 500; // 环形缓冲区的大小
  extern char g_szBuffer[]; // 环形缓冲区
  extern QSemaphore g_qsemFreeBytes; // 控制环形缓冲区的空闲区(指生产者还没填充数据的区域,或者消费者已经读取过的区域)
  extern QSemaphore g_qsemUsedBytes; // 控制环形缓冲区中的使用区(指生产者已填充数据,但消费者没有读取的区域)
  /**************************/
  上述代码中g_nDataSize、g_nBufferSize为全局常量,其他为全局变量。
  /**********res.cpp定义全局变量************/
  #pragma once
  #include "res.h"
  // 定义全局变量
  char g_szBuffer[g_nBufferSize];
  QSemaphore g_qsemFreeBytes(g_nBufferSize);
  QSemaphore g_qsemUsedBytes;
  /**************************/