C/C++面试题总结1(包含指针很重要的内容)
1. const常量和宏定义常量的区别
1.const常量具有类型,编译器可以进行安全检查
2.#define宏定义没有数据类型,只是简单的字符串替换,不能进行安全检查
3.const定义的常量只有一份,#define有多份
4.const其保护作用,防止修改变量
2.c/c++的基本数据类型在不同32/64位os下各数据类型字节数
基本类型 32位平台下的字节数 64位平台下的字节数 取值范围 bool 1 1 true(!0);flase(0); char 1 1 [-128,127] unsigned char 1 1 [0,255] short 2 2 [-32768,32767] unsigned short 2 2 [0,65535] int 4 4 [-231-1,231] unsigned int 4 4 [0,2^32-1] long 4 4/8(一般都是4字节) [-231-1,231],~/[-263-1,263] unsigned long 4 8 [0,232-1],[0,264-1] double 8 8 float 4 4 long long 8 8 long double(拓展精度浮点数) 8 8 void * 4 8 32位地址空间/64位地址空间 32和64位只用long类型和指针类型的字节数不同 // 指针类型字节数 // 32位:os寻址空间为32位,所以指针为:32bit = 4 byte // 64位:os寻址空间为64位,所以指针为:64bit = 8 byte
3.#define定义的函数
#define DOUBLE(x) x+x cout<<5*DOUBLE(5);
问输出值为?
5*5+5 = 30
4.c语言的字符数组问题
char arr[] = "str"; // sizeof(arr):4,未指定字符数组长度,字符数组会在字符串末尾自动添加'\0'
char at[3] = "ab"; // sizeof(at):3,指定字符数组长度,要留一个位置给'\0'使用,
// 字符数组的长度,是算上'\0'的
5.函数指针和函数指针数组
5.1 指向函数的指针
void a(){
cout<<1<<endl;
}
void b(){
cout<<2<<endl;
}
int main(int argv,char** argc) {
// 函数类型为void ();
// 定义一个指向没有参数且无返回值的函数
// 定义一个指向void ()()类型的函数指针p
void (*p)();
// 用函数名a(函数入口地址)给指向此函数的指针赋值 p 指向 a
p = a;
// 调用a函数
(*p)();
// 用函数名b(函数入口地址)给指向此函数的指针赋值 p 指向 b
p = b;
// 调用b函数
(*p)();
return 0;
}
// 结果:
// 1 2
5.2 指向函数指针的数组 【重点】
void a(){
cout<<1<<endl;
}
void b(){
cout<<2<<endl;
}
int main(int argv,char** argc) {
// 函数类型为:void ()() - 函数指针类型;
// p[2]里面存储void ()()类型的函数的指针void (*)() -- 存指针地址
// 定义两个指向void (*)()类型的数组
void (*p[2])();
// 赋值
p[0] = a;
p[1] = b;
// 调用函数
(*p[0])();
(*p[1])();
return 0;
}
5.3 函数指针经典案例 -- 括号拆分法
5.3.1 ( * ( void( * )( ) )0)( ) ;
这是《CTraps and Pitfalls》这本经典的书中的一个例子。没有发狂吧?下面我们就来分 析分析:
第一步:void(*)(),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
第二步:(void(*)())0,这是将 0 强制转换为函数指针类型,0 是一个地址,也就是说一 个函数存在首地址为 0 的一段区域内。
第三步:(*(void(*)())0),这是取 0 地址开始的一段内存里面的内容,其内容就是保存 在首地址为 0 的一段区域内的函数。
第四步:(*(void(*)())0)(),这是函数调用。 // 调用0地址处的函数
5.3.2 void( * signal ( int,void( * )( int ) ) )( int )
第一步:signal ( int,void( * )( int ) ),可以明白signal是一个函数名,它有两个参数一个是int,一个是一个void( * )( int )函数指针,且该函数指针指向的是一个匿名函数,该函数参数为int,返回值类型为void型。
第二步:void( * signal ( int,void( * )( int ) ) )( int ),去掉已分析过的黄色部分,可以明白signal函数的返回类型为函数指针类型void(* )( int ),该返回类型(函数指针)指向的是一个匿名函数,该函数参数为int,返回值类型为void型。
5.4 函数指针的应用:回调函数 【重点】
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给 另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调⽤用,⽽而是在特定的事件或条件发⽣生时由另外的一方调⽤用的,用于对该事件或条件进行响应。
回调函数的例子:
#include <iostream> using namespace std; // 使用回调函数 // 当输入数据是'$'时候,计算1+...+n的和 // 该函数指针类型为:int (*)(char,int); // 这个函数是我们写好的默认是系统的,供其他函数回调的 /** * 求和函数 * @param ch * @param n * @return */ bool sum(const char ch,const int n){ int t = 0; if(ch == '$') { for (int i = 0; i < n; ++i) { t += i; } cout << "1+...+n = " << t << endl; return true; } return false; } // 函数指针做函数的参数 print是回调函数 /** * 求1+...+n的函数,回调sum函数求和 * @param message:输入信号有误的信息 * @param ch:输入信号 '$':求和;其他信号:什么也不干 * @param n:求1+..+n的尾数 * @param p:回调函数的函数指针 */ void print(char message[],const char ch,const int n,bool (*p)(const char ch,const int n)){ bool sign = (*p)(ch,n); if (!sign){ cout<<message<<"is "<<ch<<endl; } } int main(int argv,char** argc) { print("input sign is not $,",'$',10,sum); return 0; }
5.4 函数指针数组应用 【重用表 -- 一般用在汇编语言orC语言写底层时候】
把写好的函数的名字放到一个执行该类型函数的表中,供其他函数查表调用
void producer(){ cout<<"producer"<<endl; } void consumer(){ cout<<"consumer"<<endl; } void consumer1(){ cout<<"consumer1"<<endl; } // arr中存放两个 void (*)()类型的函数指针(函数地址) void (*arr[3])() = {producer,consumer,consumer1}; int main(int argv,char** argc) { int n; while (true){ cout<<"请输入想要调用的函数:"; cin >>n; switch (n) { case 0: // arr[0]中存储的是函数地址,(*函数地址)():执行函数的意思 (*arr[0])(); break; case 1: (*arr[1])(); break; case 2: (*arr[2])(); break; default: cout<<"operation is error"<<endl; break; } } return 0; }
6.指向二维数组的指针
6.1 使用一级指针访问二维数组
// a[2][3]: // 就是2个3列的一维数组组成的 int a[2][3] = { {1,2,3}, {4,5,6} }; // 使用一级指针指向二维数组,利用的是C语言数组在内存中是按行展开的 int* p = &a[0][0]; // 访问二维数组,因为二维数组在内存中是按行排列的,所以可以直接访问每一个元素 for (int i = 0; i < 6; ++i) { cout<<*(p+i)<<" "; }
6.2 使用指向数组的一级指针访问二维数组 -- 数组指针的应用
int a[2][3] = { {1,2,3}, {4,5,6} }; // 使用指向数组类型int [3]的指针指向二维数组a,int [3]表示a的一维数组的类型 // 所以,p就是指向一个有3列一维数组的指针 // 定义一个指向int [3](一维数组)类型的指针p int (*p)[3] = a; // 访问二维数组 // *(q+j):表示存储在q中的一维数组,就是a的每行首元素的地址,*(q+j)=&a[j]; // *(q+j)+i:表示一维数组a[j]中的第i个元素的地址 *(q+j)+i = &a[j][i]; // *(*(q+j)+i):表示访问a[i][j]的内容 for (int j = 0; j < 2; ++j) { for (int i = 0; i < 3; ++i){ cout<<*(*(q+j)+i)<<" "; } }
6.3 使用指向二维数组的指针访问二维数组 【重点】 -- 数组指针的应用
int arr[2][3] = { {1,2,3}, {4,5,6} }; // 定义一个指向int [2][3](两行三列二维数组)的指针 int (*p)[2][3]; // p 指向 arr p = &arr; // *p是二维数组的名字arr === > (*p) == arr // (*p)[i][j]:访问数组的第i行j列元素 -- 指针加(),还是因为[]优先级高于* for (int i = 0; i < 2; ++i) { for (int j = 0; j < 3; ++j) { cout<<(*p)[i][j]<<" "; } cout<<endl; }
7.三维数组的指针访问 -- 其他高维指针自己弄吧,学过线性代数就好理解了
int main(int argv,char** argc) { // arr[2][3][4]: // 就是2个3行4列的二维数组组成的 // 初始化时,{}表示一行,给两行二维数组即可 int arr[2][3][4] = { { {1,1,1,1}, {1,1,1,1}, {1,1,1,1} }, { {0,0,0,0}, {0,0,0,0}, {0,0,0,0} } }; int (*p)[2][3][4]; p = &arr; // *p = arr // 访问(i,j,k)位置的元素:(*p)[i][j][k] for (int i = 0; i < 2; ++i) { for (int j = 0; j < 3; ++j) { for (int k = 0; k < 4; ++k) { cout<<(*p)[i][j][k]<<" "; } cout<<endl; } cout<<endl; } return 0; }
8. 指针数组和数组指针 -- 区别:*,[]优先级不同搞出来的
指针数组 - 是一个存放指针的数组
形如: // [] 优先级高于*,所以arr和[]先结合,所以是指针数组 int *arr[3]; // 定义一个存储3个int*(int型指针)类型的数组 void (*at[3])(char ch); // 定义一个存储3个void (*)(char) [-- 指向void ()(char)函数类型的指针]类型的数组 // 定义一个存储3个指向void ()(char)函数类型的指针的数组
数组指针 - 是一个指向数组的指针
形如: // []优先级高于*,但是()优先级高于[],arr先和*结合,所以是指针 int (*arr)[3]; // 定义一个指向int [3](有3个元素的一维数组)的指针 // 定义一个指向含有3个元素的一维数组的指针 int (*arr)[2][3];
定义一个指向高维数组的指针书写方式
1.先找指针要指向的类型 2.在添加(*指针名) eg:定义一个指向2行3列的数组的指针 1.指向的类型为:int [2][3]; 2.int (*arr)[2][3]; // arr是一个指向二维数组([2][3])的指针 3.*arr:表示二维数组