C++面试基础
- 自己整理了一些常见的面试题,频率挺高的都是,而且感觉这里这些基础的东西都会问,自己过几天也要面试了,所以发上来让大家一起看看,有什么错误的地方望提醒我纠正。
- 32位数据类型以及sizeof大小、
- char:1; short int:2; int:4; long:4; long long:8; float:4; double:8;bool:1
- 指针类型的都是4(虚函数也是4,因为它是靠虚指针管理的)。sizeof结构体的运算都是内存对其的知识点。(或者可以按照8的倍数去算)。
- 对类来说:空类:1个。若类中有虚函数:保存了一个指针(vptr)指向虚表,所以虚函数四个。继承的时候要把基类的内存分配的大小也算上。
- int arr[6]={};sizeof(arr)=4*6;sizeof (array) / sizeof (int) 这个是返回数组元素个数
详见:http://blog.csdn.net/Hello_Hwc/article/details/41170719 - 这里也可能考到继承中的sizeof的大小
-
#include<iostream> #include<stdio.h> #include<cstring> using namespace std; class A { public: A() : p1(1), p2(2), p3(3) {} //当基类中含有虚函数的时候,含有多个虚函数都是通过一个虚函数指针来控制一个大的虚函数表的,所以所有的虚函数合起来就都是4的大小(32位的操作系统中) virtual int a(){} virtual int b(){} public: int p1; private: int p2; protected: int p3; }; class B: public A {}; int main() { A a; std::cout << *(int *)&a << std::endl; std::cout << *((int *)&a + 1) << std::endl; std::cout << *((int *)&a + 2) << std::endl; B b; std::cout << *(int *)&b << std::endl; std::cout << *((int *)&b + 1) << std::endl; std::cout << *((int *)&b + 2) << std::endl; cout<<sizeof(B)<<endl; }
-
- 内存分配有哪几种形式?分别为何?区别是什么?对编译速度影响是何(详解:http://www.cnblogs.com/daocaoren/archive/2011/06/29/2092957.html)
- 栈:存储局部变量,函数参数。由系统自动创建和释放的。
- 堆:它是通过new和delete管理的,不用编译器管理,由程序员管理,如果没有释放掉,操作系统会自动的回收,动态的扩展和收缩。
- 栈和堆的区别
- 管理方式:栈由系统控制,堆由程序员管理,所以堆容易产生碎片(忘记释放空间);
- 大小:栈比较小,堆比较大;
- 生长方式:堆是向上生长的,栈是向下生长的。
- 碎片问题:堆如果频繁new和delete来申请和释放内存,会导致内存的不连续,这样可能会造成大量的内存浪费(产生许多碎片);栈是先进后出的,所以在一块内存弹出之前,它的上一块内存早就弹出了,不会出现内存保留问题
- 分配方式:栈可以动态也可以静态,堆由于是new和delete 操作所以只能是动态的
- 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是 C/C++ 函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
- 自由存储区:malloc和free申请内存你的地方,和堆很相似。
- 全局静态存储区:存储全局变量,静态变量。
- 常量存储区:只是存储常量,一般不允许修改。
- 知道局部变量为什么比全局变量快么?
- 当Cache命中的时候,CPU访问内存的效率是最高的 由于局部变量是存在栈中的,当一个函数占用的栈空间不是很大的时候,这部分内存很有可能全部命中cache,这时候CPU访问的效率是很高
- 当Cache命中的时候,CPU访问内存的效率是最高的 由于局部变量是存在栈中的,当一个函数占用的栈空间不是很大的时候,这部分内存很有可能全部命中cache,这时候CPU访问的效率是很高
- new,delete和malloc,free的区别
- new是C++的运算符,malloc是C中的库函数(STL的底层结构的内存管理都是用malloc来进行的(为什么自己查))
- malloc只管分配不进行初始化,产生的都是随机值;
- new返回的是一个指针对象,malloc返回的是一个强制转换的指针对象
- int *p=new int pp[100]; int *p=(Int*)malloc(sizeof(int)*128);
- int *p=new int pp[100]; int *p=(Int*)malloc(sizeof(int)*128);
- 有了malloc/free为什么还要new/delete?
- malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
- 由于C++函数经常要调用C中的库函数,那么就可能会用到malloc函数,所以要有malloc。
- 对于不是内置的数据类型,malloc和free无法满足动态对象的需求,对象在创建和销毁的时候会调用构造函数和析构函数,但是这两个函数不是运算符,不在编译器管理范围之内,所以只能用new和delete
- 内联函数与宏定义的区别,它们各有什么优点
- 内联函数在运行时可调试,而宏定义不可以
- 编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
- 内联函数可以访问类的成员变量,宏定义则不能
- 在类中声明同时定义的成员函数,自动转化为内联函数
- 内联函数的优点
- 上面都是,也能够减少内存开销,提高效率(函数调用引起的)
- 缺点:引入内联函数过多,导致代码膨胀。
- 上面都是,也能够减少内存开销,提高效率(函数调用引起的)
- 宏定义优点:
- 使程序简洁易懂,提高程序的运行效率(使用带参数的宏定义可以完成函数的调用功能,减少了函数调用产生的出栈入栈的开销,提高了运行的效率)
- 缺点:会产生二义性(括号的使用等),不会检查参数是否合法,存在安全隐患,还有上面的那些区别。
- 理解什么是重载、覆盖、隐藏,区别是何?可否举例?(详见:http://www.cnblogs.com/qlee/archive/2011/07/04/2097055.html)
- 重载:在一个类中对函数进行重载,函数名称相同,函数的参数个数,参数可能不同。
- 覆盖:派生类覆盖了基类中的同名函数,函数名称相同,参数相同,基类函数必须是虚函数,
- 隐藏:派生类中的函数屏蔽了与其同名的基类函数,如果函数名称相同,参数不同(无论是不是虚函数),隐藏;如果函数名称相同,参数相同,但是不是虚函数,也隐藏
- 多态
- 多态是什么:多态是由虚函数实现的。举例:动物基类和派生类(叫声)
- 多态都有哪些(动态和静态的):http://blog.csdn.net/hackbuteer1/article/details/7475622
- 多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。(通过函数重载实现的)而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。(通过虚函数实现的)
- 动态绑定怎么实现:声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。
- 例如:A *a;B b;a=&b;a.god();//这也是相当于动态绑定(有些题目比较难理解)
- 纯虚函数和虚函数区别
- 纯虚函数:在基类中只能声明不能定义,在派生类中必须都实现这个纯虚函数。带有纯虚函数的称为抽象类,只能当做基类使用,抽象类不能定义对象
- 虚函数:在基类中声明定义,但是在派生类中可以不定义。
- 引入纯虚函数比较安全,效率也比较高。
- 虚函数原理:主要是通过虚函数指针和一个虚函数表来实现的。举个例子:在一个类中有一个虚函数,当这个类创建一个实例对象的时候,那么对象的内存中会存在一个虚函数表(保存虚函数的地址),同时创建一个虚函数指针,保存虚函数表的地址,当调用方法的时候,虚函数指针去指向这个虚函数表。对象不同调用的方法也就不想同,这就实现了多态。
- 虚函数表的内存分配?
- 为什么析构函数要定义成虚函数
- 首先要明确:1.每个析构函数(不加 virtual) 只负责清除自己的成员。
2.可能有基类指针,指向的确是派生类成员的情况。(这是很正常的)
那么当析构一个指向派生类成员的基类指针时,程序就不知道怎么办了。
所以要保证运行适当的析构函数,基类中的析构函数必须为虚析构。
反正你在写一个类时,将其析构函数写为虚函数总不会错,一样重载的。
- 首先要明确:1.每个析构函数(不加 virtual) 只负责清除自己的成员。
- struct 和class有什么区别?c语言中的struct 和c++中的struct一样么?有什么区别?
- C++中的struct和class区别
- 默认的访问权限:struct是C中升级版本,struct的默认是public;class默认是private
- 默认的继承权限;struct是public,class是private
- class可以有默认的构造和析构函数,而struct没有。
- 他们都有成员函数,继承,多态,struct其实也是一个类(但是实际上也还是一个数据结构)。class可以继承struct,struct可以继承class
- C中的struct和C++中的struct区别
- C中的struct只是一个自定义的数据类型(结构体),struct是抽象的数据类型,支持一些类的操作和定义
- C中的struct只是一个自定义的数据类型(结构体),struct是抽象的数据类型,支持一些类的操作和定义
- C++中的struct和class区别
- 说说什么是野指针?野指针什么情况下出现? (详细)
- 野指针:指向一个已经删除的对象或者未申请访问受限地址的指针。
- 出现情况:没有初始化(要么等于NULL,要么指向对的地方),释放指针没有置为NULL(释放只是释放了指针所指的内存,指针本身并没有释放掉)
- 你熟悉预编译指令么?条件编译是用来做什么的?你会写么?
- 预编译主要是处理#开头的指令,如#include,宏定义,条件编译等,进行初期的代码替换的工作(宏替换等)
- #include 包含源代码;#ifdef(#if define),#ifndef(ifndef=if not define:如果没有宏定义),#endif:
- 条件编译:#if #else #elif #endif 目的是:防止头文件的重复包含和编译
-
1 #ifdef 标识符 2 /*程序段 1*/ 3 #else 4 /*程序段 2*/ 5 #endif 6 7 它的作用是当标识符已经由#define定义过了,则编译程序段1,否则编译程序段2,也可以使用简单形式 8 9 #ifdef 标识符 10 /*程序段1*/ 11 #endif
- String
- 知道断言ASSERT()怎么样么?一定要常用。它是函数还是宏?为什么不能是函数?
- assert其实是一个宏,我自己没怎么用过,它的作用是一个判断计算表达式真假。例如:assert(a!=0)(一般的它只允许调用一个表达式参数,如果多个参数判断不准确),如果a!=0,就是结果正确,程序继续运行,如果false那就会先向stderr打印一个错误信息,然后再调用abort终止程序。(一般把这个宏放在程序中来进行调试,它主要是用作debug版本中 )
- 它的缺点是:频繁的调用会影响程序的性能,会增加额外的开销。
- 链表的一些操作和面试常见的题目
- http://blog.csdn.net/luckyxiaoqiang/article/details/7393134
- 题目
- 栈的一些操作和面试题:
- 题目:
- 两个队列实现栈
- 两个栈实现队列
- 元素出栈、入栈顺序的合法性
- 一个数组构造两个栈(最大限度利用空间,就是通过两个个指针:前后各一个分别扩充两个栈的内存)
- 题目:
- 二叉树,红黑树的一些操作和面试题
- 红黑树:(详见:http://www.cnblogs.com/skywang12345/p/3245399.html)
- 红黑树的数据结构(枚举颜色,父,左,右指针,颜色,节点值)
-
1 enum Color 2 { 3 RED = 0, 4 BLACK = 1 5 }; 6 7 struct RBTreeNode 8 { 9 struct RBTreeNode*left, *right, *parent; 10 int key; 11 int data; 12 Color color; 13 };
-
- 红黑树的各种操作的时间复杂度是多少?(所有操作都是logn)而二叉树的都是o(h)
- 红黑树的特性(五点)
- 红黑树和哈希表怎么选择?
- 应该从四个方面分析:数据总量,空间消耗,查找效率
- 查找效率:哈希表,常数时间不是吹的,这个和数据总量无关的,红黑树是logn级别的。(但是不一定选哪个,如果数量到达了一定的级别,哈希表还是比较好的,但是哈希表还有别的函数的消耗,这个也会影响)
- 内存消耗:对内存消耗十分严格,最好是用红黑树,哈希表有可能会太大太难管理
- 在实际的系统中,例如,需要使用动态规则的防火墙系统,使用红黑树而不是散列表被实践证明具有更好的伸缩性。Linux内核在管理vm_area_struct时就是采用了红黑树来维护内存块的。
- 红黑树相比于BST和AVL树有什么优点?
- 红黑树是牺牲了严格的高度平衡的优越条件为代价,它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。红黑树能够以O(log n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。
- 相比于BST(log(n+1)/log2),它的时间复杂度是更低的,而且最坏的时间复杂度是oN,这是不如红黑树的
- 相比于AVL树,时间复杂度是相同的,但是红黑树的统计效率更高。其实选择哪个树主要是看插入的数据,插入的数据分布比较好则适合AVL,插入的数据比较混乱的话就适合红黑树了
- 插入和删除操作(画图解释)
- 左旋(将X变为左子节点),右旋操作(将Y变成右子节点);插入(三种情况)删除(四种情况)操作。(画图解释原理)
- 红黑树的数据结构(枚举颜色,父,左,右指针,颜色,节点值)
- 二叉树
- 二叉树的一些节点的公式()
- 二叉树第i层上的结点数目最多为 2{i-1} (i≥1)。
- 深度为k的二叉树至多有2{k}-1个结点(k≥1)。
- 包含n个结点的二叉树的高度至少为log2 (n+1)。
- 在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1。
- 二叉树的操作的复杂度:o(H)
- 二叉树的一些面试题目
- 前序,中序,后序遍历。(递归的)
- 怎么判断二叉树是不是平衡。(每个部分都是平衡的才算平衡(高度差来判断)):递归法和非递归法
- 二叉搜索树转化为有序双链表
- 求一个二叉树的深度(递归)
- 二叉树中两个节点的最近公共祖先节点(三种情况:无根,有parent;有根,无parent,有左右;有根和节点值)
- 二叉树中叶子节点的个数(递归分别求左子树中的叶子节点和右子树的叶子节点))
- 二叉树的一些节点的公式()
- 红黑树:(详见:http://www.cnblogs.com/skywang12345/p/3245399.html)
- 你知道函数不能返回栈指针么?
-
#include<iostream> #include<string> using namespace std; char * fun() { char s[]="abc";//局部变量存储在栈中 return s;//返回的是栈指针 } string fun1(){ string s="abc"; return s; } int main(){ char *ptr=fun();//返回的栈指针,但是不管指向地方的内容是什么,一旦发生了函数调用(这里发生了函数调用,函数的入栈 //压栈操作使得上次栈指针往下压了(栈是向下生长的),指向的内容也就发生了变化
但是栈指针指向的所以返回的栈指针指向的内容就变了,返回乱码。 string s=fun1(); cout<<ptr<<endl; cout<<s<<endl; return 0; }
-
- 排序
- 冒泡,交换,快排,堆排序,选择排序
- http://www.cnblogs.com/Kobe10/p/5573415.html
- 冒泡:最好n平方,最坏n平方,a[j]<a[j+1]
- 交换:最好n平方,最坏n平方,从头到尾遍历找到最小的进行交换(固定)(a[j]<a[i])
- 选择排序:最好n平方,最坏n平方,每次遍历从头开始遍历找到最小的放在数组首部,然后从第二个开始遍历,依次类推。
- 插入排序:将一个数字插入到已经排序好的有序数组中。最好n平方,最坏n平方。思想:先是找出第一个数,这个数默认是排序的,然后取出第二个数,从后往前比较,小的的交换,依次进行直到头部,然后多次执行同样的操作。
- 快排:最好是lgn,最坏是n平方。快排的空间复杂度是lgn,其他都是常数级别。思想:选出第一个数,然后设置两个指针,指向第二个数和尾部,先是尾部指针移动,再是头部指针移动,最后将x插入中间,然后递归的快排左边和右边。
- 希尔排序:最好是lgn,最坏是n的s次方(1<s<2)。思想:设置一个步长,
- 堆:最好是lgn,最坏也是lgn
- 知道什么是struct中的对齐么?
- http://blog.chinaunix.net/uid-14802518-id-2784907.html
- 内存对齐的原则(http://blog.chinaunix.net/uid-26548237-id-3874378.html)
- (1)数据成员的对齐:对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个暑假成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员自身长度)的倍数;
(2)结构体的对齐:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员大小中,比较小的那个进行;(自己举例)结构体(char,int,short)=12
- (1)数据成员的对齐:对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个暑假成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员自身长度)的倍数;
- extern c‘ 是干什么的?
- extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,
- 此外extern也可用来进行链接指定。
- 也就是说extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目 全非,可能是fun@aBc_int_int#%$也可能是别的,这要看编译器的"脾气"了(不同的编译器采用的方法不一样),为什么这么做呢,因为 C++支持函数的重载啊,在这里不去过多的论述这个问题,如果你有兴趣可以去网上搜索,相信你可以得到满意的解释!
第二,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块活其他模块中使用,记住它是一个声明不是定义!也就是 说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量, 但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。 - C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
- 函数指针和指针函数(const)?
- int *const a;常量指针。a的值不能修改,因为它的地址已经固定了,但是*a是可以修改的。 (地址的内容可以修改)
- const int *a;指针常量,指向常量的指针。*a是不能修改了,指向的是一个常量,但是a是可以修改的,可以给地址重新赋值
- const int const *a;既不可以修改内容,也不可以修改地址。
- 内存管理你懂多少?(包括内存泄漏,野指针知识,非法调用,内存溢出等)
- 内存泄漏:程序中一块没有使用的内存,由于用完没有释放,导致一直存在程序中,但是又不能分给其他程序使用,造成了内存的浪费,它不会及时的发生错误
导致内存泄漏:一般是new或者malloc的时候忘记了free或者delete了
内存泄漏解决方式:VLD - 内存溢出:就是我申请了一块int a[10]的内存,但是我赋值的时候赋值a[11],这就是内存溢出了
- 因素:存取或者复制内存缓冲区的方式不安全。数组越界。是用非类型安全的语言(C/C++)。编译器设置的内存缓冲区太靠近关键数据结构。
char s[]="1234567890"; char d[]="123"; strcpy(d,s);cout<<d<<endl<<s<<endl;输出的是1234567890 567890(15位的数,内存溢出了,栈的机制) - 数组下标越界:这个应该是的问题,数组255大小,但是当a[255]就是256个元素,相当于越界了。
死循环:这个就是字符型的变量大小在0-255之间,所以说i永远不可能大于255的,死循环。
内存泄漏:创建的临时变量,在栈中,应该会由系统自动释放,所以应该是不存在内存泄漏的问题。
内存溢出:通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。此时软件或游戏就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件。
栈溢出:属于缓冲区溢出的一种。栈溢出是由于C语言系列没有内置检查机制来确保复制到缓冲区的数据不得大于缓冲区的大小,因此当这个数据足够大的时候,将会溢出缓冲区的范围。
- 内存泄漏:程序中一块没有使用的内存,由于用完没有释放,导致一直存在程序中,但是又不能分给其他程序使用,造成了内存的浪费,它不会及时的发生错误
- malloc返回什么?怎么用?
- 字符串的操作和面试题
- 替换空格20%
- 打印字符串的所有排列
- 第一个只出现一次的字符
- 翻转单词顺序VS左旋转字符串
- 怎样将整数转换成字符串数,并且不用itoa函数?
- 把字符串转化为数字
- 对称子字符串的最大长度(第一步先是找字串,找到字串之后从串中间开始往两边进行判断是不是对称的,这里要分两种情况,如果字串的长度是奇数的话那么就是以中间一个数为中心;如果字串的长度是偶数的话,那就得以两个数为中心)
- C++的四种类型转换机制
- const_cast,static_cast,dynamic_cast (),reinterpret_cast(ri:ɪnˈtɜ:rprɪt)
- http://blog.csdn.net/it_yuan/article/details/22758561
- static_cast:(静态的类型转换)主要用在继承关系的对象类型(还有一般类型)的转换(不用于指针)。A a;B b; a=static_cast<A>b;(强制转换)
- dynamic_cast:(动态的类型转换:在运行的时候才进行)只能用于有继承关系的对象类型转换(指针和引用)。A a;B b;B*p=&b;A *pp=dynamic_cast<A*>p;
当然有虚函数的类也行。 - const_cast:用于将指针常量转换为普通的常量。const int * p="2"; int * pp= const_cast<int *> p;
- reinterpret_cast:将一个类型的指针转换为另一个类型的指针。double * b=2.0;int *a=reinterpret_cast<double*>b;
- const的作用
- const定义的常量必须赋值初始化,不能另外的初始化。
- const修饰函数的输入参数:当传入的参数是用户自定义的类型,最好是用const引用修饰,可以提高效率。
- const修饰函数的返回值。
- const修饰类的成员函数。
- strcpy函数的编写?
-
char *strcpy(char *strDest, const char *strSrc); { assert((strDest!=NULL) && (strSrc !=NULL)); // 2分 char *address = strDest; // 2分 while( (*strDest++ = * strSrc++) != ‘\0’ ) ;// 2分return address ; // 2分 }
读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了,完美的版本为:
int strlen( const char *str ) //输入参数const 以下是引用片段:
{
assert( str != NULL ); //断言字符串地址非0
int len=0; //注,一定要初始化。
while( (*str++) != '/0' )
{
len++;
}
return len;
}
strcpy返回值的作用。返回的是一个char*,用来支持链式表达,举例如下
- strcpy 函数将strSrc 拷贝至输出参数strDest 中,同时函数的返回值又是strDest。 这样做并非多此一举,可以获得如下灵活性: char str[20]; int length = strlen( strcpy(str, “Hello World”) )
-
- explicit作用
- 这个参数是用来修饰构造函数的,被修饰的构造函数不能发生隐式的类型转换。
- 隐式转换的例子:A a=1;(这个发生了隐式转换,隐式的调用了)http://www.cnblogs.com/this-543273659/archive/2011/08/02/2124596.html
- 隐式转换的例子:A a=1;(这个发生了隐式转换,隐式的调用了)http://www.cnblogs.com/this-543273659/archive/2011/08/02/2124596.html
- 这个参数是用来修饰构造函数的,被修饰的构造函数不能发生隐式的类型转换。
- 为什么使用static_cast而不是使用C中的转换函数?:static_cast更安全。
-
- A* p1 = (A*) &b; // 这句是c风格的强制类型转换,编译不会报错,留下了隐患
- A* p2 = static_cast<A*>(&b); // static_cast在编译时进行了类型检查,直接报错
- A* p3 = dynamic_cast<A*>(&b);
-
- C++中的异常处理机制
- 通常,监测异常情况的程序语句包含在try中。如果try块中发生了异常(也就是错误),则用throw处理。异常由catch捕获,并得到处理。
- 一般一个try不止一个catch(捕获并处理异常),具体哪个由系统来决定
- 构造函数中,成员变量一定要通过初始化列表来初始化的是
- const修饰的成员变量,引用。(这两个都是必须在定义的时候初始化),还有需要初始化的数据成员是对象(继承时调用基类构造函数)
- const修饰的成员变量,引用。(这两个都是必须在定义的时候初始化),还有需要初始化的数据成员是对象(继承时调用基类构造函数)
- auto_ptr和shared_ptr
- auto_ptr动态的管理内存资源的智能指针,当对象不存在的时候它会自动的释放内存,不需要人为的去管理,但是有缺陷,最好是用shared_ptr auto_ptr注意事项:
- auto_ptr不能共享所有权。例如auto_ptr<T> a(new A);auto_ptr<T> b(a);这样a的指针的所有权就归b了,a就不能通过指针去调用函数了。
- auto_ptr不能指向数组
- auto_ptr不能作为容器的成员
- 不能通过复制操作来初始化auto_ptr
std::auto_ptr<int> p(new int(42)); //OKstd::atuo_ptr<int>p = new int(42);//Error
- 不要把auto_ptr放入容器
- shared_ptr能共享所有权,而且很多好处,一般就用这个。
- static关键字的作用
- static的第二个作用是保持变量内容的持久
- static的第三个作用是默认初始化为0(static变量)
- 可以将类中的函数声明为static,只能够访问静态的数据成员和静态的成员函数。
- 隐藏
- 什么是拷贝构造函数
- 它是一个单个参数的构造函数,参数类型是const&的对象的类型。它必须有防止自我赋值的操作,最好的拷贝构造函数如下
- 系统会提供默认的拷贝构造函数和赋值运算符,有时候默认的拷贝构造函数不好使,可以自己定义一个拷贝构造函数。
-
赋值运算符
widger& widget::operator=(const widget & rhs){//一份不安全的=版本 if (this==&rhs)return *this;//这个版本还是存在异常的问题,如果new bitmap出现异常,指针会指向一块已经被delete的部分, delete pb; pb=new bitmap(*rhs.pb); return *this; }
- 深拷贝和浅拷贝
- 浅拷贝:指的是在对象复制时,只是对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。
- 深拷贝:当对象复制的时候,对象中存在动态成员,那么就要进行深拷贝,重新动态的分配空间。
Rect(const Rect& r) { width = r.width; height = r.height; p = new int; // 为新对象重新动态分配空间 *p = *(r.p); }
- 防止默认拷贝函数发生的办法:将默认拷贝构造函数声明为private。
- 哈希表
- 什么是哈希表?
- 哈希表也称散列表。它是通过实值和键值两部分组成。通过关键的键值来访问数据结构中的实值,来提高查找效率。
- 解决碰撞的方法
- 线性探测,二次线性探测,开链法
- 线性探测:比如说插入,当找到的位置不存在那就只能依次向后遍历进行查找,直到找到一个合适的位置插入,如果还咩有就跳到表头进行线性探测。
- 二次线性探测:查找的方式和一次相同,但是每次是前进i平方个位置。
- 开链法:是由一个vector和多个list组成。思路:vector中存放的是一些指针,这些指针指向list,用插入的元素除以vector的大小,余数对应于指针数,将其插入。
- 哈希冲突如何产生
- 有相同的键值但是对应的实值不同插入进来就会产生哈希冲突
- 有相同的键值但是对应的实值不同插入进来就会产生哈希冲突
- 什么是哈希表?
- array和list的区别
- array是连续存储的,list的是线性存储的
- 当数据量很大的时候:array的查找效率肯定是比list要高的
- 当频繁的需要插入和删除的时候:用list
-
1.capacity
指容器在不分配新的存储空间的前提下它最多可以保存多少元素。
2. size
指当前容器已经保存的元素的数目。
在弄清这两个概念以后,很容易懂resize和reserve的区别
vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!
原因如下:
reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。
c.reserve(n)指分配至少能容纳n个元素的内存空间。
resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。
两 个函数的参数形式也有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小, 第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。下面是这两个函数使用例子:
- 虚函数能声明为内联函数吗?为什么
- 不能。内联函数是编译期就已经绑定了的,而虚函数要考虑到多态的性质
- 不能。内联函数是编译期就已经绑定了的,而虚函数要考虑到多态的性质
-
拷贝构造函数作用及用途?什么时候需要自定义拷贝构造函数?
- 深拷贝和浅拷贝
- 深拷贝和浅拷贝
-
二维动态数组的申请和删除
-
size_t row, col; //输入row和col的数值 int **MathTable = new int*[row]; for (int i = 0; i < row; i++) MathTable[i] = new int[col]; //code for (int i = 0; i < row; i++) delete[] MathTable[i]; delete[]MathTable;
-
- 指针和引用的区别
- 指针是一个变量,指向一个地址,引用时一个原变量的别名
- const指针,没有const引用
- 指针可以有多级,而指针只能有一级
- 指针可以为空,引用不能为空
- sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;
- STL中的sort用的什么算法
- 快排。
- 快排。
- #include<>和include""的区别
- include<>先访问的编译器的类库路径的头文
- include""引用的是本地的程序目录的相对路径的头文件。(自己编写的那些头文件就是)
- 构造函数和析构函数的调用顺序
- (有继承关系)构造函数:
1.基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序。
2.成员类对象构造函数。如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。
3.派生类构造函数。 - (无继承关系)析构函数:与构造函数相反
1.对象生命周期结束,被销毁时(一般类成员的指针变量与引用都i不自动调用析构函数);
2.delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
3.对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。 - 没有继承关系的构造函数和析构函数:按照初始化对象的顺序进行构造,按照与构造顺序相反的顺序进行析构。
- (有继承关系)构造函数:
- C++不能重载的运算符
- c++不能重载的运算符有.(点号),::(域解析符),?:(条件语句运算符),sizeof(求字节运算符),typeid,static_cast,dynamic_cast,interpret_cast(三类类型转换符)。---出自C++ primer plus
- c++不能重载的运算符有.(点号),::(域解析符),?:(条件语句运算符),sizeof(求字节运算符),typeid,static_cast,dynamic_cast,interpret_cast(三类类型转换符)。---出自C++ primer plus
- 异常处理中final,finally,finalize
- final为关键字; finalize()为方法; finally为为区块标志,用于try语句中;
- 作用
- final:修饰常量的关键字,他修饰的变量存放在常量池中。
- finally:用于标识代码块,与try进行配合,如果发生异常,这个必定会执行
- finalize:释放对象占用的资源,有时候也是类似于析构函数
- final详解
- final定义基本类型变量时,要求变量初始化必须在声明时或者构造函数中,不能用于其它地方。该关键字定义的常量,除了初始化阶段,不能更改常量的值。
- final定义基本类型变量时,要求变量初始化必须在声明时或者构造函数中,不能用于其它地方。该关键字定义的常量,除了初始化阶段,不能更改常量的值。
- 使用final关键字定义的方法,不能被子类继承
- 一个任何final类无法被任何人继承,这也就意味着此类在一个继承树中是一个叶子类,并且此类被认为是很完美的,不需要进行任何修改(总之是不推荐使用)
- sizeof和strlen对字符数组的一些常见题目
-
#include <stdio.h> #include<stdlib.h> int main(void) { char str[] = {4, 3, 9, 8, 2, 0, 1, 5}; char str1[]="hello"; char str2[10]={1,2,3,4,0}; char *a=str; //这里很明显了,strlen是计算长度的,不把\0包括在内,而0数字带便的就\0,所以第一个数就是5 //sizeof是计算元素的个数的,是计算大小的,他是暴扣\0的,没满的时候 printf("%d\n",strlen(str)); printf("%d\n",sizeof(str)); printf("%d\n",strlen(str1)); printf("%d\n",sizeof(str1)); printf("%d\n",strlen(str2)); printf("%d\n",sizeof(str2)); printf("%d\n",sizeof(a)); }
-
-
构造函数与析构函数是否能为虚函数?
大家都知道析构函数可以是虚函数,构造函数不能为虚函数。为什么构造函数不能为虚函数呢?
1。从存储空间角度
虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
2。从使用角度
虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。
3。从作用
虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
4。
vtable在构造函数调用后才建立,因而构造函数不可能成为虚函数。在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有必要成为虚函数。 -
vector map list set deque stack几大容器的使用区别
1。vector (连续的空间存储,可以使用[]操作符)快速的访问随机的元素,快速的在末尾插入元素,但是在序列中间岁间的插入,删除元素要慢,而且如果一开始分配的空间不够的话,有一个重新分配更大空间,然后拷贝的性能开销.
2。deque(小片的连续,小片间用链表相连,实际上内部有一个map的指针,因为知道类型,所以还是可以使用[],只是速度没有vector快)快速的访问随机的元素,快速的在开始和末尾插入元素,随机的插入,删除元素要慢,空间的重新分配要比vector快,重新分配空间后,原有的元素不需要拷贝。对deque的排序操作,可将deque先复制到vector,排序后在复制回deque。
3。list (每个元素间用链表相连)访问随机元素不如vector快,随机的插入元素比vector快,对每个元素分配空间,所以不存在空间不够,重新分配的情况
4。set 内部元素唯一,用一棵平衡树结构来存储,因此遍历的时候就排序了,查找也比较快的哦。
5。map 一对一的映射的结合,key不能重复。
6。stack 适配器,必须结合其他的容器使用,stl中默认的内部容器是deque。先进后出,只有一个出口,不允许遍历。
7。queue 是受限制的deque,内部容器一般使用list较简单。先进先出,不允许遍历。
1.如果我们需要随机访问一个容器则vector要比list好得多 。
2.如果我们已知要存储元素的个数则vector 又是一个比list好的选择。
3.如果我们需要的不只是在容器两端插入和删除元素则list显然要比vector好
4.除非我们需要在容器首部插入和删除元素否则vector要比deque好。
5.如果只在容器的首部和尾部插入数据元素,则选择deque.
6.如果只需要在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑输入时将元素读入到一个List容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的list容器复制到一个vector容器中。
-
2.简述sizeof和strlen的区别
最常考察的题目之一。主要区别如下:
1)sizeof是一个操作符,strlen是库函数。
2)sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。
3)编译器在编译时就计算出了sizeof的结果。而strlen 函数必须在运行时才能计算出来。并且sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。
4)数组做sizeof的参数不退化,传递给strlen就退化为指针了 - 说说strcpy、sprintf与memcpy三个函数
三个函数的功能分别为:
strcpy:实现字符串变量间的拷贝
sprintf:主要实现其他数据类型格式到字符串的转化Memcpy:主要是内存块间的拷贝
它们的区别有:
(1)操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,目的操作对象是字符串,memcpy 的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。
(2)执行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
-
16.写个小程序确定一个数转化成二进制后是1的位的个数
很久以前就开始流传的一道微软面试题。
- (x)
- countx;
- }
-
19.当一个类C 中没有任何成员变量与成员函数,这时sizeof(C)的值是多少。如果不是零,请解释一下编译器为什么没有让它为零。
一个空类对象的大小是1byte。这是被编译器安插进去的一个字节,这样就使得这个空类的两个实例得以在内存中配置独一无二的地址。
-
20.用变量a给出下面的定义
a) 一个整型数(An integer)
b) 一个指向整型数的指针(A pointer to an integer)
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an
integer)
d) 一个有10个整型数的数组(An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function
that takes an integer as an argument and returns an integer)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型
数( An array of ten pointers to functions that take an integer argument and return an integer )
非常非常经典的一道题,很多笔试面试题是从上述a-h中的一个或者几个,答案如下:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return
an integer
-
24. 虚拟函数与普通成员函数的区别?内联函数和构造函数能否为虚拟函数?
区别:虚拟函数有virtual关键字,有虚拟指针和虚函数表,虚拟指针就是虚拟函数的接口,而普通成员函数没有。内联函数和构造函数不能为虚拟函数。
-
1.面向对象
面向对象的三大特性:封装、继承、多态。
类和对象:类由数据成员和成员函数构成,代表抽象派,玩的就是概念,某种意义上来说是一种行为艺术;而对象是具体的,比如说过年回家和老爹下中国象棋,发现棋盘上少了一对‘象’,那是你爸在告诉你该找“对象”了(单身狗表示选择Go Die)。
封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的类进行信息隐藏。(C++最大的优点:可以隐藏代码的实现细节,使得代码更模块化)
继承:可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展,但是基类的构造函数、复制构造函数、析构函数、赋值运算符不能被派生类继承。(优点是实可以扩展已存在的代码模块类)
多态:一个类实例的相同方法在不同情形有不同表现形式。多态实现的两种方式:将子类对象的指针赋给父类类型的指针或将一个基类的引用指向它的派生类实例。(其中比较重要的是虚函数的使用以及指针或引用)
this指针:一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类的内部,当在类的非静态(前面没加Static)成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,各成员的访问均通过this指针进行。(静态成员是没有this指针的)
-
10.将“引用”作为函数参数有哪些特点?
<1>传递引用给函数和传递指针的效果是一样的
<2>使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作(当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好)
<3>与指针作为函数的参数,需要分配存储单元,且容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参,而引用更容易使用、更清晰
- 14.“引用”与指针的区别是什么?
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传引用和指针的区别。 - 20.重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
从定义上来说:
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义父类虚函数的方法。
从实现原理上来说:
重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译期间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定) - 27. main 函数执行以前,还会执行什么代码?
答案:全局对象的构造函数会在main 函数之前执行。 - 35.简述数组与指针的区别?
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
(1)修改内容上的差别
char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误,运行时错误
(2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof(p) << endl; // 4 字节
计算数组和指针的内存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字节而不是100 字节
} - 38.如何打印出当前源文件的文件名以及源文件的当前行号?
答案:
cout << __FILE__ ;
cout<<__LINE__ ;
__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。 -
40.如何判断一段程序是由C 编译程序还是由C++编译程序编译的?
#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
- (3)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序,这次要求用递归方法进行。 (Autodesk)
- 合并两个链表,非递归实现
-
已知String类定义如下:
class String
{
public:
String(const char *str = NULL); // 通用构造函数
String(const String &another); // 拷贝构造函数
~ String(); // 析构函数
String & operater =(const String &rhs); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
String::~String() { delete []m_data ; } String& String::operator =(const String &rhs) { if ( this == &rhs) return *this ; delete []m_data; //删除原来的数据,新开一块内存 m_data = new char[strlen(rhs.m_data) + 1]; strcpy(m_data,rhs.m_data); return *this ; } String::String(const String &another) { m_data = new char[strlen(another.m_data) + 1]; strcpy(m_data,another.m_data); } String::String(const char *str) { if ( str == NULL ) //strlen在参数为NULL时会抛异常才会有这步判断 { m_data = new char[1] ; m_data[0] = '/0' ; } else { m_data = new char[strlen(str) + 1]; strcpy(m_data,str); } }
- 52.#include<file.h> 与 #include "file.h"的区别?
答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。 -
58.全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
全局变量和局部变量的区别主要在于生存周期不同,全局变量在整个程序生成期间可见,局部变量在自己的作用域内可见。全局变量的内存分配是静态的,位于PE文件在数据区,在main()前由C、C++运行期函数初始化,如果没有初值,会被初始化为0。局部变量的内存分配是动态的,位于线程堆栈中。如果没有初始化的,初值视当前内存内的值而定。
操作系统和编译器从定义变量为变量分配内存时,从变量的定义和存储区域来分别局部变量和全局变量
-
二、什么函数不能声明为虚函数:
一个类中将所有的成员函数都尽可能地设置为虚函数总是有益的。
设置虚函数须注意:
1:只有类的成员函数才能说明为虚函数;
2:静态成员函数不能是虚函数;
3:内联函数不能为虚函数;
4:构造函数不能是虚函数;
5:析构函数可以是虚函数,而且通常声明为虚函数。 -
77.不能做switch()的参数类型是?switch的参数不能为实型
- 82.do……while和while……do有什么区别?
答 、前一个循环一遍再判断,后一个判断以后再循环 - 89.对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
答 、c用宏定义,c++用inline -
97.关键字volatile有什么含意 并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
} - 102.编写算法,从10亿个浮点数当中,选出其中最大的10000个
用外部排序,在《数据结构》书上有
《计算方法导论》在找到第n大的数的算法上加工 (注意:先将数据进行分割成数据量小的一些文件,如1000000个数据为一个文件,然后将每个文件数据进行排序,用快速排序法排序,然后使用K路合并法将其合并到一个文件下,取出排序好的最大的10000个数据) - 字符串的逆序
-
#include <iostream> #include <string> using namespace std; int main() { string s, r; // 声明字符串 cin >> s; // 输入字符串 for (int i = 0; i < s.length (); i++) { r = s [i] + r; } cout << r; // 输出字符串 return 0; }
-
#include <cstring> using namespace std; int main() { // 输入字符串。 char s [300]; scanf("%s",s); for (int i = 0; i < strlen(s); i++) { cout << *(s + strlen(s) - i - 1); } cout << endl; return 0; }
- 还可以用栈进行实现
-