static面试题
static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量只被初始化一次,下一次依据上一次结果值;
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
1、static全局变量与普通的全局变量有什么区别 ?
- 全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。 static全局变量只初使化一次,防止在其他文件单元中被引用。
2、static局部变量和普通局部变量有什么区别 ?
- 把局部变量加static后是改变了它的存储方式即改变了它的生存期。 由栈存储改变成数据区存储,生命周期延长。
3、static函数与普通函数有什么区别?
- static函数与普通函数最主要区别是static函数在内存中只有一份,普通静态函数在每个被调用中维持一份拷贝程序的局部变量存在于(栈)中。
4、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
- 可以,在不同的C文件中以static形式来声明同名全局变量。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值, 此时连接不会出错。
C++中static
1)、文件内的,匿名命名空间内,定义的变量,相当于文件静态变量。作用域为文件,生命周期是程序生命期。
//标头.h
#include<iostream>
namespace
{
static void Print()
{
std::cout << "what ";
}
}
//main.cpp
#include<iostream>
#include"标头.h"
int main()
{
Print();
system("pause");
return 0;
}
2)、修饰成员变量,修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。static修饰的变量先于对象存在,所以static修饰的变量要在类外初始化,因为static是所有对象共享的变量,必须要比对象先存在。
class A
{
public:
static int a;
};
int A::a = 10;
3)、因为static修饰的成员数函数不属于类,所以static修饰的成员函数可以不生成对象调用,没有this指针,static 函数内不能访问非静态成员。
#include<iostream>
using namespace std;
class A
{
public:
static void Print()
{
cout << "Print" << endl;
//cout << a << endl;
}
private:
int a;
};
int main()
{
A::Print();
system("pause");
return 0;
}
const
1、const修饰引用变量
const可以修饰const变量、非const变量,而非const变量不能修饰const变量,只能修饰非const变量。
int a=10; //非const变量
const int b=20; //const变量
const int& c=a; //const修饰非const变量
const int& d=b; //const修饰const变量
int& e=a; //非const修饰非const
错误引用
int& f=b; //非const修饰const
const变量说明变量只读,而非const变量来引用就改变了它的只读属性,违背了它只读原则,所以不允许
2、const修饰指针变量
int a=10;//非const data
const int * a4 = &a1; //const data,non-const pointer
int * const a5 = &a1; //non-const data,const pointer
int const * const a6 = &a1; //const data,const pointer
const int * const a7 = &a1; //const data,const pointer
const修饰指针变量时:
1)常变量:const在*
前,数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。
2)常指针:const在*
后,表示指针本身是常量, 不能指向其他内存地址;指针所指的数据可以通过解引用修改。
3)常指针变量两个const,*
前后都有const,表示指针和指针所指数据都不能修改
3、const修饰函数返回值
const修饰函数返回值相对于值传递没有意义,而对于指针传递来说
const int * mallocA(){ ///const data,non-const pointer
int *a=new int(2);
return a;
}
int main()
{
const int *a = mallocA();
///int *b = mallocA(); ///编译错误
return 0;
}
返回值const在*
之前,数据不能修改,不能使用不是常变量类型指针接受。
4、const修饰成员函数
(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)
(2)const修饰的成员函数不能调用非const成员函数,因为非const成员函数可能会修改const修饰的成员函数的成员变量
5、const在C语言和C++中的区别
在C语言中,当const 修饰一个标识符的时候我们说,这个标识符依然是一个变量,但是它具有常属性,不能被直接改变,也就是说a只能初始化一次。
错误
const int a = 10;
a=20;
But
#include<stdio.h>
int main()
{
const int a = 10;
int*p = &a;
*p = 20;
printf("%d\n", a);
system("pause");
return 0;
}
结果
20
请按任意键继续. . .
在C++中const修饰的标识符就是常量。
函数传参
1、传值----随函数栈帧
当调用该函数时候,该函数自己形成栈帧,形参数值是拷贝过来的新变量,在函数中对形参变量操作,自然不会改变实参的值。在该函数执行结束,该函数的栈帧销毁,拷贝数据(形参)自然也就丢失了。
2、传地址----可获得实参地址,但是不安全。
函数形成栈帧,形参就是实参地址,可以改变实参的数值。
3、传引用----安全地获取实参地址
引用定义出来的形参变量相当于实参变量的别名,也就是说形参等同于实参,它们虽是两个不同的变量名,指向的是同一块内存地址,对形参操作实参也会改变。
#include<iostream>
void func_val(int a)
{
a = 1;
}
void func_addr(int*a)
{
*a = 2;
}
void func_quote(int&a)
{
a=3;
}
int main()
{
int a = 0;
std::cout << "初值:" << a << std::endl;
func_val(a);
std::cout << "传值:" << a << std::endl;
func_addr(&a);
std::cout << "传地址:" << a << std::endl;
func_quote(a);
std::cout << "传引用" << a << std::endl;
system("pause");
}
求结构体大小
- 对齐数:改成员类型所占大小和默认对齐数的较小者。VS中默认的值为8,Linux中的默认值为4。其它成员要对齐到对齐数的整数倍。
- 结构体的总大小要是最大的成员的整数倍。
- 内嵌结构体的对齐数=自身最大对齐数。
VS编译器
struct S3
{
double d; //对齐数是8,8字节
char c; //对齐数是1,9字节
int i; //对齐数是4,要是8的整数倍16字节
};
//结构体嵌套问题
struct S4
{
char c1; //对齐数是1,1字节
struct S3 s3; //对齐数是8,16字节
double d; //对齐数是8,24字节
};
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
struct S
{
char a : 3; //3<8,1字节
char b : 4; //3+4<8,1字节
char c : 5; //7+5>8,2字节
char d : 4; //5+4>8,3字节
};
this指针
1)this指针是指向对象自身的指针。 通常在class定义时要用到类型变量自身时,因为这时候还不知道变量名,就用this这样的指针来使用变量自身。
2)this 指针是一个隐含于每一个非静态成员函数中的特殊指针。
3)this 指针被隐含地声明为: ClassName *const this
,this指针是只能指向变量自身的常指针。
4)this 指针是个右值,所以不能取得 this 的地址(不能 &this)。
5)this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。
野指针和悬空指针
野指针 | 悬空指针 |
---|---|
未初始化的指针,其指针内容为一个随机值。 | 指针所指向的内容已被销毁,指针未置空 |
任何指针变量刚被创建时不会自动成为NULL指针,所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。 | 指针正常初始化,曾指向过一个正常的对象,但是对象销毁了,该指针未置空,就成了悬空指针。 |
它没有被正确的初始化于是指向一个随机的内存地址。存在野指针是一个严重的错误。 | 悬空指针存在并没有什么问题,除非你尝试通过这个指针访问指向的地址空间。 |
int main() {
int *p; // 指针未初始化,此时 p 为野指针
int *pi = nullptr;
{
int i = 6;
pi = &i; // 此时 pi 指向一个正常的地址
*pi = 8; // ok
}
*pi = 6; // 由于 pi 指向的变量 i 已经销毁,此时 pi 即成了悬空指针
return 0;
}
关于new失败
- new失败了会怎么样?抛异常bad_alloc,抛了异常就需要捕获。所以对new操作符的返回值进行判断是毫无意义的。
- 不让new抛异常怎么办?
int* p = new (std::nothrow) int; // 这样如果 new 失败了,就不会抛出异常,而是返回空指针
if ( p == 0 ) // 如此这般,这个判断就有意义了
return -1;