常见问题总结

C++

1. 实现memcpy, strcpy

如果目标地址大于源地址,先拷贝高位源地址;
如果目标地址小于源地址,先拷贝低位源地址。
memcpy基本原则
 1 void mymemcpy(void* dst, const void* src, size_t num) {
 2     assert(src != NULL && dst != NULL); 
 3     const char* psrc = (const char*)src; 
 4     char* pdst = (char*) dst;
 5     if (pdst > psrc && pdst < psrc + num) {
 6         psrc = psrc + num - 1;
 7         pdst = pdst + num - 1;
 8         while (num--)
 9             *pdst-- = *psrc--;
10         }
11     } else {
12         while (num--) {
13             *pdst++ = *psrc++;
14         }
15     }
16 }
17 
18 /*
19     易错点:
20 1. 函数接口:用void*比较通用一些;而且,src要加上const。
21 2. 判空是个很重要的考察点!这里用assert实现;
22 3. 下面对指针从void*转换到char* 和const char*;
23 4. 如果对size_t进行操作时一定要注意,size_t是unsigned,它降到0之后再减一就变成INT_MAX了,可不是-1!
24 */
mymemcpy

follow up:如何优化?

默认的memcpy是逐字节拷贝;
优化方法是,不用一个字节一个字节的拷贝;
可以多字节拷贝,或者整块的拷贝,也可以是用int指针拷贝(一个int占4字节)。
如果用int指针拷贝,需要分别计算一下

wordNum = num / 4; 
slice = num % 4;
优化

 

拓展:strcpy等一系列经典函数实现   ref       ref2        ref3       ref4

除了memcpy外,其他函数在实现时可以不用考虑内存覆盖问题。具体可以和interviewer沟通。

 1 char* strcpy(char* dst, const char* src) {
 2     assert(dst != NULL && src != NULL);
 3     char* pdst = dst;
 4     const char* psrc = src;
 5     while (*psrc != '\0') {
 6         *pdst++ = *psrc++;
 7     }
 8     *pdst = '\0';
 9     return dst;
10 }
strcpy(未考虑内存覆盖)
 1 char* mystrcpy(char* dst, const char* src) {
 2     assert(dst != NULL && src != NULL);
 3     int size = strlen(src) + 1; //
 4     const char* psrc = src;
 5     char* pdst = dst;
 6     if (psrc < pdst && psrc + size > pdst) {
 7         psrc += size - 1;
 8         pdst += size - 1;
 9         while (size--) {
10             *pdst-- = *psrc--;
11         }
12     } else {
13         while (size--)     {
14             *pdst++ = *psrc++;
15         }
16     }
17     return dst;
18 }
19 
20 /*
21     需要注意的是,计算size时应该在strlen(src)基础上+1,因为要把字符串末尾的'\0'计算进去。
22     有返回值的原因,是为了支持链式表达式,比如:
23     int iLength=strlen(strcpy(strA,strB));
24 
25 http://www.cnblogs.com/kedebug/archive/2012/12/23/2829945.html
26 */
strcpy
 1 char* mystrncpy(char* dst, const char* src, size_t size) {
 2     assert(dst != NULL && src != NULL);
 3     char* pdst = dst;
 4     const char* psrc = src;
 5     size_t i = 0;
 6     while (i++ < size && (*pdst++ = *psrc++) != '\0');
 7     if (*pdst != '\0') {
 8         *pdst = '\0';
 9     }
10     return dst;
11 }
strncpy
 1 char* strcat(char* dst, const char* src) {
 2     assert(dst != NULL && src != NULL);
 3     char* pdst = dst;
 4     const char* psrc = src;
 5     while (*pdst != '\0') {
 6         pdst++;
 7     }
 8     while (*psrc != '\0') {
 9         *pdst++ = *psrc++;
10     }
11     *pdst = '\0';
12     return dst;
13 }
strcat
1 int strcmp(const char* str1, const char* str2) {
2     assert(str1 != NULL && str2 != NULL);
3     while (*str1++ == *str2++) {
4         if (*str1 == '\0') {
5             return 0;
6         }
7     }
8     return *str1 - *str2; 
9 }
strcmp

 

