C++ 语言基础
1. 局部变量和全局变量能否重名?
局部变量是定义在函数内部的变量。
全局变量是定义在函数之外的变量,可以被本文件中其他函数所共用。
局部变量和全局变量可以重名,在函数中默认使用的是局部变量(即会屏蔽全局变量)。要使用全局变量,要在变量名前添加 “::” 。
2. 全局变量可不可以被定义在可被多个 .c 文件包含的头文件中?为什么?
可以,因为可以在不同的 .c 文件中以 static 形式来声明同名的全局变量,但是在这些同名的全局变量中只能有一个 .c 文件对其进行赋值,这样才不会出错。
3. 如何引用一个已经定义过的全局变量?
- 用引用头文件的方式
- 用 extern 关键字引用
注意:使用头文件的方式来引用某个头文件中的全局变量,如果变量名书写错误,则在编译时提示错误;使用 extern 关键字引用时,如果变量名书写错误,则在链接时提示错误。
4. 全局变量和局部变量在内存中是怎样存放的?两者之间有何区别?
全局变量存储在静态数据库,局部变量存储在堆栈。
全局变量在程序开始执行时分配存储区,程序执行完毕释放,在程序执行过程中全局变量始终占据固定的存储单元;局部变量是动态分配存储空间的,在函数调用结束时自动释放这些存储空间。
5. -8 在内存中的存储形式?
整型数据在内存中是以二进制的形式存放的,数值是以补码表示的。
一个正数的补码和其原码的形式相同,一个负数的补码是将该数二进制形式符号位保持不变,其余按位取反再加 1 。
所以 -8 在内存中的存储形式是 11111111 11111000 。
6. 数值 029 是一个什么数?
非法数。
本题主要是对八进制、十六进制和十进制三种形式的考察。八进制整常数必须以 0 开头,数码取值为 0 ~ 7;十六进制整常数是以 0X 或 0x 开头的,数码取值为 0 ~ 9,A ~ F 或 a ~ f ;十进制整常数无前缀,数码为 0 ~ 9 。
这里的 029 是以 0 开头,却含有 9 ,所以不是八进制数,因此它是非法的。
7. 整数除法取值
请问 i 和 j 的值分别是什么?
int i, j;
i = 5 / 2;
j = -5 / 2;
i = 2,j = -2
对于整数除法取值来说,在 C/C++ 中是将结果的小数部分截掉,只保留整数部分。
8. 写出 float a 与“零值”比较的 if 语句
if ((a >= -0.00001) && (a <= 0.00001));
本题考察 float 型变量特性及对 0 值的判断。
一般来说,如果用 if 判断一个整型变量(short、int、long 等),应该用 if (a == 0) ,表明是与 0 进行“数值”上的比较;但是 float 型变量并不精确,不能直接拿来与 0.0 进行比较,所以不可使用 “==” 或 “!=” 这种形式,应该使用 “>=” 或 “<=” 这种形式。因为计算机在处理浮点数的时候是有误差的,所以判断两个浮点数是不是相同,是要判断是不是落在同一个区间的。
9. 如何将单精度数值保留小数点后两位,第三位四舍五入?
float a = 1.2345;
//将 (a * 100 + 0.5) 的值强制转换为整数值再进行计算
a = (int) (a * 100 + 0.5) / 100.0;
10. 整数自动转换原则
下面代码输出是什么?为什么?
unsigned int a = 6;
int b = -20;
(a + b > 6) ? puts("> 6") : puts("<= 6");
输出为 “> 6” ,因为当表达式中存在有符号类型和无符号类型时所有的操作数会自动转换为无符号类型。因此 -20 被转换为 0xffffffec (4294967276) (即取补码),所以该表达式计算出的结果大于 6 。
在 C/C++ 中无符号类型只能存放不带符号的整数,不能存放负数,当为其赋值为负数时,会自动转换为无符号类型数值,其取值范围是 0 ~ 2 ^ 32 - 1 。
11. 逗号表达式
看下面代码,输出结果是什么?
int a, b, c, d;
a = 3;
b = 5;
c = a, b;
d = (a, b);
printf("c = %d\n", c);
printf("d = %d\n", d);
c = 3
d = 5
在 C/C++ 中逗号有两个作用,一是用来分隔表达式,二是作为逗号运算符。
C++ 提供一种特殊的运算符——逗号运算符,用它将两个表达式连接起来。如:** 3 + 5, 6 + 8 称为逗号表达式,又称为“顺序求值运算符”。逗号表达式的一般形式为:表达式 1 , 表达式 2** 。
逗号表达式的求解过程是:先求解表达式 1 ,再求解表达式 2 。整个逗号表达式的值是表达式 2 的值。如,逗号表达式 a = 3 * 5 , a * 4 赋值运算符的优先级别高于逗号运算符, 因此应先求解 a = 3 * 5 (也就是把“a = 3 * 5”作为一个表达式)。经计算和赋值后得到 a 的值为 15 ,然后求解 a * 4 ,得 60 。整个逗号表达式的值为 60 。
12. printf() 函数和 cout 函数是从右向左计算输出表达式的值
13. static 关键字
C++ 的 static 有两种用法:面向过程程序设计中的 static 和面向对象程序设计中的 static 。前者应用于普通变量和函数,不涉及类;后者主要说 static 在类中的作用。
一、面向过程设计中的 static
1、静态全局变量
在全局变量前,加上关键字 static,该变量就被定义成为一个静态全局变量。静态全局变量有以下特点:
(1)该变量在全局数据区分配内存;
(2)未经初始化的静态全局变量会被程序自动初始化为 0(自动变量的值是随机的,除非它被显式初始化);
(3)静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的。(利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。)
2、静态局部变量
在局部变量前,加上关键字 static ,该变量就被定义成为一个静态局部变量。静态局部变量有以下特点:
(1)该变量在全局数据区分配内存;
(2)静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
(3)静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为 0 ;
(4)它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
3、静态函数
在函数的返回类型前加上 static 关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。定义静态函数的好处:
(1)静态函数不能被其它文件所用;
(2)其它文件中可以定义相同名字的函数,不会发生冲突;
二、面向对象的 static 关键字(类中的 static 关键字)
1、静态数据成员
在类内数据成员的声明前加上关键字 static ,该数据成员就是类内的静态数据成员。静态数据成员有以下特点:
(1)对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;
(2)静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。
(3)静态数据成员和普通数据成员一样遵从 public , protected , private 访问规则;
(4)因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;
(5)静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:
<数据类型><类名>::<静态数据成员名>=<值>
(6)类的静态数据成员有两种访问形式:
<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>
如果静态数据成员的访问权限允许的话(即 public 的成员),可在程序中,按上述格式来引用静态数据成员 ;
(7)静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了;
(8)同全局变量相比,使用静态数据成员有两个优势:静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;可以实现信息隐藏,静态数据成员可以是 private 成员,而全局变量不能。
2、静态成员函数
与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部对象服务而不是为类的某一个具体对象服务。静态成员函数有以下特点:
(1)普通的成员函数一般都隐含了一个 this 指针, this 指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。通常情况下, this 是缺省的。但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有 this 指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。
(2)出现在类体外的函数定义不能出现关键字 static ;
(3)静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
(4)非静态成员函数可以任意地访问静态成员函数和静态数据成员;
(5)由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;
(6)调用静态成员函数,可以用成员访问操作符( . )和( -> )为一个类的对象或指向类对象的指针调用静态成员函数,也可以直接使用如下格式:
<类名>::<静态成员函数名>(<参数表>)
调用类的静态成员函数。
14. const 关键字
const 意味着“只读”。const 在谁后面谁就不可修改,const 在最前面则将其后移一位即可,二者等效。主要优点是便于类型检查、同宏定义一样可以方便地进行参数修改和调整、节省空间、避免不必要的内存分配、可为函数重载提供参考。
const int a; //a 是一个常整型数
int const a; //a 是一个常整型数
const int *a; //a 是一个指向常整型数的指针,指向的整型数是不可修改的,但指针可以
int * const a; //a 是一个指向整型数的常指针,指针指向的整型数是可以修改的,但指针是不可修改的
int const * a const; //a 是一个指向常整型数的常指针,指针指向的整型数是不可修改的,同时指针也是不可修改的
const 的作用如下:
(1)欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
参考:
关键字const有什么含义?
c语言中const关键字详解
15. 请说出 const 与 #define 相比有何优点?
(1)const 修饰的只读变量具有特定的类型,而宏没有数据类型。编译
器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
(2)有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏
常量进行调试。
(3)编译器通常不为普通 const 只读变量分配存储空间,而是将它们保
存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。
参考:
关键字const(1)
16. sizeof 关键字
sizeof 实际上是关键字,使用类似于一元操作符,所以有时也会被当作一元操作符。它的作用是返回一个对象或类型名的长度,长度的单位是字节,它是在编译阶段求值的。sizeof 运算符永远不会产生 0,即使对于空类也是如此。
sizeof 有三种语法形式,如下:
- sizeof( object ); // sizeof( 对象 );
- sizeof( type_name ); // sizeof( 类型 );
- sizeof object; // sizeof 对象;
结论:不论sizeof要对谁取值,最好都加上()。
实际上, sizeof 计算对象的大小也是转换成对对象类型的计算,也就是说,同种类型的不同对象其 sizeof 值都是一致的。这里,对象可以进一步延伸至表达式,即 sizeof 可以对一个表达式求值,编译器根据表达式的最终结果类型来确定大小,一般不会对表达式进行计算。
各种对象或类型名的大小:(在 32 位系统中)
1. 基本数据类型
bool:1, char:1, short:2, float:4, int:4, long:4, double:8。
2. 指针
不管是什么类型的指针,大小都是 4 个字节的,因为指针就是 32 位的物理地址。
3. 数组
数组的大小是各维数的乘积 * 数组元素的大小。
这里有一个陷阱:
int *d = new int[10];
cout << sizeof(d) << endl; // 4
d 是我们常说的动态数组,但它实际上还是一个指针,所以 sizeof(d) = 4。
4. 函数
对函数使用 sizeof ,在编译阶段会被函数返回值的类型取代。
5. 自定义数据类型
自定义类型的 sizeof 取值等同于它的类型原形。
6. 联合体
union 的大小取决于它所有的成员中,占用空间最大的一个成员的大小。
复合数据类型,如 union , struct , class 的对齐方式为成员中对齐方式最大的成员的对齐方式。
6. 结构体
为了性能考虑,编译器会对结构体进行对齐。
struct
{
int b;//4
double a;//8
char c;//1
}A;
sizeof(A) = 24 ,内存布局如下:
|<<<b=4>>>|补齐4个字节|<<<<<<<a=8>>>>>>>>|<c=1>|<<补齐7个字节>>|
struct
{
double a;
int b;
char c;
}A;
sizeof(A) = 16,内存布局如下:
|<<<<<<<<<<<a=8>>>>>>>>>>|<<<<b=4>>>>|<c=1>|<<补齐3个字节>>|
7. 类
空类大小:1。
(1)类的大小为类的非静态成员数据的类型大小与虚指针(如果有的话)之和,也就是说静态成员数据和普通成员函数不作考虑。
(2)类的总大小也遵守类似结构体字节对齐的调整规则。
几种简单情况需要记住:空类为 1 ,单一继承的空类空间也为 1 ,多重继承的空类空间还是为 1 。但是虚继承的空类设计到虚表(虚指针),所以为 4 。
17. sizeof 和 strlen 的区别?
- sizeof 是一个操作符,而 strlen 是库函数。
- sizeof 的参数可以是数据的类型,也可以是变量,而 strlen 只能以结尾为 '\0' 的字符串作参数。
- 编译器在编译时就计算出了 sizeof 的结果,而 strlen 必须在运行时才能计算出来。
- sizeo f计算数据类型占内存的大小, strlen 计算字符串实际长度。
参考:
sizeof与strlen的区别
网友评论