1、register关键字的变化
- register关键字请求“编译器”将局部变量存储于寄存器中
- C语言中无法取得 register 变量地址
- 在C++中依然支持register关键字
- C++编译器有自己的优化方式,不使用register也可能做优化
- C++中可以取得 register 变量的地址
C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。
早期C语言编译器不会对代码进行优化,因此register变量是一个很好的补充。
举例:
exp-1:
#include <stdio.h>
int main()
{
register int i = 10;
int *p = &i;
printf("%p\t", p);
return 0;
}
当试图编译上述代码时,gcc给出了一个错误:
exp-2:
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
register int i = 10;
int *p = &i;
cout << p << '\t' << *p <<endl;
return 0;
}
对于上述代码,g++能顺利编译通过,而且能正确运行。
2、全局变量的定义
- 在C语言中,重复定义多个同名的全局变量是合法的
- 在C++中,不允许定义多个同名的全局变量
C语言中多个同名的全局变量最终会被链接到全局数据区的同一个地址空间上。
C++直接拒绝这种二义性的做法。
举例:
编写如下的文件:
test.h
#ifndef _TEST_H_
#define _TEST_H_
extern int num;
void print_addr_t();
#endif
test.c
#include "test.h"
#include <stdio.h>
int num = 100;
void print_addr_t() //访问该作用域内的全局变量num
{
printf("num addr = %p\tvalue = %d\n", &num, num);
}
main.c
#include <stdio.h>
#include "test.h"
int num;
void print_addr_m() //访问该作用域内的全局变量num
{
printf("num addr = %p\tvalue = %d\n", &num, num);
}
int main()
{
print_addr_t();
print_addr_m();
return 0;
}
可见,在上面的test.c 和 main.c 中同时定义了全局变量num,我暂时把它们分别称作A和B,那么有如下的几点需要注意:
- A、B 不一定非要分别定义在不同的文件中,它们可以同时在一个源文件中存在(VS下编译不过,GCC编译通过,估计是直接忽略了重复定义)
- 不管如何定义,不能同时给A、B赋值,否则编译器报错,因为它们无法被链接到全局数据区的同一块内存段上
- 虽然A、B分别定义在不同的文件中,但它们的地址一样,值一样
- 如果编译器编译上述代码报错,一个解决方法是加上static关键字限定它们各自的作用域,此时运行程序,它们的地址不一样,值也不一样
3、const 常量
C++编译器对const常量的处理:
- 当碰见常量声明时在符号表中放入常量;
- 编译过程中若发现使用常量则直接以符号表中的值替换;
- 编译过程中若发现对const使用了extern或者&操作符,则给对应的常量分配存储空间,否则,不分配存储空间;
- C++中,const常量可能会被分配存储空间:
- 当const常量为全局,并且需要在其它文件中使用时
- 当使用&操作符取const常量的地址时
注意:
C++编译器虽然可能为const常量分配空间,但不会使用其存储空间中的值。
图示:
C语言中的const常量:
- C语言中const常量是只读变量,有自己的存储空间;
- C语言中const常量的值是可改的,因此我有时也称它为“常变量”;
示例:
exp-3:
#include <stdio.h>
int main()
{
const int i = 5;
int *p = (int*)&i;
printf("%d\n", *p);
*p = 500;
printf("%d\n", i);
return 0;
}
编译并运行上述C程序时,编译器并不会报任何的警告或是错误,运行结果显示,确实改掉了“常量” i 的值;
exp-4:
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
const int i = 5;
cout << "i 的原始值:" << i << endl;
int *p = (int*)&i;
cout << "为 i 分配的地址为:" << p << endl;
cout << "地址空间中的值为:" << *p << endl;
*p = 500;
cout << "更改过后的值为:" << *p << endl;
cout << "i 的值为:" << i << endl;
return 0;
}
编译运行上述C++程序,结果为:
4、C++中的const小结
- C++中的const常量类似于宏定义
- const int c = 5; ≈ #define c 5
- C++中的const常量在与宏定义不同
- const常量是由编译器处理的,提供类型检查和作用域检查;
宏定义由预处理器处理,单纯的文本替换;
- const常量是由编译器处理的,提供类型检查和作用域检查;
5、struct类型的加强
- C语言的struct定义了一组变量的集合,C编译器并不认为这是一种新的类型;
- C++中的struct是一个新类型的定义声明,因此在C++中,可以用如下的方式使用结构体类型:
struct Student
{
const char *name;
int age;
};
Student stu1,stu2;
6、更为严格的类型检查
- C++中所有的变量和函数都必须有类型
- C语言中的默认类型在C++中是不合法的
例如:
#include <stdio.h>
f(i)
{
printf("i = %d\n", i);
}
g()
{
return 5;
}
int main()
{
const int i = 1;
f(i);
int j = g();
printf("j = %d\n", j);
j = g(i);
printf("j = %d\n", j);
return 0;
}
在gcc中编译上述代码时,编译器只产生了一些警告,而程序完全能正常运行:
运行结果:
而在g++中编译时,会产生错误!
从上述代码可看出,两个函数都标明没有返回值类型,这些都是C语言的默认类型;再者,g函数看似没有参数,但实际上并不是这样,我们可以给它传递任意多的参数。
int f(); 与 int f(void); 的区别是什么?
在C语言中
- int f();表示返回值为int,接受任意参数的函数
- int f(void);表示返回值为int的无参函数
在C++中
- int f();和int f(void)具有相同的意义,都表示返回值为int的无参函数
7、小结
C++以C语言为基础进行了加强
- C++更强调实用性,可以在任意的地方声明变量
- C++中的register只是一个向后兼容的作用,C++编译器能够进行更好的变量优化
- C++中的const是一个真正意义上的常量,而不是只读变量
- C++更加强调类型,任意的程序元素都必须显示指明类型