1.C/C++关键字
1.1 static(静态)变量
在C中,关键字static是静态变量:
- 静态变量只会初始化一次,然后在这函数被调用过程中值不变。
- 在文件内定义静态变量(函数外),作用域是当前文件,该变量可以被文件内所有函数访问,不能被其他文件函数访问。为本地的全局变量,只初始化一次。
在C++中,类内数据成员可以定义为static
- 对于非静态数据成员,每个对象有一个副本。而静态数据成员是类的成员,只存在一个副本,被所有对象共享。
- 静态成员变量没有实例化对象也可以使用,“类名:静态成员变量”
- 静态成员变量初始化在类外,但是private和protected修饰的静态成员不能类外访问。
class Stu
{
public:
static int age;
private:
static int height;
};
//初始化静态成员变量
int Stu::age = 19;
int Stu::height = 180;
int main()
{
cout<<Stu::age<<endl;//输出19;
cout<<Stu::height<<endl;//错误的,私有无法访问。
Stu s;
cout<<s::age<<endl;//输出19;
cout<<s::height<<endl;//错误的,私有无法访问。
return 0;
}
- 在类中,static修饰的函数是静态成员函数。静态成员函数一样属于类,不属于对象,被对象共享。静态成员函数没有this指针,不能访问非静态的函数和变量,只能访问静态的。
与全局变量相比,静态数据成员的优势:
- 全局变量作用域是整个工程,而static作用域是当前文件,避免命名冲突
- 静态数据成员可以是private成员,而全局变量不能,实现信息隐藏
为什么静态成员变量不能在类内初始化?
因为类的声明可能会在多处引用,每次引用都会初始化一次,分配一次空间。这和静态变量只能初始化一次,只有一个副本冲突,因此静态成员变量只能类外初始化。
为什么static静态变量只能初始化一次?
所有变量都只初始化一次。但是静态变量在全局区(静态区),而自动变量在栈区。静态变量生命周期和程序一样,只创建初始化一次就一直存在,不会销毁。而自动变量生命周期和函数一样,函数调用就进行创建初始化,函数结束就销毁,所以每一次调用函数就初始化一次。
在头文件中定义静态变量是否可行?
不可行,在头文件中定义的一个static变量,对于包含该头文件的所有源文件,实质上在每个源文件内定义了一个同名的static变量。造成资源浪费,可能引起bug
1.2 const的作用
常量类型也称为const类型,使用const修饰变量或者对象
在C中,const的作用为:
- 定义变量(局部或者全局)为常量
const int a = 10; //常量定义时,必须初始化
- 修饰函数的参数,函数体内不能修改这个参数的值
- 修饰函数的返回值
- const修饰的返回值类型为指针,返回的指针不能被修改,而且只能符给被const修饰的指针
const char* GetString() { //... } int main() { char *str = GetString();//错误,str没被const修饰 const char *str = GetString();//正确 }
- const修饰的返回值类型为引用,那么函数调用表达式不能做左值(函数不能被赋值)
const int & add(int &a , int &b) { //.. } int main() { add(a,b) = 4;//错误,const修饰add的返回引用,不能做左值 }
- const修饰的返回值类型为普通变量,由于返回是普通临时变量,const修饰没意义。
- const修饰的返回值类型为指针,返回的指针不能被修改,而且只能符给被const修饰的指针
在c++中,const还有作用为:
- const修饰类内的数据成员。表示这个数据成员在某个对象的生命周期是常量,不同对象的值可以不一样,因此const成员函数不能在类内初始化。
- const修饰类内的成员函数。那么这个函数就不能修改对象的成员变量
const的优点?
-
进行类型检查,使编译器对处理内容有更多了解。
-
避免意义模糊的数字出现,类似宏定义,方便对参数进行修改。
-
保护被修饰的内容,防止被意外修改
-
为函数重载提供参考
class A { void f(int i){...} //非const对象调用 void f(int i) const {...}//const对象调用 }
5.节省内存
6.提高程序效率(编译器不为普通const常量分配存储空间,而保存在符号表中。称为一个编译期间的常量,没有存储和读内存的操作)
什么时候使用const?
-
修饰一般常量
-
修饰对象
-
修饰常指针
const int *p; int const *p; int *const p; const int *const p;
-
修饰常引用
-
修饰函数的参数
-
修饰函数返回值
-
修饰类的成员函数
-
修饰另一文件中引用的变量
extern const int j;
const和指针(常量指针、指针常量)
-
常量指针(const 修饰常量,const在*的左边)
const int *p = &a; // const修饰int,指针的指向可以修改,但是指针指向的值不能改 int const *p;//同上 p = &b;//正确 *p = 10;//错误
-
指针常量(const修饰指针,const在*的右边)
int *const p = &a;//const修饰指针,指针的指向不可以改,但是指针指向的值可以改 *p = 10;//正确 p = &b;//错误
-
const都修饰指针和常量(指针和常量都不能修改)
const int *const p; int const *const p;
1.3 switch语句中case结尾是否必须加break
**一般必须在case结尾加break。**因为通过switch确认入口点,一直往下执行,直到遇见break。否则会执行完这个case后执行后面的case,default也会执行。 注,switch(c),c可以是int、long、char等,但是不能是float
1.4 volatile 的作用
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。
- 编译器不再进行优化,从而可以提供对特殊地址的稳定访问。
- 系统总是重新从它所在的内存读取数据,不会利用cache中原有的数值。
- 用于多线程被多个任务共享的变量,或者并行设备的硬件寄存器
1.5 断言ASSERT()是什么?
**是一个调试程序使用的宏。**定义在<assert.h>中,用于判断是否出现非法数据。括号内的值 为false(0),程序报错,终止运行。
ASSERT(n != 0);// n为0的时候程序报错
k = 10/n;
ASSERT()在Debug中有,在Release中被忽略。 ASSERT()是宏,assert()是ANSCI标准中的函数,但是影响程序性能。
1.6 枚举变量的值计算
#include<stdio.h>
int main()
{
enum {a,b=5,c,d=4,e};
printf("%d %d %d %d %d",a,b,c,d,e);
return 0;
}
输出为 0 5 6 4 5
1.7 字符串存储方式
- 字符串存储在栈中
char str1[] = "abc";
char str2[] = "abc";
- 字符串存储在常量区
char *str3 = "abc";
char *str4 = "abc";
- 字符串存储在堆中
char *str5 = (char*)malloc(4);
strcpy(str5,"abc");
char *str6 = (char*)malloc(4);
strcpy(str6,"abc");
- 字符串是否相等
- str1 != str2 ,str1和str2是两个字符串的首地址。
- str3 == srt4 , str3和str4是常量的地址,同样字符串在常量区只存在一份。
- str5 != str6 ,str5 和str6是指向堆的地址。
1.8 程序内存分区
内存高地址 | 栈区 |
---|---|
堆区 | |
全局/静态区 (.bss段 .date段) | |
常量区 | |
内存低地址 | 代码区 |
- 栈区(stack)
-
临时创建的局部变量存放在栈区。
-
函数调用时,其入口参数存放在栈区。
-
函数返回时,其返回值存放在栈区。
-
const定义的局部变量存放在栈区。
- 堆区(heap)
-
堆区用于存放程序运行中被动态分布的内存段,可增可减。
-
malloc函数分布的内存,必须用free进行内存释放,否则会造成内存泄漏。
- 全局区(静态区)
- 全局区有.bss段和.data段组成,可读可写。
- .bss段
-
未初始化的全局变量存放在.bss段。
-
初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
-
.bss段不占用可执行文件空间,其内容有操作系统初始化。
- .data段
-
已经初始化的全局变量存放在.data段。
-
静态变量存放在.data段。
-
.data段占用可执行文件空间,其内容有程序初始化。
-
const定义的全局变量存放在.rodata段。
- 常量区
-
字符串存放在常量区。
-
常量区的内容不可以被修改。
- 代码区
- 程序执行代码存放在代码区。
1.9 *p++ 和 (*p)++ 的区别
- *p++ 先完成取地址,然后对指针地址进行++,再取值
- (*p)++,先完成取值,再对值进行++
1.10 new / delete 与 malloc / free的异同
-
相同点
- 都可用于内存的动态申请和释放
-
不同点
-
new / delete 是C++运算符,malloc / free是C/C++语言标准库函数
-
new自动计算要分配的空间大小,malloc需要手工计算
-
new是类型安全的,malloc不是。例如:
int *p = new float[2]; //编译错误 int *p = (int*)malloc(2 * sizeof(double));//编译无错误
-
new调用名为operator new的标准库函数分配足够空间并调用相关对象的构造函数,delete对指针所指对象运行适当的析构函数;然后通过调用名为operator delete的标准库函数释放该对象所用内存。malloc / free均没有相关调用
-
malloc / free需要库文件支持,new / delete不用
-
new是封装了malloc,直接free不会报错,但是这只是释放内存,而不会析构对象
-
1.11 exit()和return 的区别
- return是语言级的,标志调用堆栈的返回。是从当前函数的返回,main()中return的退出程序
- exit()是函数,强行退出程序,并返回值给系统
- return实现函数逻辑,函数的输出。exit()只用来退出。