c/c++常见关键字

来源:互联网 发布:win10好软件 编辑:程序博客网 时间:2024/05/17 08:46

static

  • 函数体内的static变量的作用范围为该函数体,变量保存在全局变量区,而不是保存在栈中;不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值
  • 在文件内定义的static全局变量可以被该文件内所有函数访问,但不能被文件外的函数访问
  • 在文件内定义的static函数只可被该文件内的其它函数调用,这个函数的使用范围被限制在声明它的文件中
  • 在类中定义的static成员变量属于整个类所拥有。对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新。
  • 在类中定义的static成员函数属于整个类所拥有,这个函数不接受this指针。我们知道,一般而言,类中的成员函数具有一个附加的隐含实参,即指向该类对象的一个指针。这个隐含实参命名为this。因为static成员函数不是任何对象的组成部分,所以static成员函数不存在this形参。static成员函数可以直接访问所属类的static成员,但是不能直接使用非static成员函数!也不能访问static const 类型的成员!

extern

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”

1.extern修饰变量的声明。举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明extern int v,还取决于变量v本身是能够被引用到的。这涉及到c语言变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了。

2.extern修饰函数声明。从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。

3.此外,extern修饰符可用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同(作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:void foo( int x, int y );该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字)。
一个详细介绍了extern “C”的博客:http://www.cnblogs.com/rollenholt/archive/2012/03/20/2409046.html

register

使用register关键字表示请求编译器尽可能(注意是尽可能,不是绝对)的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。寄存器其实就是一块一块小的存储空间,只不过其存取速度要比内存快得多。

CPU不直接和内存打交道,要访问内存时,数据先从内存里拿出来放到寄存器,然后CPU再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里。

使用register修饰符有几点限制。
①register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。

②因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。

③只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量)不行。在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束后释放寄存器。此后,在调用另外一个函数时又可以利用这些寄存器来存放该函数的寄存器变量。

④局部静态变量不能定义为寄存器变量。不能写成:register static int a, b, c;

⑤由于寄存器的数量有限,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。

在某些情况下,把变量保存在寄存器中反而会降低程序的运行速度。因为被占用的寄存器不能再用于其它目的。
早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充。然而,随着编译程序设计技术的进步,在决定那些变量应该被存到寄存器中时,现在的C编译环境能比程序员做出更好的决定。实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。

auto

这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。这个关键字不怎么多写,因为所有的变量默认就是auto的。

volatile

volatile是一个类型修饰符,一个变量添加了volatile修饰符意味着每次在用到这个变量时,编译器必须重新从内存中直接读取这个变量的值,而不是使用保存在寄存器里的备份,该变量不会被优化器优化。添加了volatile的变量表明该变量可能会被意想不到地改变,例如一个被系统时钟更新的变量,那么该变量应该声明为volatile。
一个详细介绍了volatile的博客:http://blog.csdn.net/bigloomy/article/details/6645810

const

在一个符号前面加上const限定符只是表示这个符号不能被赋值,意味着它的值对于这个符号来说是只读的,但它并不能防止通过程序的其它方法来修改这个值。const最有用的地方是用它来限定函数的形参,这样该函数就无法修改实参指针指向的数据。如

const int limit = 10;const int *limitp = &limit;int i = 27;limitp = &i;

无法通过limitp这个指针修改它指向的数据的值,但这个指针的值却可以改变。
默认情况下,const对象仅在文件内有效,除非在定义和声明时都添加extern说明符
const常量:

const int bufsize = 512

const引用:

int i = 42;const int j = 35;const int &r1 = i;const int &r2 = j;const int &r3 = 24;const int &r4 = r1*2;

const指针:

const double pi = 3.14;const double* p = π/*指向常量的指针,指向对象的值不能变,但指针的值可以变*/double abc = 5.1;double* const ptr = &abc;/*常量指针,ptr将一直指向abc变量,可以利用该指针改变abc的值。该常量指针不能用来指向const double类型变量,只能指向double类型变量*/const double *const cptr = π/*指向常量对象的常量指针*/

要想弄清楚这些声明的含义最有效的方法是从右向左阅读。如离ptr最近的符号是const,表明ptr是一个常量对象。

typedef

typedef为一种类型引入了新的名字。比较有用的是,typedef用来简化一些复杂的声明。比如signal函数:

void (*signal(int sig,void(*func)(int)))(int);

signal函数返回一个函数指针,该指针所指向的函数接受一个int参数并返回void。要想简单明了,只要在传统的变量声明表达式里用自己定义的类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。
上面代码等效于下面代码:

typedef void(*ptr_to_func)(int);/*原本void(*ptr_to_func)(int)表示定义了一个函数指针ptr_to_func,该指针指向的函数接受一个int参数,返回值为void,现在加上typedef后表示给这类型的指针取了一个别名ptr_to_func*/ptr_to_func signal(int,ptr_to_func);/*表明signal是一个函数,返回值为ptr_to_func*/

typedef与宏定义的区别体现在两个方面

  • 可以用其它类型说明符对宏类型进行扩展,但不能对typedef所定义的类型名进行扩展
#define peach intunsigned peach i;/*没问题*/
typedef int peach;unsigned peach i;/*错误,有问题*/
  • 在连续的几个变量声明中,用typedef的类型能保证声明的所有变量为同一类型,但宏定义无法保证。
#define int_ptr int *int_ptr a,b;/*a是指针,b是整型*/
typedef int * int_ptr;int_ptr a,b;/*a,b都是指针*/

不要为了方便对结构使用typedef。这样做的唯一好处是不必书写“struct”,但这个关键字能提高程序的可读性。typedef应该用于:

  • 数组、结构、指针以及函数的组合类型
  • 可移植类型(如typedef int int32_t;)

sizeof

sizeof是运算符,可用于任何变量名、类型名或常量值,当用于变量名(不是数组名)或常量时,它不需要用圆括号。它在编译时起作用,而不是运行时。sizeof的结果等于对象或者类型所占的内存字节数。使用时需要考虑字节对齐。
字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2)结构体每个成员相对于结构体首地址的偏移量(offset)都是该成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailingpadding)。

struct S1{    char c;    int i;};struct S2{    char c1;    S1 s;    char c2;};

S2的最宽简单类型为int;这时s是一个整体,它作为结构体变量也满足前面三个准则,所以其大小为8,偏移量为4,c1与s之间便需要3个填充字节;最后得到 sizeof(S2)的值为16。

new与delete

int main(){    int *p1=new int;    int *p2=new int(2);//*p2的初始值为2    int *p3=new int[10];//申请了10int的空间    delete p1;    delete p2;    delete[] p3;//不能用delete p3,要用delete[]才可以    return 0;}
0 0
原创粉丝点击