海康威视2015年研发笔试题

本文详细介绍了C++中指针与引用的区别,包括定义、性质、作为函数参数传递时的行为,以及在实际编程中的应用场景。强调了引用作为函数参数能实现对实参的直接修改,而指针则需要通过解引用操作。此外,文章还讨论了动态数据结构、类型安全、平台无关性等概念,并给出了相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、引用和指针的区别?

指针和引用在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


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值