C/C++面试语常见关键字

C语言中以功能有32个关键字(注意这个是针对C99而言的)
C++中的关键字共有63个(针对C++98而言的)
常考的关键字:

volatile的作用

volatile的意思是易变的、不稳定的,volatile与const一样是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其他线程等。volatile主要的作用来提醒编译器volatile所修饰的变量可能会被修改,告诉编译器不要对访问自己自己所修饰的变量的代码进行优化,从而可以提供对特殊地址的稳定访问。
对于一般情况下,变量可能被优化存储在寄存器中,当下一次需要时可直接从寄存器中读取,在这个过程该变量所在的内存地址存储的变量可能已经被外部改变,但是加上volatile之后该变量就不会被优化存储在寄存器中,而是每次直接从内存中读取最新的值
在VS中一般是在Debug版本下无优化,在release版下才会进行代码优化。

extern

extern修饰的变量时声明变量,声明变量是在其他文件中进行声明(也可以看做是引用变量)这里就牵扯到声明和定义。

  • 什么是声明?什么是定义?两者之间有什么区别?
  int i;//定义
  extern int i;//声明

定义:所谓的定义就是创建一个对象,并且为这个对象分配一块内存空间并给它取上一个变量名,这个变量名一旦和内存空间匹配起来就是绑定了,锁死了。也就是说,一个变量或者对象只能在自己的定义域内定义一次。(就好像是一个人这辈子只能同时和一个对象结婚,一夫多妻不存在的!)
声明:声明有两重含义
1. 告诉编译器,这个变量名已经匹配到一块内存上了,下面的代码用到的变量或对象是在别的地方定义的。声明可以出现多次。
2. 告诉编译器,这个变量名已经被使用了,别的地方不能再用它作为变量名。
声明和定义最主要的区别在于:定义创建了对象并为这个对象分配了内存,而声明并没有为变量分配内存空间

register

register关键字所修饰的变量叫做寄存器变量,register请求编译器尽可能的将变量存在CPU内部的寄存器而不是用过寻址访问以提高效率,是尽可能的,并不是绝对的,因为CPU的寄存器也就那么点,并不是把所有寄存器变量都放在寄存器中。因为register 变量可能不存放在内存中,所以不嫩用&运算符来获取寄存器变量的地址。
寄存器是CPU不和内存直接打交道,而是通过寄存器,因为寄存器就是一小块一小块的存储空间,它存取的速度要比内存快的多。

const关键字有哪些用途(要分两个方面C语言和C++)?

在C语言中:const定义的是只读变量,具有不可修改的属性,编译器通常不会为普通的const只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读取内存的操作,从而效率提高
const可以修饰数组(只读数组)、指针、函数的参数以及返回值(不希望参数或返回值被改变)和普通变量。

   const修饰指针:
    const int*p;//const修饰*p,p是指针,*p也就是p所指向的对象不可变
    int const*p;/const修饰*p,*p不可变
    int* const p;//const修饰p,指针p不可变,指向的对象可变
    const int*const p;//指针p和指向对象*p都不可变
    这类题目的宗旨就是:const离谁近就修饰谁,谁就不可变

const和#define的区别:
1. const 定义的是一个具有常属性的变量;#define定义的只是一个常数没有类型
2. define定义的常量是在预编译阶段进行替换的,而const定义的具有常属性的变量是在编译期间处理确定其值的。
3. const定义的只读变量从汇编角度来看只是给出了对应的内存地址,而不是像define一样给出的是立即数
4. const定义的只读变量在程序运行期间只有一份拷贝(因为它是全局的只读变量,存放在静态区),而define定义的宏常量在内存中有若干个拷贝
5. const不能重定义,#define可以通过#undef取消某个符号的定义再次进行重新定义
6. #define可以用来防止头文件的重复引用,const不能


从C++角度看const
C语言中的const是一个“冒牌货”,C++中的const是一个真正的常量,原因:C语言中为具有常属性的变量分配了空间,C++编译器遇到const声明的常量时,直接将这个常量放入符号表中不会为其分配内存,在使用这个常量的时候直接从符号表中取值。但是若是对const使用了extern或者&操作符,会为const常量另外分配空间。
const的应用场景:
* const修饰形参,一般和引用同时使用,传递效率高,避免对象被修改
* const修饰返回值,返回值不可修改
* const修饰类数据成员,必须在构造函数的初始化列表中初始化
* const修饰类成员函数,实际修饰隐含的this,表示在类成员函数中不可以对类的任何成员进行修改
const成员函数才可以调用一个const修饰的类对象
* 在const修饰的成员函数中要对类的某个数据成员进行修改,该数据成员定义声明必须要加mutable关键字
* const修饰的常量在编译期间替换
类的const成员函数:
1. const其实修饰的是this指针所指向的对象,保证调用这个函数的对象不会被改变
2. const类型的成员变量必须在构造函数的初始化列表中初始化
3. const对象可以调用非const的成员函数和const的成员函数
4. 非const对象可以调用非const成员函数和const成员函数
5. const成员函数可以调用const函数但是不能调用非const成员函数,因为在非const成员函数中有可能会改变对象
6.非const成员函数可以调用const成员函数和非const成员函数

static关键字

