现在的位置: 首页 > 综合 > 正文

C Primer Plus 第五版

2013年09月09日 ⁄ 综合 ⁄ 共 5505字 ⁄ 字号 评论关闭

 int num;   //单词num是一个标识符,变量、函数、或其他实体所选的名字,这个声明把一个特殊的标识符和计算机内存中的一个特殊的位置联系起来,同时确定了该位置存储的信息类型。

C99标准允许一个标识符最多可以有63个字符。可使用的字符有大小写字母和下划线。操作系统和C库通常使用以一个或两个下划线开始的名字。而且名字区分大小写的。

 

C语言关键字列表:

auto break case char const continue default do double else enum extern float for goto if inline int long register restrict return short signed sizeof static struct switch typedef union unsigned void volatile while _Bool _Complex _Imaginary

----------------------------

foo.c:

/* 这里定义了一个inline的函数foo() */
inline foo() {
    ...;   <- 编译器会像非inline函数一样为foo()生成独立的汇编码
}

void func1() {
    foo(); <- 同文件内foo()可能被编译器内联展开编译而不是直接call上面生成的汇编码
}

而在另一个文件里调用foo()的时候,则直接call的是上面文件内生成的汇编码:

bar.c:

extern foo(); <- 声明foo(),注意不能在声明内带inline关键字

void func2() {
    foo();    <- 这里就是直接call在foo.c内为foo()函数生成的汇编码了
}
-------------
static
  常见的两种用途:
    1>统计函数被调用的次数;
    2>减少局部数组建立和赋值的开销.变量的建立和赋值是需要一定的处理器开销的,特别是数组等含有较多元素的
存储类型。在一些含有较多的变量并且被经常调用的函数中,可以将一些数组声明为static类型,以减少建立或者初始化这
些变量的开销. 
  详细说明:
    1>、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。
这一点是它与栈变量和堆变量的区别。
    2>、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
    3>当static用来修饰全局变量时,它就改变了全局变量的作用域,使其不能被别的程序extern,限制在了当前文件里,
但是没有改变其存放位置,还是在全局静态储存区。

  使用注意:
    1>若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
    2>若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
    3>设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,
需要考虑重入问题(只要输入数据相同就应产生相同的输出)。 
--------------------------
const
  被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。它可以修饰函数的参数、返回值,
甚至函数的定义体。 
  作用:
    1>修饰输入参数
      a.对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。
例如将void Func(A a) 改为void Func(const A &a)。
      b.对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。
否则既达不到提高效率的目的,又降低了函数的可理解性。
例如void Func(int x) 不应该改为void Func(const int &x)。
    2>用const修饰函数的返回值
      a.如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,
该返回值只能被赋给加const修饰的同类型指针。
       如对于: const char * GetString(void);
       如下语句将出现编译错误:
        char *str = GetString();//cannot convert from 'const char *' to 'char *';
       正确的用法是:
       const char *str = GetString();
      b.如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,
加const修饰没有任何价值。如不要把函数int GetInt(void) 写成const int GetInt(void)。
    3>const成员函数的声明中,const关键字只能放在函数声明的尾部,表示该类成员不修改对象.

   说明:
    const type m; //修饰m为不可改变
   示例:
    typedef char * pStr; //新的类型pStr;
    char string[4] = "abc";
    const char *p1 = string;
    p1++; //正确,上边修饰的是*p1,p1可变
    const pStr p2 = string;
    p2++; //错误,上边修饰的是p2,p2不可变,*p2可变
   同理,const修饰指针时用此原则判断就不会混淆了。
    const int *value; //*value不可变,value可变
    int* const value; //value不可变,*value可变
    const (int *) value; //(int *)是一种type,value不可变,*value可变
              //逻辑上这样理解,编译不能通过,需要tydef int* NewType;
    const int* const value;//*value,value都不可变
------------------
volatile
  表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,
而不是使用保存在寄存器里的备份。它可以适用于基础类型如:int,char,long......也适用于C的结构和C++的类。
当对结构或者类对象使用volatile修饰的时候,结构或者类的所有成员都会被视为volatile.
  该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,
而程序通过该变量同步各个线程。
  简单示例:
   DWORD __stdcall threadFunc(LPVOID signal)
   {
     int* intSignal=reinterdivt_cast(signal);
     *intSignal=2;
     while(*intSignal!=1)
     sleep(1000);
     return 0;
   }
  该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。
