笔试题目
不用任何局部和全局变量实现int strlen(char *a)
int strlen(char *a) {
if(0 == *a)
return 0;
else
return 1 + strlen(a + 1);
}
hashtable的实现
|
//头文件 #pragma once #define HASH_MAX 10 enum result{error = -1, false = 0, true}; typedef char elemtype; struct hashnode { elemtype *pstr; struct hashnode *next; };//用单链表存储数据,以哈西表的结点作为链表的头结点 struct hashtable { size_t table_size; struct hashnode *head[HASH_MAX];//建议定义成**head二级指针, //在初始化时给head 单独malloc一个空间即可 }; int hash_init(struct hashtable **ht);//初始化 int hash_destroy(struct hashtable **ht);//销毁 int hash_insert(struct hashtable *ht, elemtype *val);//插入 int hash_delete(struct hashtable *ht, elemtype *val);//查找 int hash_show(struct hashtable *ht);//打印出来 struct hashnode *hash_find(struct hashtable *ht, elemtype *key);//查找 //函数实现文件 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "hash.h" int hash_key(elemtype *key) { int hash_val = 0; while(*key++) hash_val = (hash_val * 3) + (int)(*key); return (hash_val % HASH_MAX); } int hash_init(struct hashtable **ht) { int i; *ht = (struct hashtable *)malloc(sizeof(struct hashtable)); if(! *ht) return false; (*ht)->table_size = HASH_MAX; for(i = 0; i < HASH_MAX; i++) { (*ht)->head[i] = (struct hashnode *)malloc(sizeof(struct hashnode)); if(!(*ht)->head[i]) { free(*ht); return false; } } return true; } int hash_destroy(struct hashtable **ht) { int i; struct hashnode *pcur, *ptmp; for(i = 0; i < HASH_MAX; i++) { pcur = (*ht)->head[i]->next; while(pcur) { ptmp = pcur->next; free(pcur->pstr); pcur->pstr = NULL; free(pcur); pcur = NULL; pcur = ptmp; } free((*ht)->head[i]); (*ht)->head[i] = NULL; } free(*ht); *ht = NULL; return true; } int hash_insert(struct hashtable *ht, elemtype *val) { int hash_val = hash_key(val); struct hashnode *htmp, *pcur; if(hash_find(ht, val) != false) { printf("%s is already in.\n", val); return false; } htmp = (struct hashnode *)malloc(sizeof(htmp)); if(!htmp) return false; htmp->pstr = (elemtype *)malloc(sizeof(elemtype)); if(!htmp->pstr) { free(htmp); return false; } strcpy(htmp->pstr, val); htmp->next = NULL; for(pcur = ht->head[hash_val]; pcur->next != NULL; pcur = pcur->next); pcur->next = htmp; return true; } struct hashnode *hash_find(struct hashtable *ht, elemtype *key) { int hash_val = hash_key(key); struct hashnode *htmp; htmp = ht->head[hash_val]->next; while(htmp != NULL)// && !strcmp(htmp->pstr, key)) { if(strcmp(htmp->pstr, key) == 0) break; htmp = htmp->next; } if(htmp == NULL) return false; else return htmp; } int hash_show(struct hashtable *ht) { int i; struct hashnode *pcur; for(i = 0; i < HASH_MAX; i++) { pcur = ht->head[i]->next; printf("%d: ", i + 1); while(pcur != NULL) { printf("%s ", pcur->pstr); pcur = pcur->next; } printf("\n"); } printf("\n"); return true; } int hash_delete(struct hashtable *ht, elemtype *val) { struct hashnode *pcur, *pre; int hashkey = hash_key(val); pcur = ht->head[hashkey]; pre = pcur->next; while(pre) { if(strcmp(pre->pstr, val) == 0) { printf("find: %s\n", pre->pstr); break; } pcur = pre; pre = pre->next; } if(pre == NULL) { printf("%s is not be found\n", val); return false; } pcur->next = pre->next; free(pre); pre = NULL; return true; } //主函数 #include <stdio.h> #include <stdlib.h> #include "hash.h" #include "mystring.h" int main(int argv, char **argc) { struct hashtable *ht; elemtype *str1 = "Hljytlo"; elemtype *str2 = "ellergo"; elemtype *str3 = "yelglo"; elemtype *str4 = "mHetello"; elemtype *str5 = "kHelslo"; elemtype *str6 = "pHelasdlo"; hash_init(&ht); hash_show(ht); hash_insert(ht, str1); hash_insert(ht, str2); hash_insert(ht, str3); hash_insert(ht, str4); hash_insert(ht, str5); hash_insert(ht, str6); hash_show(ht); printf("Input a string you want to delte: "); str1 = read_line(10);//这个需要自己写的函数,可以用fgets代替, //建议不要使用gets,因为gets有bug hash_delete(ht, str1); hash_show(ht); hash_destroy(&ht); return 0; } |
http://www.cnblogs.com/guoshiandroid
/* Code by Slyar */
#include <stdio.h>
#include <stdlib.h>
#define max 8
int queen[max], sum=0; /* max为棋盘最大坐标 */
void show() /* 输出所有皇后的坐标 */
{
int i;
for(i = 0; i < max; i++)
{
printf("(%d,%d) ", i, queen[i]);
}
printf("\n");
sum++;
}
int check(int n) /* 检查当前列能否放置皇后 */
{
int i;
for(i = 0; i < n; i++) /* 检查横排和对角线上是否可以放置皇后 */
{
if(queen[i] == queen[n] || abs(queen[i] - queen[n]) == (n - i))
{
return 1;
}
}
return 0;
}
void put(int n) /* 回溯尝试皇后位置,n为横坐标 */
{
int i;
for(i = 0; i < max; i++)
{
queen[n] = i; /* 将皇后摆到当前循环到的位置 */
if(!check(n))
{
if(n == max - 1)
{
show(); /* 如果全部摆好,则输出所有皇后的坐标 */
}
else
{
put(n + 1); /* 否则继续摆放下一个皇后 */
}
}
}
}
int main()
{
put(0); /* 从横坐标为0开始依次尝试 */
printf("%d", sum);
system("pause");
return 0;
}
前缀表达式和后缀表达式的问题
35,15,+,80,70,-,*,20,/ //后缀表达方式
(((35+15)*(80-70))/20)=25 //中缀表达方式
/,*,+,35,15,-,80,70, 20 //前缀表达方式
人们习惯的运算方式是中缀表达式。而碰到前缀,后缀方式。。迷茫。其实仅仅是一种表达式子的方式而已(不被你习惯的方式),我这里教你一种也许你老师都没跟你讲的简单转换方式。一个中缀式到其他式子的转换方法~~这里我给出一个中缀表达式~ a+b*c-(d+e)
第一步:按照运算符的优先级对所有的运算单位加括号~
式子变成拉:((a+(b*c))-(d+e))
第二步:转换前缀与后缀表达式
前缀:把运算符号移动到对应的括号前面
则变成拉:-( +(a *(bc)) +(de))
把括号去掉:-+a*bc+de 前缀式子出现
后缀:把运算符号移动到对应的括号后面
则变成拉:((a(bc)* )- (de)+ )-
把括号去掉:abc*+de+- 后缀式子出现
不用sizeof测量数据类型的长度
#include <iostream>
using namespace std;
typedef double testtype;
int main(){
testtype t[2];
cout<< (int)(&t[1])-(int)(&t[0])<<endl;
system("pause");
}
-------------------- -------------------- -------------------- --------------------
一、预备知识—程序的内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456/0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456/0放在常量区,编译器可能会将它与p3所指向的"123456"
优化成一个地方。
}
二、堆和栈的理论知识
2.1申请方式
stack: 由系统自动分配。 例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间
heap: 需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = new char[10];
但是注意p1、p2本身是在栈中的。
2.2
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
2.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
2.4申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。
2.5堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
2.6存取效率的比较
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到
edx中,再根据edx读取字符,显然慢了。
2.7小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就
走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自
由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由
度大。
-------------------- -------------------- -------------------- --------------------
多态性与虚函数
面向对象理论中的3个术语:对象、方法和消息。对象(object):不言而喻,它是构成系统的基本单位,有属性和行为两个要素,在C++中,每个对象都是由数据和函数这两部分组成的,数据即是对象的属性,行为称之为方法(method),方法是对数据的操作,通常由函数实现。调用对象中的函数就是向该对象传送一个消息(message),所谓“消息”,其实就是一个命令。例如:
stud.display();
就是向对象stud发出的一个“消息”,通知它执行其中的display“方法”(即display函数)。即:stud是对象,display()是方法,语句“stud.display();”是消息。
1.多态性(polymorphism)
多态性定义:由继承而产生的相关的不同的类,向其对象发送同一个消息,不同的对象接收到后会产生不同的行为(即方法)。
多态性分为两类:静态多态性和动态多态性。函数重载和运算符重载实现的多态性属于静态多态性,在程序编译时系统就能决定调用的是哪个函数,因此静态多态性有称为编译时的多态性。静态多态性是通过函数的重载实现的(运算符重载实质上也是函数重载)。动态多态性是在程序运行过程中才动态地确定操作所针对的对象,故称之为运行时的多态性。动态多态性是通过虚函数实现的。
关于静态多态性和动态多态性,请看下面的例子:
定义3个类:点、圆和圆柱
- #include <iostream.h>
- //定义Point基类
- class Point
- {
- public:
- Point(float=0, float=0);
- void display();
- friend ostream & operator <<(ostream &, const Point &);
- protected:
- float x, y;
- };
- Point::Point(float a, float b)
- {
- x=a; y=b;
- }
- ostream & operator <<(ostream &output, const Point &p)
- {
- output<<"["<<p.x<<","<<p.y<<"]"<<endl;
- return output;
- }
- void Point::display()
- {
- cout<<"["<<x<<","<<y<<"]"<<endl;
- }
- //定义Circle基类
- class Circle: public Point
- {
- public:
- Circle(float=0, float=0, float=0);
- float area ( ) const;
- void display();
- friend ostream & operator <<(ostream &, const Circle &);
- protected:
- float radius;
- };
- Circle::Circle(float a,float b,float r):Point(a,b),radius(r){ }
- float Circle::area( ) const
- { return 3.14159*radius*radius; }
- ostream & operator <<(ostream &output, const Circle &c)
- {
- output<<"Center=["<<c.x<<","<<c.y<<"], r="<<c.radius<<", area="<<c.area( )<<endl;
- return output;
- }
- void Circle::display()
- {
- cout<<"Center=["<<x<<","<<y<<"], r="<<radius<<", area="<<area( )<<endl;
- }
- //定义圆柱体类
- class Cylinder: public Circle
- {
- public:
- Cylinder (float=0,float=0,float=0,float=0);
- float area() const;//计算圆表面积,和Circle类中的area重名
- float volume() const;
- void display();
- friend ostream & operator <<(ostream &, const Cylinder &);
- protected:
- float height;
- };
- Cylinder::Cylinder(float a,float b,float r,float h):Circle(a,b,r),height(h){}
- float Cylinder::area( ) const
- { return 2*Circle::area()+2*3.14159*radius*height; }
- float Cylinder::volume() const
- { return Circle::area()*height; }
- ostream & operator <<(ostream & output, const Cylinder &cy)
- {
- output<<"Center=["<<cy.x<<","<<cy.y<<"], r="<<cy.radius<<", h="
- <<cy.height<<"\narea="<<cy.area( )<<", volume="<<cy.volume( )<<endl;
- return output;
- }
- void Cylinder::display()
- {
- cout<<"Center=["<<x<<","<<y<<"], r="<<radius<<", h="
- <<height<<"\narea="<<area( )<<", volume="<<volume( )<<endl;
- }
主函数(1),静态关联
- int main()
- {
- Cylinder cy1;
- cout<<"A Cylinder:\n"<<cy1; //用重载运算符“<<”输出cy1的数据
- Point &p=cy1; //将圆柱cy1赋值给点,p是Point类对象的引用变量
- cout<<"\np as a Point:\n"<<p; //p作为一个“点”输出
- Circle &c=cy1; //将圆柱cy1赋值给圆,c是Circle类对象的引用变量
- cout<<"\nc as a Circle:\n"<<c<<endl; //c作为一个“圆”输出
- return 0;
- }
主函数(2),动态关联:
- int main( )
- {
- Point p1(9,9);
- Circle c1(6,6,8);
- Cylinder cy1(5,5,15,7.5);
- Point *pt=&p1;
- pt->display();
- pt=&c1;
- pt->display();
- pt=&cy1;
- pt->display();
- return 0;
- }
要想pt能指向Circle::display(),就必须用虚成员函数来实现,即把基类中的display()函数声明为virtual类型。基类中的display()函数声明为了virtual类型,代表了它可以在派生类中被重新定义,为它赋予新功能(所以可以基类中的虚函数的函数体可以为空,或者写成纯虚函数的形式),注意是“重新定义”而不是“共存”,即此时Circle中定义的display()函数不再看做是增加的部分,而是看做基类的部分,所以直接用pt->display()或者pt->Point::display()就可以调用Circle类中定义的display()函数了,写成pt-> Circle::display()反而会出错。
2. 虚函数
上面已经说了,C++的动态多态性是通过虚函数来实现的。“虚成员函数”简称“虚函数”,C++不允许在类外声明虚函数。“虚函数允许派生类取代基类所提供的实现。编译器确保当对象为派生类时,取代者(译注:即派生类的实现)总是被调用,即使对象是使用基类指针访问而不是派生类的指针。”
上面的例子,写成虚函数的形式:
- class
- {
- …
- virtual void display(){} //声明为空的虚函数
- }
- int mai()
- {
- Point p1(9,9);
- Circle c1(6,6,8);
- Cylinder cy1(5,5,15,7.5);
- Point *pt=&p1;
- pt->display(); //错误,因为Point类中的display()被定义为空,没有输出功能
- pt=&c1;
- pt->display(); //直接调用就可以输出圆的内容了
- ……
- }
- class
- {
- …
- virtual void display() =0; //声明为纯虚函数
- }
- int mai()
- {
- Point p1(9,9); //错误,包含纯虚函数的类被成为抽象类,不能被初始化
- Circle c1(6,6,8);
- Cylinder cy1(5,5,15,7.5);
- Point *pt=&p1;
- pt->display();
- pt=&c1;
- pt->display(); //直接调用就可以了
- ……
- }
例如,可以给点、圆和圆柱体定义一个抽象基类Shape(形状):
- class Shape
- {
- public:
- virtual float area() const { return 0.0; } //虚函数
- virtual float volume() const { return 0.0; } //虚函数
- virtual void shapeName() const =0; //纯虚函数
- };
3. 虚析构函数
如果用new运算符建立了临时对象,若基类中有析构函数,并且定义了一个指向该基类的指针变量。在程序用带指针参数的delete运算符撤销对象时,会发生一个情况:系统会只执行基类的析构函数,而不执行派生类的析构函数。例如:
- #include <iostream.h>
- //定义Point基类
- class Point
- {
- public:
- Point( ){ }; //定义构造函数
- ~Point() { cout<<"Point OK!"<<endl; } //析构函数
- };
- class Circle: public Point
- {
- public:
- Circle( ){ };
- ~Circle() { cout<<"Circle OK!"<<endl; }
- protected:
- float radius;
- };
- int main( )
- {
- Point *p=new Circle;
- delete p;
- return 0;
- }
Point OK!
表示只执行了基类Piont的析构函数,而没有执行派生类Circle的析构函数。如果希望能执行派生类中的析构函数,可以将基类的析构函数声明为虚函数,如
virtual ~ Point() { cout<<"PointOK!"<<endl; }
如果将基类的析构函数声明为虚函数,由该基类所派生的所有派生类的析构函数也自动成为虚函数,即使它们名字不同。可见原理和格式与上面所说的虚函数是一样的。运行结果为:
Point OK!
Circle OK!
专业人员一般都习惯声明虚析构函数,即使基类并不需要析构函数,也显式地定义一个函数体为空的析构函数,以保证在撤销动态存储空间时能得到正确的处理。
浙公网安备 33010602011771号