C语言中的static:
1. 生命周期:static修饰的变量,存储在内存的静态存储区,无论是在函数内还是函数外,生命周期都是整个程序运行期间,static修饰的函数其生命周期也是整个程序的运行期间
2. 作用域:static修饰的局部变量的作用域就是定义该变量的那个作用域内,static修饰的全局变量的作用域是从这个变量定义开始到整个程序结束
3. static修饰的变量都存储在静态区
4. static修饰函数或者全局变量会改变他们的链接属性,只具有内部链接属性。也就是说,被static修饰的全局变量或者函数在其他文件中不能使用,只能在源文件中使用
5. static修饰的变量默认初始化为0


C++中的static:
1. static修饰的类成员变量为静态成员变量
1.静态成员变量一定要在类外进行初始化
2. 静态成员变量为所有的类对象所共享
3. 静态成员变量必须在类中声明,类外定义,定义时不用加static关键字
4. 包含静态成员变量的类对象的大小并不包含静态成员变量的大小
2. static修饰类成员函数为静态成员函数
1. 静态成员函数必须在类中声明,类外定义,定义时不用加static关键字
2. 静态成员函数为所有类对象所共享
3. 静态成员函数也有访问权限
4. 静态成员函数内部职能访问静态的成员变量;普通的成员函数可以访问静态成员变量,也可以访问非静态成员变量
5.静态成员函数没有隐藏的this指针不能访问任何非静态成员
编译器是通过类的作用域的方式来访问静态成员的。
静态成员变量和非静态成员变量的区别:
* 静态成员变量必须要放在类外定义
* 静态成员所有类对象共享,普通成员变量每个类对象各自拥有自己的
* 计算类对象的时候不包含静态成员变量
* 普通类型的变量只能借助对象来访问,静态成员变量既可以通过对象来访问也可以通过类的作用域来访问
* 存储的位置不同,普通变量存储在对象所在位置的栈上,静态成员变量存储在数据段
* 它在类对象生成前就已经构造完成,可以对其进行修改
* 如果类声明放在.h文件中,那么static成员的定义必须在.cpp中
静态成员函数和非静态成员函数的区别:
* 静态成员函数中不能访问普通变量—>(普通的成员变量和普通的成员函数)
* 静态成员函数没有this指针,普通成员函数有隐藏的this指针
* 静态成员函数中可以访问静态成员变量(因为类里面所有对象共享)
* 静态成员函数不能被const修饰,因为const修饰的实际上是this指针
* 静态成员函数不能调用非静态成员函数,非静态成员函数可以调用静态成员函数

sizeof运算符

sizeof是最冤枉的关键字,它是一个运算符而不是函数!!!
1. sizeof是一个关键字(操作符),它的结果类型是size_t.它在头文件中typedef为unsigned int类型。sizeof是算符,而strlen是一个函数
2. sizeof可以用类型做参数,strlen只能用char*做参数,而且必须是以’\0’结尾的,sizeof还可以用函数调用(不执行函数体)做参数大小等于返回类型的大小。
3. 数组做sizeof的参数不退化,传递给strlen就退化为指针了
4. sizeof是在程序编译的时候就已经将sizeof计算过了(是类型或者变量的长度),而strlen的结果是要在调用的时候才能计算出来,是用来计算字符串的长度,而不是内存的大小
5. sizeof后如果是类型就必须加括弧,如果是变量就可以不加,但是strlen函数调用时必须后面有括弧
6. sizeof返回的是实际空间的大小,并不能计算动态分配了的数组或外部的数组的大小
7. sizeof操作符不能用于函数类型,不完全类型或位段,不完全类型值具有位置未知存储大小的数组类型(柔性数组),位置内容的联合体或者结构体类型、void类型等
8. sizeof可以求取void*类型的大小
9. 当表达式作为sizeof的操作数的时候,它的返回值是表达式计算结果的类型大小,但是它不对表达式求值

struct

struct可以将一些相关联的数据打包成一个整体。既然提到了struct那么肯定就避免不了结构体,结构体的内存对齐。
1. 什么是结构体?
结构体是一系列类型数据的集合这些数据可能描述了一个物体。
2. 什么时候会用到结构体?
1. 当内置类型无法满足用户需求的时候,没有合适类型的时候,需要封装特定的类型
2. 当函数有多个参数时,或者函数的返回值过多的时候,需要封装特定的类型,将参数打包返回
3. 什么是结构体内存对齐?为什么存在内存对齐?内存对齐的规则是什么?
编译器为程序中的每一个数据单元安排在合适的位置上,从而导致了相同的变量,不同声明顺序的结构体的大小不同。 内存对齐存在的主要原因:
1. 平台原因:不是所有的硬件平台都可以访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出异常(这个为什么会抛出异常,不是很懂)
2. 性能原因:经过内存对齐后,CPU的内存访问速度大大提升,因为如果没有内存对齐的话,CPU在访问一个数据时可能会进行多次访问然后拼接在一起。
结构体的内存对齐规则:
1. 结构体的第一个成员存放在与结构体变量偏移量为0的地址处
2. 结构体的其他成员要对齐到对齐数的整数倍地址处。对齐数:编译器默认的一个对齐数与该成员大小中的较小值,VS默认的对齐数位8,Linux中默认值为4
3. 结构体的总大小最最大对齐数的整数倍
4. 如果一个结构体中嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的大小就是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍
5. 可以通过#pragma pack number设置对齐数
如何知道结构体中某个成员相对于结构体起始位置的偏移量?

//使用offsetof宏  
 size_t offsetof( structName, memberName )

你可能感兴趣的:(C语言面试题常见关键字总结,C语言关键字,面试,C++关键字)