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

一、函数的声明和定义

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就会检测到。

五、逗号运算符

一个例子:

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

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

六、宏

宏的两个特殊的用法:

先看如下示例:

1 #define OUT(x) cout<<#x ” = ”<<endl  // 输出一个变量
2 #define DEFINT(x) int int_##x       // 定义一个int类型的变量
3  
4 DEFINT(1);   // 产生如下定义:int int_1;
5 int_1 = 1;
6 OUT(int_1);  // 输出:int_1 = 1

 

1. 字符串定义

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

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

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

 

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

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

2. 标志粘贴

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

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

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

 以后会关注的

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

 2. makefile 

原文地址:https://www.cnblogs.com/hdtianfu/p/2275081.html