1、char *GetMemory()
{
char p[]="hello world";
return p;
}
void main()
{
char *p = GetMemory();
printf(p);
}
出自林锐的面试题。问题出在GetMemory返回了指向局部对象(堆栈)的指针, 调用printf时会破坏掉p指向的内容,从而导致输出的不确定性。
如果改成:
char *GetMemory()
{
char *p="hello world";
return p;
}
就能正常输出“hello world”, 它们之间的区别在哪呢?
p[]是在堆栈上分配了的数组, 并被初始化为“hello world”。p是数组首地址。
p是定义的一个指针变量,并指向了“hello world”, “hello world”是在编译阶段分配的空间,存在静态存储区。不能对“hello world”的值进行更改,因为它是个常量。
2、
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str); //str的值没变,但指向的内容变了。
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
char *str1 = (char *) malloc(10);//提示对话框
}
执行上述代码可以输出“world”, 但是存在隐患。
因为在free(str)后,str成为了野指针,对那片内存区域更改内容后,如果继续分配内存,虽然执行时没有问题,但是在调试时vc在分配内存空间时会检查堆内容的一致性,如果可分配堆的内容被篡改了,会弹出对话框:user breakpoint called from code at 0x7c941230.
点击确定后,执行完毕后,output窗口会提示:
HEAP[test.exe]: HEAP: Free Heap block 30fd8 modified at 31000 after it was freed。
3、写出一个比较相同类型的数据是否相等的函数。
函数模板:
template<typename T> bool compare(T a, T b)
//template<class T> bool compare(T a, T b)
{
return a == b;
}
void main()
{
string a = "ni";
string b = "ni";
bool c = compare(a, b);
c=compare(“ni”, “hao”);//在静态区存了“ni”, “hao”,是常量,见问题1。
c = compare(1.0,2.0);
c = compare<int>(1,3);
}
4、strcpy覆盖问题:(可以)
比如:char p[]=”hello world”;
strcpy(p, p+2);
此函数与memcpy, memmove的区别,见“我眼中的数据对齐”。
5、在使用STL容器时,要先使用Using namespace std,指定名字空间;或者使用std::。因为这些容器都定义在std名字空间内(_STD_BEGIN)。
vector是序列式的,相当于数组,可以随机访问。
set, multiset, map, multimap是关联式式的,都是基于红黑树的结构。
容器都可以使用Iterator来进行遍历,定义iterator时要指定名字域,比如map<string,int>::iterator。
set, multiset:是只有key,set不允许重复,multiset可以重复。那multiset有什么意义呢?
Map, multimap:是<key, value>的形式。Map的key唯一;multimap不唯一,比如一个人有好几个电话号码。
6、hash_map和map的区别在哪里? 查《程序设计实践》
hash_map需要hash函数,等于函数;map只需要比较函数(小于函数)。
hash_map采用hash表存储,map一般采用红黑树(RB Tree)实现。
7、平衡二叉树(AVL树)是一种二叉查找树,并且或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度之差之差的绝对值不超过1。
红黑树是一种二叉查找树,红黑树并不是平衡二叉树,恰恰相反,红黑树放松了平衡二叉树的某些要求,由于一定限度的“不平衡”,红黑树的性能得到了提升。查老严的《数据结构》。
附:二叉排序树和二分查找以及平衡二叉树的比较。
8、去除字符串前后的空格。
1)使用正则表达式
2)
char * trim(char * src)
{
for(int i = 0; *(src+i) ==' '; i++)
;
//src=src+i;
strcpy(src,src+i);
for(i = strlen(src)-1; i>=0; i--)
if(*(src+i) != ' ')//可以使用s[i], 查看K&R《The c programming language》
break;
*(src+i+1) = '/0';
return src;
}
9、统计文本文件的行数。
unsigned short count_lines(FILE* fp)
{
int c, nl;
if(fp==NULL)
return 0;
nl = 0;
while ((c = fgetc(fp)) != EOF) //用fgets,fread一次读一行的话,要确保数组足够大。
if (c == '/n')
++nl;
return nl+1;
}
附:
(1)在二叉排序树上进行查找时的平均查找长度和二叉树的形态有关:
①在最坏情况下,二叉排序树是通过把一个有序表的n个结点依次插入而生成的,此时所得的二叉排序树蜕化为棵深度为n的单支树,它的平均查找长度和单链表上的顺序查找相同,亦是(n+1)/2。
②在最好情况下,二叉排序树在生成的过程中,树的形态比较匀称,最终得到的是一棵形态与二分查找的判定树相似的二叉排序树,此时它的平均查找长度大约是lgn。
③插入、删除和查找算法的时间复杂度均为O(lgn)。
(3)二叉排序树和二分查找的比较
就平均时间性能而言,二叉排序树上的查找和二分查找差不多。
就维护表的有序性而言,二叉排序树无须移动结点,只需修改指针即可完成插入和删除操作,且其平均的执行时间均为O(lgn),因此更有效。二分查找所涉及的有序表是一个向量,若有插入和删除结点的操作,则维护表的有序性所花的代价是O(n)。当有序表是静态查找表时,宜用向量作为其存储结构,而采用二分查找实现其查找操作;若有序表里动态查找表,则应选择二叉排序树作为其存储结构。
(4)平衡二叉树
为了保证二叉排序树的高度为lgn,从而保证然二叉排序树上实现的插入、删除和查找等基本操作的平均时间为O(lgn),在往树中插入或删除结点时,要调整树的形态来保持树的"平衡。使之既保持BST性质不变又保证树的高度在任何情况下均为O(lgn),从而确保树上的基本操作在最坏情况下的时间均为O(lgn)。
注意:
① 平衡二叉树(Balanced Binary Tree)是指树中任一结点的左右子树的高度大致相同。
② 任一结点的左右子树的高度均相同(如满二叉树),则二叉树是完全平衡的。通常,只要二叉树的高度为O(1gn),就可看作是平衡的。
③ 平衡的二叉排序树指满足BST性质的平衡二叉树。
④ AVL树中任一结点的左、右子树的高度之差的绝对值不超过1。在最坏情况下,n个结点的AVL树的高度约为1.44lgn。而完全平衡的二叉树度高约为lgn,AVL树是接近最优的。