一、引用和指针的区别?
指针和引用在C++中很常用,但是对于它们之间的区别很多初学者都不是太熟悉,下面来谈谈他们2者之间的区别和用法。
1.指针和引用的定义和性质区别:
(1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:
int a=1;int *p=&a;
int a=1;int &b=a;
上面定义了一个整形变量和一个指针变量p,该指针变量指向a的存储单元,即p的值是a存储单元的地址。
而下面2句定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。
(2)可以有const指针,但是没有const引用;
(3)指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)
(4)指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
(5)指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
(6)"sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;
(7)指针和引用的自增(++)运算意义不一样;
2.指针和引用作为函数参数进行传递时的区别。
(1)指针作为参数进行传递:
#include<iostream> using namespace std; void swap(int *a,int *b) { int temp=*a; *a=*b; *b=temp; } int main(void) { int a=1,b=2; swap(&a,&b); cout<<a<<" "<<b<<endl; system("pause"); return 0; }
结果为2 1;
用指针传递参数,可以实现对实参进行改变的目的,是因为传递过来的是实参的地址,因此使用*a实际上是取存储实参的内存单元里的数据,即是对实参进行改变,因此可以达到目的。
再看一个程序;
#include<iostream> using namespace std; void test(int *p) { int a=1; p=&a; cout<<p<<" "<<*p<<endl; } int main(void) { int *p=NULL; test(p); if(p==NULL) cout<<"指针p为NULL"<<endl; system("pause"); return 0; }
运行结果为:
0x22ff44 1
指针p为NULL
大家可能会感到奇怪,怎么回事,不是传递的是地址么,怎么p回事NULL?事实上,在main函数中声明了一个指针p,并赋值为NULL,当调用test函数时,事实上传递的也是地址,只不过传递的是指地址。也就是说将指针作为参数进行传递时,事实上也是值传递,只不过传递的是地址。当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,即上面程序main函数中的p何test函数中使用的p不是同一个变量,存储2个变量p的单元也不相同(只是2个p指向同一个存储单元),那么在test函数中对p进行修改,并不会影响到main函数中的p的值。
如果要想达到也同时修改的目的的话,就得使用引用了。
2.将引用作为函数的参数进行传递。
在讲引用作为函数参数进行传递时,实质上传递的是实参本身,即传递进来的不是实参的一个拷贝,因此对形参的修改其实是对实参的修改,所以在用引用进行参数传递时,不仅节约时间,而且可以节约空间。
看下面这个程序:
#include<iostream> using namespace std; void test(int &a) { cout<<&a<<" "<<a<<endl; } int main(void) { int a=1; cout<<&a<<" "<<a<<endl; test(a); system("pause"); return 0; }
输出结果为: 0x22ff44 1
0x22ff44 1
再看下这个程序:
这足以说明用引用进行参数传递时,事实上传递的是实参本身,而不是拷贝。
所以在上述要达到同时修改指针的目的的话,就得使用引用了。
#include<iostream> using namespace std; void test(int *&p) { int a=1; p=&a; cout<<p<<" "<<*p<<endl; } int main(void) { int *p=NULL; test(p); if(p!=NULL) cout<<"指针p不为NULL"<<endl; system("pause"); return 0; }
输出结果为:0x22ff44 1
指针p不为NULL
reference:http://www.cnblogs.com/dolphin0520/archive/2011/04/03/2004869.html
二、C++中为什么用模板类?(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型
三、请写出下面代码的输出结果
int main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d\n",*(a+1),*(ptr-1));
system("pause");
return 0;
}
输出:2,5
输出1中,a为数组首元素地址,a+1则向右移动一位,输出为2。
输出2中,&a+1则向右移动四位,1是整数是四个字节。因此ptr-1指向第4个元素。
四、请填写BOOL,float,指针变量和“零值”比较的if语句
BOOL型变量:if(!var)
int型变量: if(var==0)
float型变量:
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
指针变量: if(var==NULL)
reference:http://blog.youkuaiyun.com/root_robot/article/details/53494171
五、
class A
{
};
class B{
public:
B(){};
~B(){};
};
class C{
public:
C(){};
virtual ~C(){};
};
int main()
{
printf("%d,%d,%d\n",sizeof(A),sizeof(B),sizeof(C));
system("pause");
return 0;
}
输出结果是:1,1,4
以上输出结果是使用coldBlocks平台,32位机
C类中的析构函数是虚函数,在类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。基类中中的析构函数的虚函数,是为了释放派生类的对象,以防造成内存泄漏。
reference:http://blog.youkuaiyun.com/starlee/article/details/619827
六、运行下面代码,打印输出的信息。
class A
{
private:
int m_value;
public:
A(int value)
{
m_value=value;
}
void Print1()
{
printf("hello world!\n");
}
virtual void Print2()
{
printf("hello world\n");
}
};
int main()
{
A *pA=NULL;
pA->Print1();
pA->Print2();
system("pause");
return 0;
}
输出的结果是Print1中的结果:“hello woeld!”
没有输出Print2中的内容,理解为在当前类中声明虚函数,其函数的定义必须放在派生类中。
七、抢完成
1、不使用C/C++的字符串函数,请编写函数strcpy函数。
2、解释为什么会返回char *类型。
已知strcpy的函数声明为char *strcpy(char *strDest,const char *strSrc)其中strDest是目的字符串,strSrc是源字符串。
void my_strcpy(char* Dest, const char *Src)
{
assert((Dest!= NULL)&&(Src!=NULL));
while((*Dest = *Src)!='\0'){
Dest++;
Src++;
}
}
int main()
{
int a=3;
char dest[]="abc";
//char src[]="def";
char *src="def"; ///can use
my_strcpy(dest,src);
printf("%s\n",dest);
return 0;
}
第二问:
有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,
可以附加返回值。
例如字符串拷贝函数strcpy 的原型:
char *strcpy(char *strDest,const char *strSrc);
strcpy 函数将strSrc 拷贝至输出参数strDest 中,同时函数的返回值又是strDest。
这样做并非多此一举,可以获得如下灵活性:
char str[20];
int length = strlen( strcpy(str, “Hello World”) );
以上考察林锐 高质量的 C/C++编程
reference:http://blog.youkuaiyun.com/jiyanfeng1/article/details/8087984
八、用户输入M,N值,从1到N开始顺序循环数数,每数到M输出该数值,直至全部输出,写出程序。(15分)
本题考察的是 约瑟夫环 Josephus算法 问题。
1、使用循环链表数据结构解决问题
核心思路是:
这个问题采用的是典型的循环链表的数据结构,就是将一个链表的尾元素指针指向队首元素。 p->link=head
解决问题的核心步骤:
1.建立一个具有n个链结点,无头结点的循环链表
2.确定第1个报数人的位置
3.不断地从链表中删除链结点,直到链表为空
法1:
typedef struct node
{
int data;
struct node *next;
}LNode;
LNode* Create(int n,int k)/*创建循环链表*/
{
int start=k-1;
LNode *s,*p,*L=0,*t;
if (start==0) start=n;
while (n!=0)
{
s=(LNode *)malloc(sizeof(LNode));
if (L==0) p=s;
if (n==start) t=s;
s-> data=n;
s-> next=L;
L=s;
n--;
}
p-> next=L;
return t;
}
LNode* GetNode(LNode *p)/*出队函数*/
{
LNode *q;
for (q=p;q-> next!=p;q=q-> next);
q-> next=p-> next;
free (p);
return (q);
}
int Print(LNode *p,int m)/*输出函数*/
{
int i;
printf ( "出队编号:\n ");
while (p-> next!=p)
{
for (i=1;i <=m;i++)
p=p-> next;
printf ( "%d ",p-> data);
p=GetNode(p);
}
printf( "%d\n ",p-> data);
return 0;
}
main()
{
LNode* Create(int,int);
LNode* GetNode(LNode *);
int Print(LNode *,int);
LNode *p;
int n,k,m;
do
{
printf ( "输入总人数 ");
scanf ( "%d ",&n);
}while(n <=0);
do
{
printf ( "输入开始人的序号(1~%d) ",n);
scanf ( "%d ",&k);
}while (k <=0 || k> n);
do
{
printf( "输入间隔数字 ");
scanf( "%d ",&m);
}while(m <=0);
p=Create(n,k);
Print(p,m);
return 0;
}
法2:
// 用户输入M,N值,从1至N开始顺序
// 循环数数,每数到M输出该数值,
// 直至全部输出
#include <stdio.h>
// 节点
typedef struct node
{
int data;
node* next;
}node;
// 创建循环链表
void createList(node*& head, node*& tail, int n)
{
if(n<1)
{
head = NULL;
return ;
}
head = new node();
head->data = 1;
head->next = NULL;
node* p = head;
for(int i=2; i<n+1; i++)
{
p->next = new node();
p = p->next;
p->data = i;
p->next = NULL;
}
tail = p;
p->next = head;
}
// 打印循环链表
void Print(node*& head)
{
node* p = head;
while(p && p->next!=head)
{
printf("%d ", p->data);
p=p->next;
}
if(p)
{
printf("%d\n", p->data);
}
}
// 用户输入M,N值,从1至N开始顺序
// 循环数数,每数到M输出该数值,
// 直至全部输出
void CountPrint(node*& head, node*& tail, int m)
{
node* cur = head;
node* pre = tail;
int cnt = m-1;
while(cur && cur!=cur->next)
{
if(cnt)
{
cnt--;
pre = cur;
cur = cur->next;
}
else
{
pre->next = cur->next;
printf("%d ", cur->data);
delete cur;
cur = pre->next;
cnt = m-1;
}
}
if(cur)
{
printf("%d ", cur->data);
delete cur;
head = tail = NULL;
}
printf("\n");
}
int main()
{
node* head;
node* tail;
int m;
int n;
scanf("%d", &n);
scanf("%d", &m);
createList(head, tail, n);
Print(head);
CountPrint(head, tail, m);
system("pause");
return 0;
}
reference:http://www.cnblogs.com/EricYang/archive/2009/09/04/1560478.html
法3:使用数组方法(相对来说比较简单一些)int main()
{
int i,j,m,n,out_num;
j=0;
out_num = 0;
printf("Please input N and M:\n");
scanf("%d %d",&n,&m);
int a[2][N];
for(i=1;i<=n;i++) //是否被取出标志
{
a[0][i]=0;
}
for(i=1;i<=n;i++) //数组赋值
{
a[1][i]=i;
}
while(out_num != n)
for(i=1;i<=n;i++)
{
if(0==a[0][i])
{
j++;
}
if(m==j)
{
printf("\n%d",i);
out_num++;
j=0;
a[0][i]=1;
}
}
printf("\n循环排序输出完成");
}
reference:http://blog.youkuaiyun.com/code_jober/article/details/9223761法4:使用数学方法
int main()
{
int n, m, i, s = 0;
printf ("N M = ");
scanf("%d %d", &n, &m);
for (i = 2; i <= n; i++)
{
s = (s + m) % i;
}
printf ("\nThe winner is %d\n", s+1);
}
reference:http://www.cnblogs.com/EricYang/archive/2009/09/04/1560478.html