您的位置:首页 > 编程语言 > C语言/C++

Thinking in C++ ----第三章 C++中的C

2011-12-03 23:20 239 查看

一、函数的声明和定义

1. 函数重载

在C语言中函数不能被重载。

2. 函数使用前的函数声明

在C语言中:

如果函数在使用之前没有进行声明,那么编译器会对其进行隐式声明:

假设这个函数的返回类型为int类型,但不对其参数做任何假设。

在C++中:

一个函数在使用之前必须进行声明。

3. 在函数声明中没有参数列表

在C语言中:

如果一个函数声明没有参数列表,如下:int func();

那么编译器不会对func的参数做任何假设,并且不会检查func的参数是否合法。这时,

func可以定义为:int func(int i){}

上述func函数的定义不会出错,因为编译器已经关闭了对其参数的检查。

在C++中:

如果一个函数声明没有参数列表,则表明这个函数没有参数,等价于:int func(void);

4. 函数定义中未命名的参数

在C语言中:

每一个参数都必须指定名字,若函数定义为如下形式:int func(int ){}

则,编译器就会报错

在C++中:

允许出现未命名的参数。当然,如果一个参数没有名字,那么也就无法使用它。

5. 忽略返回值类型

在C语言中:

如果一个函数声明或函数定义忽略返回值类型,如:fun(void);

那么这个函数的返回类型默认为是int类型。

在C++中:

上述情况不合法,一个函数声明或定义必须显示的指定它的返回类型。

6. 函数中的return

在C语言中:

编译器不会检查函数的返回类型,如:int func(){}

上述定义是不会出错的,但是它的返回值是没有意义的。

在C++中:

编译器会强制检查函数的返回值类型。

二、内部链接 和 外部链接

在一个程序中标识符代表存放变量或被编译过的函数体的存储空间。链接这些变量或函数的方式有两种:内部链接(internal linkage)和外部链接(external linkage)。

如果一个变量的链接方式是内部链接,那么意味着此变量只在定义它的文件中可见,对于这个变量的引用仅限于这个文件;在其他的文件中可以使用相同的名字定义标识符,这不会引起命名冲突。在c/c++中,内部链接是由关键字static指定的。

全局变量(C++中的const常量除外)和函数 默认为外部链接。在其他文件中可以使用extern关键字来访问具有外部链接的标识符。在全局变量定义时可以使用static关键字修饰,使它们成为内部链接方式;也可以使用extern关键字进行修饰,显示指定标识符为外部链接。

对于局部变量来说,它们不存在链接方式。因为局部变量只是临时存在于堆栈,链接器看不到它们。

C++中的const常量(不是C语言)是一个特例,它默认为内部链接的方式,若想使它变为外部链接,则必须使用extern关键字进行修饰。

三、const

C语言中的const

在C语言中对于表达式:const int i = 0;

它的含义是定义了一个变量i,并且变量i的值不能被修改。

你不能使用一个const变量定义一个数组,如下:

const int size = 3;

char arr[size]; // 错误,因为size是一个变量而不是常量。

在C语言中定义常量的方式有两种,

1. 使用宏,如:#define size 3

2. 使用enum,如:enum{size = 3};

C++中的const

在C++中,const有许多种用法,这里只讨论使用它定义常量的情况。

在C++中对于表达式:const int size = 0;

它的含义是定义了一个常量size,它可以被这样使用:

char arr[size];

对于表达式:const int size = str.size();

此时,这个size就不是一个常量了,因为它的值不能再编译期间确定。

而const在这里的含义只是表示:size的值不能被改变。

如果你这样使用size:

char arr[size]; // 错误,此时size不是常量

所以说在C++中使用const修饰的标识符可能是常量也可能是变量。

使用const常量替换宏常量

宏常量只是简单的进行值替换,它无法进行类型检查,这可能会导致一些问题;而使用const常量则可以避免这些问题。

要在多个文件中使用const常量,可以把const常量的定义放在头文件中。要使用这个常量,只需包含头文件即可。因为const常量的链接方式是内部链接,所以这不会造成命名冲突。

const常量的存储空间

先给出这样一个结论:如果在程序中不需要const常量的地址,那么编译器不会为const常量分配存储空间。

如果程序中只使用了const常量的值,而没有使用它的地址的话,那么这个const常量会被保存在符号表中,不用在堆或栈中为其分配存储空间。

为const常量分配存储空间的情况:

1. 示例代码如下:

const int i = 10; // 定义了一个整型的常量

void *addr = (void *)&i; // 取i的地址

分析:第一行定义了一个编译期的常量i,第二行需要i的地址,此时编译器被迫为i分配了内存空间,因为只有这样,i才会具有一个地址。

2. 示例代码如下:

extern const int i = 10; // 使用extern修饰i,使之成为外部链接的方式

分析:此时i成为了外部链接的方式,这意味着其他的编译单元也可以对它进行引用,因此编译器必须为它分配空间。

四、volatile关键字

volatile关键字告诉编译器,这个变量是“易变的”,不要对它做任何优化。这样做的好处是,一旦某个变量发生变化,程序会立刻知道它。例如在多线程 程序中A线程根据某个变量的值而进行某些操作,将这个变量定义为volatile的,这样这个变量一旦被修改,线程A就会检测到。

五、逗号运算符

一个例子:

int a = 0, b = 1,c = 2, d = 3;
2 a = (b++, c++, d++); // 相当于 (a = b++), c++, d++, e++;

也就是说,逗号运算符返回的是最后一个表达式的值。

六、宏

宏的两个特殊的用法:

先看如下示例:

#define OUT(x) cout<<#x ” = ”<<endl // 输出一个变量
#define DEFINT(x) int int_##x // 定义一个int类型的变量

DEFINT(1); // 产生如下定义:int int_1;
int_1 = 1;
OUT(int_1); // 输出:int_1 = 1

1. 字符串定义

如上例所示,使用#x可以产生一个字符串,

如果传递进来的x是abc,那么将产生”abc”,

如果传递进来的x是123,那么将产生”123”

这个特性在某些情况下非常有用,比如可以利用上例中的OUT宏来跟踪一个变量的值。

int a = 1;
int b = 2;
OUT(a); // 输出:a = 1
OUT(b); // 输出:b = 2

2. 标志粘贴

如上例所示,使用 ## 可以将其两边的符号连接起来

比如:a ## b,将会产生标识符:ab,##会忽略第一次遇到的空格

在某些情况你可能想要创建许多以某种前缀开头的变量,那么就可以如上例中的DEFINT一样定义自己的宏。

以后会关注的

1. C/C++中调用其他语言,如汇编,Python

2. makefile
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: