大端小端
大端小端的区别,编写辨别函数,编写转换函数
区别:
大端与小端是用来描述多字节数据在内存中的存放顺序,即字节序。大端(Big Endian)是指低地址端存放高位字节,小端(Little Endian)是指低地址端存放低位字节。
辨别函数:
char 类型大小:一个字节;
short 类型大小:双字节;
利用联合类型 或者 指针强制转化
- 联合类型
1.bool IsBigEndian(){
2. union NUM{
3. int a;
4. char b;
5. }num;
6. num.a = 0x01;
7. if( num.b == 0x01 )
8. {
9. return false;
10. }
11. return true;
12.}
- 指针强制转化
1.
2. short t= 0x0102;
3. char* p = (char*)&t;
4. if(*p == 01)
5. {
6. cout<<”大端!\n”<<endl;
7. }else
8. {
9. cout <<”小端!\n”<<endl;
10. }
转换函数:
- 移位法
1. uint32_t reversebytes_uint32t(uint32_t value){
2. return (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 |
3. (value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24;
4. }
1. // 先将64位的低32位转成小端模式,再将64位的高32位转成小端模式
2. // 在将原来的低32位放置到高32位,原来的高32位放置到低32位
3. uint64_t reversebytes_uint64t(uint64_t value){
4. uint32_t high_uint64 = uint64_t(reversebytes_uint32t(uint32_t(value)));//低32位转成小端
5. uint64_t low_uint64 = (uint64_t)reversebytes_uint32t(uint32_t(value >> 32));//高32位转成小端
6. return (high_uint64 << 32) + low_uint64;
7. }
- 利用栈:
1. uint64_t rever(uint64_t rhs)
2. {
3. stack<char> stack_char;
4. char* p = (char*)&rhs;
5. for (auto i(0); i < 8;i++)
6. {
7. cout << (int)*p << endl;
8. stack_char.push(*p);
9. p++;
10. }
11. p = (char*)&rhs;
12. for (auto i(0); i < 8; i++)
13. {
14. *p = stack_char.top();
15. cout << (int)*p << endl;
16. p++;
17. stack_char.pop();
18. }
19. return rhs;
20. }
内存泄漏
C++内存泄漏的检测和解决方法
内存泄漏一般是指堆内存的泄漏,也就是程序在运行过程中动态申请的内存空间不再使用后没有及时释放,导致那块内存不能被再次使用。
更广义的内存泄漏还包括未对系统资源的及时释放,比如句柄、socket等没有使用相应的函数释放掉,导致系统资源的浪费。
解决方法:
使用智能指针:将分配的资源交给资源管理对象,以对象管理资源
使用内存检测工具:VS等
重载new和delete,以链表的形式自动管理分配的内存:
1、重载new()和delete()操作符:
1. #ifdef _DEBUG
2. inline void * __cdecl operator new(unsigned int size, const char *file, int line) { };
3. inline void __cdecl operator delete(void *p) { };
4. #endif
5.
6. #ifdef _DEBUG
7. inline void * __cdecl operator new(unsigned int size, const char *file, int line)
8. {
9. void *ptr = (void *)malloc(size);
10. AddTrack((DWORD)ptr, size, file, line);
11. return(ptr);
12. };
13. inline void __cdecl operator delete(void *p)
14. {
15. RemoveTrack((DWORD)p);
16. free(p);
17. };
18. #endif
2、自动的让所有的接受一个参数的new操作符调用接受三个参数的new操作符:
1. #ifdef _DEBUG
2. #define DEBUG_NEW new(__FILE__, __LINE__)
3. #else
4. #define DEBUG_NEW new
5. #endif
3、Removetrack中可以使用哈希表记录
重载NEW函数
如何重载全局new(),delete()函数
注意的是:需要重载new[] delete[]函数,即其实需要重载四个函数
指针的大小
指针的大小是多少?哪些类型长度与系统位数相关?
指针和引用
指针和引用的区别,函数传入指针参数并返回,与函数传入引用参数并返回有什么区别?
例:以下代码有什么问题?怎么修改?
1. void fun(int* a)
2. {
3. a = new int[100];
4. cout << a << endl;
5. }
6.
7. int main()
8. {
9. int *a;
10. fun(a);
11. a[1]= 0;
12. fun(a);
13. }
答:函数内的变量在作用域内有效,作用域外无效。运行完,变量都会释放,变量所占的空间也会被收回,所以需要修改程序如下:
14. void fun(int* &a)
15. {
16. a = new int[100];
17. cout << a << endl;
18. }
19. int main()
20. {
21. int *a;
22. fun(a);
23. a[1]= 0;
24. fun(a);
25. }
相同点:
都是地址的概念:
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
- 指针是一个对象实体,而引用仅是个别名;
- 引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;
- 引用没有const,指针有const,const的指针不可变;
- 引用不能为空,指针可以为空;
- “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
- 指针和引用的自增(++)运算意义不一样;
- 引用是类型安全的,而指针不是 (引用比指针多了类型检查)
在以下情况下你应该使用指针,一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。
寻找最长的回文子串:
- 暴力循环
- 动态规划
倒序输出字符串
- 递归
- 三步翻转后输出
- 利用栈结构
寻找字符串第一个不重复的元素:
- 扫描数组,用哈希表map记录出现次数
再扫描数组,返回第一个哈希表值为1的元素 - 扫描数组,使用26大小的数组,利用下标作为哈希值
- 扫描数组,map计数:
如果当前元素的计数值为1,加入到一个双向链表的队尾;map保留了结点在链表中的位置
如果当前元素的计数为2,从链表中移除
如果当前元素的计数大于2,只增加计数值,不操作链表
结束后双向链表头即为所求
局部静态变量内存
char* function(const char* )函数得到输入字符串的倒序字符串,此函数被频繁调用,则应如何管理内存?
声明一个足够大的局部静态字符数组(全局变量,全局静态变量也行),用于存贮被倒序的字符+“\0”结尾字符。
机器学习专场:
- 简述人脸识别算法
- BP神经网络公式推导
- 聚类算法介绍
- 简述深度学习
求差/交集合
例题:已知两个文件A、B,分别存储qq号码,求A、B文件的交集,结果存于A文件。
答:
- 双重循环
- 分别对A,B文件排序,然后归并:
vector<int> IntersectionSet(vector<int> set1,vector<int> set2){
int size1 = set1.size();
int size2 = set2.size();
// 排序
sort(set1.begin(),set1.end());
sort(set2.begin(),set2.end());
int i = 0,j = 0;
vector<int> result;
// 比较
while(i < size1 && j < size2){
// 相同的加入集中
if(set1[i] == set2[j]){
result.push_back(set1[i]);
++i;
++j;
}//if
// 值小的跳过
else if(set1[i] < set2[j]){
++i;
}//else
else{
++j;
}//else
}//while
return result;
}
利用哈希表,先用A建立哈希表,再用B查询哈希表:
哈希函数->数组下标:
把集合(元素是不重复的)中的元素作为数组下表的索引,先遍历A,H[A(i)] = 1; 然后遍历B,若H[B(i)] == 1,则为交集元素。
注:memset是以字节为单位,初始化内存块
1. vector<int> IntersectionSet(vector<int> set1,vector<int> set2){
2. int size1 = set1.size();
3. int size2 = set2.size();
4. // 计算最大值元素
5. int max = 0;
6. for(int i = 0;i < size1;++i){
7. if(max < set1[i]){
8. max = set1[i];
9. }//if
10. }//for
11. for(int i = 0;i < size2;++i){
12. if(max < set2[i]){
13. max = set2[i];
14. }//if
15. }//for
16. int* hash = new int[max+1];
17. // 初始化为0
18. memset(hash, 0, sizeof(int)*(max+1));
19. vector<int> result;
20. for(int i = 0;i < size1;++i){
21. hash[set1[i]] = 1;
22. }//for
23. // 交集
24. for(int i = 0;i < size2;++i){
25. if(hash[set2[i]] == 1){
26. result.push_back(set2[i]);
27. }//if
28. }//for
29. delete hash;
30. return result;
31. }
- 数组->位图
求链表差集合
例:已知集合A和B的元素分别用不含头结点的单链表存储,函数difference()用于求解集合A与B的差集,并将结果保存在集合A的单链表中。
例如,若集合A={5,10,20,15,25,30},集合B={5,15,35,25},完成计算后A={10,20,30}。
答:
注:链表作为参数时,传递的是指向头指针的指针或引用,以便链表头指针可以更改。
1. void Difference(ListNode** LA,ListNode* LB){
2. // 容错处理
3. if(*LA == NULL || LB == NULL){
4. return;
5. }
6. ListNode *pa,*pb,*pre,*node;
7. pa = *LA;
8. pre = NULL;
9. bool isDelete;
10. // 遍历LA
11. while(pa){
12. pb = LB;
13. isDelete = false;
14. while(pb){
15. // 删除pa
16. if(pa->val == pb->val){
17. isDelete = true;
18. // 头元素
19. if(pre == NULL){
20. *LA = pa->next;
21. pa = *LA;
22. }//if
23. else{
24. node = pa;
25. pre->next = pa->next;
26. delete node;
27. pa = pre->next;
28. }
29. break;
30. }//if
31. pb = pb->next;
32. }//while
33. // 如果有删除pa跳到pa->next
34. if(!isDelete){
35. pre = pa;
36. pa = pa->next;
37. }
38. }//while
39. }
static关键字
c++的static关键字的作用
从ELF文件、链接过程解释作用域:
http://blog.csdn.net/zhd19910223/article/details/43489089
每个可重定位目标模块m都有一个符号表:存放函数和变量的名称,地址,大小,类型等等。
不同模块或不用函数的static变量在链接过程中会被修饰为不同的符号名,编译器在.Data节(已初始化全局变量)和.bss节(未初始化全局变量)中为每个定义分配空间,并在符号表中创建一个有唯一名字的本地链接器符号。
在链接过程中,链接器上下文中有三种不同的符号:
1. 模块m定义的全局符号:非静态函数和全局变量;
2. 模块m引用的其他模块定义的全局符号;
3. 模块m定义和引用的本地符号:static修饰的函数和全局变量。
编译器在.Data(已初始化全局变量)和.bss(未初始化全局变量)中为每个定义分配空间,并在符号表中创建一个有唯一名字的本地链接器符号
(符号表不包含对本地非静态程序变量,即局部变量的任何符号,这些符号在栈中被管理)
C中:
全局变量
局部变量
函数:函数的定义和声明默认是extern的,static函数只在声明的文件中可见。好处是名字不冲突且函数不会被其他文件所用。
C++中:
成员变量
成员函数
extern关键字
const关键字,#define和const区别
编译期常量和运行期常量
两者都是常量,它们的区别,可以用是否具备内存空间来区分。
编译期常量, 比如 #define MAX 128,这个MAX就是编译期常量,没有对应的内存空间,在编译时候,所有的MAX都被128这个值代替。
运行期常量,比如const int x=128;就是一个运行期常量,分配内存空间,但是其值不允许改变。
常量折叠
所谓“常量折叠”是C++中编译器的一种优化手段,对于上面的const int x = 128,直接给已知值的,编译器一般不为x分配内存空间,而是将它保存在符号表中。
这样导致的结果就是const int x=128; 类似这样的定义,产生的结构和define一样, 出现x的地方直接被128这个值代替了。
1 const int j = 10;
2 const int i = j;
3 const int * p = &j;
4 int g[j];//合法:符号替换
5 int k[i];//也合法:两次符号替换
6 int z = j;//为j分配内存
6 int *p = (int*)&j;
7 *p = 9;
8 cout <<j<<endl;//j的值为9,输出却为10.
在下面情况下const定义的常量会分配内存
1)当用extern修饰const变量将强制为其分配内存,因为extern表示采用外部链接,因此其必须有某个地址保存其值。
2)等取x的地址时,会强制分配内存。
3)用static 修饰时,应该也要分配内存。
对于字符串. 类似 const char *a=”abc”; 这种,同样是不能修改的,不过原因就不再是上面那个,而是因为这个 “abc” 在编译之后是放在程序的”常量段”,这部分是执行文件的一部分,运行期间不可修改,如果强制修改,就会出现内存读写错误。
- 宏替换会多次分配内存,const则不会
#define PI 3.14
Const double Pi = 3.14
double i = PI;//宏替换,产生临时变量,分配内存
double j = PI;//宏替换,产生临时变量,分配内存
迭代器失效问题:
C++primer P315
vector迭代器的失效情况:
1.当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。
2.当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效。
3.当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。
deque迭代器的失效情况:没搞清楚
STL源码解析:
(STL 中deque的数据结构与具体实现:STL源码解析P146)
C++primer:
1.在deque容器插入元素会使得任何迭代器失效。
2.在除首尾位置删除元素,所有迭代器失效
3.在其首部或尾部 删除元素则只会使指向被删除元素的迭代器失效。
List/set/map:
1.删除时,指向该删除节点的迭代器失效
注:deque指针/引用失效情况:
deque<int> ideq(20,0);
for (auto i(0); i < ideq.size();i++)
{
ideq[i] = i;
}
auto iter_14 = find(ideq.begin(), ideq.end(), 14);
int * p = &*ideq.begin();
cout << *p << endl;
ideq.insert(iter_14, 21);
cout << *p << endl;
auto iter_4 = find(ideq.begin(), ideq.end(), 4);
ideq.insert(iter_4, 21);
cout << *p << endl;
输出:
0
0
1
0-20的队列,插入新元素在14,将导致14-20元素移动;0-14没有移动:P没有失效
插入新元素在4,将导致0-4元素移动;5-20没有移动:P失效,指向了1
博弈论智力题
例:桌子放硬币,硬币不重叠,最后一个下硬币的人赢,如何赢?
例:三个碗一个球,随机选择一个,从剩下两个中拿走一个一定是空的碗后,问你换不换?
计算机网络
time_wait的危害,三次握手,四次断开
析构顺序
定义在全局的对象,定义在堆中对象,定义在静态局部变量对象,定义在栈中的对象,析构函数的析构顺序
40. C c;
41. void main()
42. {
43. A*pa=new A();
44. B b;
45. static D d;
46. delete pa;
47. }
全局变量和静态局部变量时从 静态存储区中划分的空间, 二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。答:A B D C
new int[10]跟new int10区别
int * p = new int [10]; 分配了空间并将空间地址给p。
int *p = new int 10; 分配了空间并进行默认初始化:都是0,地址给p。
求二叉树的翻转(镜像问题)
递归
非递归
红黑树,普通二叉树,AVL树,完全二叉树的区别
平衡二叉树(AVL)
AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树。
一棵AVL树满足以下的条件:
1> 它的左子树和右子树都是AVL树
2> 2>左子树和右子树的高度差不能超过1
红黑树
红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单
完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
满二叉树
满二叉树是完全二叉树的特例,除最后一层无任何子节点外,每一层上的所有结点都有两个子结点
Hash表、hash碰撞
哈希表结构有两个重要的参数, 容量大小(Capacity)和负载因子(LoadFactor). 两者的乘积 Capacity * LoadFactor决定了哈希表rehash的触发条件.
hash碰撞
解决碰撞的方法:
1. 开链接:(哈希桶)
2. 开放寻址:
线性探测法:
若当前key与原来key产生相同的哈希地址,则当前key存在该地
址之后没有存任何元素的地址中
平方探测法:
若当前key与原来key产生相同的哈希地址,则当前key存在该地
址后偏移量为(1,2,3…)的二次方地址处
双散列
3. 再哈希
例:
已知一个线性表(38,25,74,63,52,48),采用的散列函数为H(Key)=Key%7,将元素散列到表长为7的哈希表中存储。若采用线性探测的开放定址法解决冲突,则在该散列表上进行等概率成功查找的平均查找长度为 __ ;
若利用拉链法解决冲突,则在该散列表上进行等概率成功查找的平均查找长度为 __
答:
38 25 74 63 52 48 mod 7分别是 3 4 4 0 3 6
所以采用线性探测的开放定址法解决冲突,表为:
63,48, ,38,25,74,52
找38,1次
找25,1次
找74,2次
找63,1次
找52,4次
找48,3次
所以成功查找的平均长度为(1+1+2+1+4+3)/6=2
拉链法
建立的为:
63,,,38 –>52,25->74, ,48
找38,1次
找25,1次
找74,2次
找63,1次
找52,2次
找48,1次
所以成功查找的平均长度为(1+1+2+1+2+1)/6=1.333
Trie树
(字典树、单词查找树、键树)
利用字符串的公共前缀降低查询时间,查询效率比哈希表高:计算 hash 的时候就肯定会是O(k),而且还有碰撞之类的问题。
基本性质可以归纳为:
(1)根节点不包含字符,除根节点意外每个节点只包含一个字符。
(2)从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
(3)每个节点的所有子节点包含的字符串不相同。
例:
统计1万个单词中出现频率最高的10个单词:
答:
先用字典树统计每个单词出现的次数,边建树边查询,节点保留次数信息。时间复杂度:O(nl)
查询完成后,将利用节点建立大小为10的最小堆。时间复杂度:O(nlg10)
tcp/udp区别
基于udp如何实现可靠传输说了一遍(网上有教程),然后udp啥一般用于视频传输,丢包几乎不影响,tcp有拥塞避免,流量控制,慢启动,快速回复算法,什么的都说了一通
4万+

被折叠的 条评论
为什么被折叠?