2. STL中vector的实现及原理

  1 // Vector.h
  2  
  3 #ifndef VECTOR_H
  4 #define VECTOR_H
  5  
  6 template <typename Object>
  7 class Vector
  8 {
  9 public:
 10     explicit Vector(): objects(0), theSize(0),theCapacity(0) {}
 11  
 12     explicit Vector(int initSize)
 13         : theSize(initSize), theCapacity(initSize + SPARE_CAPACITY)
 14     {
 15         objects = new Object[theCapacity]; 
 16         for(int i=0; i<initSize; ++i)
 17             objects[i] = 0;
 18     }
 19  
 20     Vector(const Vector & rhs)   
 21     { operator=(rhs); }
 22  
 23     ~Vector()
 24     { delete [] objects; }
 25  
 26     const Vector & operator= (const Vector & rhs)
 27     {    
 28         if(this != &rhs)      // check for aliasing
 29         {
 30             delete [] objects;
 31             theSize= rhs.size();
 32             theCapacity = rhs.capacity();
 33  
 34             objects = new Object[ capacity() ];
 35             for(int k = 0; k < size(); ++k)
 36                 objects[k] = rhs.objects[k];
 37         }
 38         return *this;
 39     }
 40  
 41     void resize(int newSize)
 42     {
 43         if(newSize > theCapacity)
 44             reserve(newSize * 2 + 1);
 45         theSize = newSize;
 46     }
 47  
 48     void reserve(int newCapacity)
 49     {
 50         if(newCapacity < theSize)
 51             return;
 52  
 53         Object * oldArray = objects;
 54  
 55         objects = new Object[ newCapacity ];
 56         for(int k = 0; k < theSize; ++k)
 57             objects[k] = oldArray[k];
 58  
 59         theCapacity = newCapacity;
 60  
 61         delete [] oldArray;
 62     }
 63  
 64     Object & operator[] (int index)
 65     {
 66         return objects[index];
 67     }
 68     const Object & operator[] (int index) const
 69     {
 70         return objects[index];
 71     }
 72  
 73     bool empty() const
 74     {
 75         return size() == 0;
 76     }
 77     int size() const
 78     {
 79         return theSize;
 80     }
 81     int capacity() const
 82     {
 83         return theCapacity;
 84     }
 85  
 86     void push_back(const Object & x)
 87     {
 88         if(theSize == theCapacity)
 89             reserve(2 * theCapacity + 1);
 90         objects[ theSize++ ] = x;
 91     }
 92     void pop_back()
 93     {
 94         theSize--;
 95     }
 96  
 97     const Object & back() const
 98     {
 99         return objects[theSize-1];
100     }
101  
102     typedef Object * iterator;
103     typedef const Object * const_iterator;
104  
105     iterator begin()
106     {     return &objects[0]; }
107     const_iterator begin() const
108     {     return &objects[0]; }
109     iterator end()
110     {     return &objects[size()]; }
111     const_iterator end() const
112     {     return &objects[size()]; }
113  
114     enum{ SPARE_CAPACITY = 16 };
115 private:
116     int theSize;
117     int theCapacity;
118     Object * objects;
119 };
120  
121 #endif
vector

ref1        ref2:Weiss<Data Structure in C++> Ch3.4              ref3

 

拓展:

2.1 

vector已经申请的内存空间并不会缩减,即使调用clear()函数,也只是清空vector的所有元素,真正占用的内存空间也不会减少。(因此,先在vector里插入1w个元素再挨个删去直到剩10个元素,vector占用的内存空间还是维持在1w个元素时的状态。)

解决办法:
vector<int>(nums).swap(nums);
类似地,
string(s).swap(s);

原理:
先创建一个临时拷贝并利用拷贝构造函数对其初始化,值得注意的是,使用拷贝构造函数时其容量是尽可能小的符合所需数据的。紧接着将该拷贝与原先的vector v进行交换(swap)。执行交换后,临时变量会被销毁,内存得到释放。此时的v即为原先的临时拷贝,而交换后的临时拷贝则为容量非常大的vector(不过已经被销毁)

http://blog.jobbole.com/37700/
《Effective STL》-Item17
http://www.cnblogs.com/summerRQ/articles/2407974.html
如何释放掉vector的多余内存

 map,set,deque... 

 

3. 实现一个string类

 

4. const的用法

1. const用于指针时:

const在星号左侧,则const修饰指针所指向的变量,即指针指向的是常量;

const在星号右侧,则const修饰指针本身,即指针本身是常量。