显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,
即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
     mov ax,signal
     label:
     if(ax!=1)
     goto label
  对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。
C 编译器是没有线程概念的,这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。
也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时候,
编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:
     label:
     mov ax,signal
     if(ax!=1)
     goto label
  注意:一个参数既可以是const同时是volatile,是volatile因为它可能被意想不到地改变。
它是const因为程序不应该试图去修改它。 
------------------------
联       合(union)  
    1. 联合说明和联合变量定义 
    联合也是一种新的数据类型, 它是一种特殊形式的变量。 
    联合说明和联合变量定义与结构十分相似。其形式为: 
     union 联合名{ 
          数据类型 成员名; 
          数据类型 成员名; 
          ... 
     } 联合变量名; 
    联合表示几个变量公用一个内存位置, 在不同的时间保存不同的数据类型 和不同长度的变量。 
    下例表示说明一个联合a_bc: 
     union a_bc{ 
          int i; 
          char mm; 
     }; 
    再用已说明的联合可定义联合变量。 
    例如用上面说明的联合定义一个名为lgc的联合变量, 可写成: 
      union a_bc lgc; 
    在联合变量lgc中, 整型量i和字符mm公用同一内存位置。 
    当一个联合被说明时, 编译程序自动地产生一个变量, 
其长度为联合中最大的变量长度。  
    联合访问其成员的方法与结构相同。同样联合变量也可以定义成数组或指针,
但定义为指针时, 也要用"->;"符号, 此时联合访问成员可表示成:  
     联合名->成员名; 
    另外, 联合既可以出现在结构内, 它的成员也可以是结构。 
    例如: 
     struct{ 
          int age; 
          char *addr; 
          union{ 
               int i; 
               char *ch; 
          }x; 
     }y[10]; 
    若要访问结构变量y[1]中联合x的成员i, 可以写成: 
      y[1].x.i; 
    若要访问结构变量y[2]中联合x的字符串指针ch的第一个字符可写成: 
      *y[2].x.ch; 
    若写成"y[2].x.*ch;"是错误的。 

    2. 结构和联合的区别 
    结构和联合有下列区别: 
    1. 结构和联合都是由多个不同的数据类型成员组成,
但在任何同一时刻, 联合转只存放了一个被选中的成员, 
而结构的所有成员都存在。  
    2. 对于联合的不同成员赋值, 将会对其它成员重写,  
原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。  
    下面举一个例了来加对深联合的理解。 
    例4: 
main() 
 { 
    union{                   /*定义一个联合*/ 
    int i; 
    struct{             /*在联合中定义一个结构*/ 
              char first; 
              char second; 
             }half; 
    }number; 

    number.i=0x4241;         /*联合成员赋值*/ 
    printf("%c%c/n", number.half.first, mumber.half.second); 
    number.half.first='a';   /*联合中结构成员赋值*/ 
    number.half.second='b'; 
    printf("%x/n", number.i); 
    getch(); 
 } 
    输出结果为: 
     AB 
     6261 
    从上例结果可以看出: 当给i赋值后, 其低八位也就是first和second的值;
当给first和second赋字符后, 这两个字符的ASCII码也将作为i 的低八位和高八位。
---------------------------
restrict是c99引入的,它只可以用于限定指针,并表明指针是访问一个数据对象的唯一且初始的方式,考虑下面的例子:
int ar[10];
int * restrict restar=(int *)malloc(10*sizeof(int));
int *par=ar;

这里说明restar是访问由malloc()分配的内存的唯一且初始的方式。par就不是了。
那么:
for(n=0;n<10;n++)
{
   par[n]+=5;
   restar[n]+=5;
   ar[n]*=2;
   par[n]+=3;
   restar[n]+=3;
}
因为restar是访问分配的内存的唯一且初始的方式,那么编译器可以将上述对restar的操作进行优化:
   restar[n]+=8;

而par并不是访问数组ar的唯一方式,因此并不能进行下面的优化:
   par[n]+=8;
因为在par[n]+=3前,ar[n]*=2进行了改变。使用了关键字restric,编译器就可以放心地进行优化了。
这个关键字据说来源于古老的FORTRAN。有兴趣的看看这个
-----------------------------

 

抱歉!评论已关闭.