可参考ref1的例子。
ref1: http://blog.csdn.net/wen294299195/article/details/6857395


2. const用于函数时:

若const在函数名之后,说明该函数不能修改对象中的数据。如果有特例需要让该函数修改的话,可以把类中的那几个特例成员变量用mutable修饰。ref

若const在函数名之前,则作用于返回值,意味着该函数返回的值只能被读取,而不能被修改。

参考ref2的例子。
ref2: http://www.iteblog.com/archives/214
const的用法

 

拓展: 

4.1 const,volatile,mutable对比

 

5. 讲一下智能指针的原理,并实现一个智能指针。

 follow up: 垃圾回收机制。

6. 运算符重载

 

7. 实现一个单例模式类

follow up: 如何定义只能在上生成的对象? 只能在上生成的呢?

8. struct对齐问题

对齐规则:

1)在没有#pragma pack(n)的情况下,struct内各个成员的对齐系数自己的长度

2)在有#pragma pack(n)的情况下,struct内各个成员的对齐系数为 min(n, 自己的长度) .

3)struct整体的对齐系数为struct内各成员对齐系数的最大值。

4)struct内每个成员相对于struct首地址的offset都是该成员自身对齐系数的整数倍(否则要补全);而且,struct整体也要保证占用的总大小是struct整体对齐系数的整数倍(末尾不够要补全)。

 

9. C++易混淆概念

 

10. C++虚函数原理及虚继承

 

11. C++对象内存模型

Q: 画一下有虚函数的情况下内存布局大概是什么样的?

 ref1    ref2(很详细!)

 

12. C++ this指针,动态绑定和静态绑定,空指针调用成员函数

看一下这几篇文章: ref1     ref2      ref3      ref4      ref5

 

 

 

static关键字相关   看一下 ref

静态成员函数不可以调用类的非静态成员,因为静态成员函数不含this指针。【注意:是的,不是随便一个非静态变量】

局部静态变量的构造是线程安全的。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

C++零碎知识点汇总

 

其他C++常见问题:

何海涛总结5篇C++面试题: ref1   ref2   ref3   ref4   ref5

http://wenku.baidu.com/view/ad50b2d765ce05087632138c.html

http://blog.csdn.net/worldwindjp/article/details/18909079

http://www.cnblogs.com/wuchanming/p/4367398.html

http://blog.csdn.net/chhuach2005/article/details/40322895

 =======================================================================================================================

概率题 reference

1. 给N张扑克牌和一个随机函数,设计一个洗牌算法

2. 如何等概率地从n个数中随机抽出m个数?

follow up: 如果上题中n大小不确定,如何做?

3. 给定一个能够生成0,1两个数的等概率随机数生成器”,如何生成⼀个产生0,1,2,3的等概率随机数生成器?

  3.1 如何用rand7生成rand9?

  3.2 有1枚硬币,以p的概率产生正面,以1-p的概率产生背面,如何利用它产生一个0.5概率的生成器?

4. A,B,C三人轮流扔硬币,第一个扔到正面的人算赢,问三个人赢的概率分别为多大?

5. A有n个硬币,B有n+1个硬币,谁丢的正面多谁赢,问A不输的概率?

6. 一个机器人在原点,右边有一个距离为k的点,机器人以p的概率右移一步,1-p概率左移一步, 问经过M步机器人处于k点的概率?

7. 扔硬币直到连续两次出现正面,求扔的期望次数

 

ref1里面有一些概率题总结的不错。

ref1: http://noalgo.info/414.html

=======================================================================================================================

操作系统  reference

 1 1. Linux中线程互斥/同步有哪几种方式?
 2 
 3 2. 同样可以实现互斥,互斥锁和信号量有什么区别?
 4 
 5 3. 请用普通的互斥锁编程实现一个读写锁
 6 
 7 4. 编程产生三个线程ABC,并让它们顺次打印ABC
 8 
 9 5. 死锁是怎么产生的?如何避免?
10 
11 6. Linux中进程通信有哪些方式?
12 
13 7. Linux中进程空间布局
14 
15 8. Linux内存分配原理
16 
17 9. malloc函数实现原理
18 
19 10. 使用mmap读写文件为什么比普通读写函数要快?
20 
21 11. 静态链接库、动态链接库原理
22 
23 12. Linux中signal实现原理
View Code

 

 

Linux常用命令/知识: ref

 

 

 

1. 进程和线程有什么区别和联系

2. 内存管理

伙伴系统

进程间通信

实现malloc/ malloc原理   ref1     ref2      ref3

 键盘上敲击一下键/按一下鼠标,后面都有哪些操作?

缓存,LRU算法,进程、线程的概念。

进程调度算法、文件系统。

<多线程编程>

 =======================================================================================================================

海量数据

1. 100亿个整数,如何找到中位数

内存足够的情况: 可以使用类似quick sort的思想进行[QuickSelection算法],均摊复杂度为O(n),算法思想如下:
• 随机选取一个元素,将比它小的元素放在它左边,比它大的元素放在右边
• 如果它恰好在中位数的位置,那么它就是中位数,可以直接返回
• 如果小于它的数超过一半,那么中位数一定在左半边,递归到左边处理
• 否则,中位数一定在右半边,根据左半边的元素个数计算出中位数是右半边的第几大,然后递归到右半边处理
内存不足的情况:
方法一:二分法
思路:一个重要的线索是,这些数都是整数。整数就有范围了,32位系统中就是[-2^32, 2^32- 1], 有了范围我们就可以对这个范围进行二分,然后找有多少个数小于Mid,多少数大于mid,然后递归,和基于quicksort思想的第k大方法类似【即QuickSelection算法】 
方法二:分桶法

思路:化大为小,把所有数划分到各个小区间,把每个数映射到对应的区间里,对每个区间中数的个数进行计数,数一遍各个区间,看看中位数落在哪个区间,若够小,使用基于内存的算法,否则继续划分。

 =======================================================================================================================

网络

TCP/IP协议相关

TCP和UDP的区别? ref

 

三次握手,四次挥手 ref

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

因为tcp是全双工模式,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,但是己方的数据可能还没有发完,有可能在发送对FIN的ACK后再发送一会数据,然后才发FIN。所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。

 

关于tcp中time_wait状态的4个问题: ref    ref2    ref3   ref4

 

 

 

http协议相关

 从输入 URL 到页面加载完的过程中都发生了什么事情

拓展:参考下这篇文章,非常好。

其他网络面试题

 

 

 

 

http://www.nowcoder.com/discuss/1937?type=&order=0&pos=6&page=1?from=wb    【这个总结非常好!认真看一下!】

 http://www.cnblogs.com/obama/p/3292335.html

 

<网络编程>

IO复用 summarize

epoll水平触发,边缘触发

select与epoll

 用户态与内核态,以及如何切换

 

ref0: 这个博主写的一系列网络编程文章都特别好(条件变量,epoll)。多看一下

 

reference 这篇文章对网络编程的知识点总结的非常好!认真学习一下

 

epoll面试相关

http://blog.csdn.net/tom555cat/article/details/24870469

http://www.cppblog.com/peakflys/archive/2012/08/26/188344.html

 http://blog.csdn.net/lianxiang_biancheng/article/details/9025881 【看一下这篇里面epoll源码,让人快速理解epoll流程】

 

nginx入门   reference

 

=======================================================================================================================

智力题

1. 25匹马,5个跑道,最少比多少次能找到前3名

 

拓展:找前5名呢?   ref2     ref

 

2. 

 

 =======================================================================================================================

算法和数据结构

1.常见排序算法复杂度、稳定性

选择排序是每次遍历一遍剩余元素,从中选出最小的与前面已排好序的元素的下一位置进行swap。最优情况下复杂度是O(n^2), 平均和最坏复杂度也是O(n^2)。

冒泡和插入排序的最优复杂度是O(n), 平均和最坏都是O(n^2)。

归并排序和堆排序的最优、平均、最坏复杂度都是O(nlgn)。

快速排序最坏情况下是O(n^2), 平均和最优都是O(nlgn).空间复杂度是O(lgN)(因为需要每次记录pivot的位置)。

稳定性:

不稳定:快速排序、堆排序、选择排序、希尔排序

稳定:归并排序、基数排序、冒泡排序、插入排序。

 

希尔排序是按照不同步长对元素进行插入排序。

ref:  ref1   ref2

=======================================================================================================================

ref: http://www.nowcoder.com/ta/nine-chapter?page=1

 

posted @ 2015-08-02 16:19  Ryan in C++  阅读(1038)  评论(0编辑  收藏  举报