程序员面试常见问题-整理-1

程序员面试常见问题-整理-1
2016.2.19 by Huangtao


以下是海康威视应用软件开发工程师笔试题涉及到的一些知识:
=====================================================================
Sizeof(结构体):
字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

一个类能够实例化,编译器就需给它分配内存空间,来指示类实例的地址。这里编译器默认分配了一个字节(如:char,编译器相关),以便标记可能初始化的类实例,同时使空类占用的空间也最少(即1字节)。
对于结构体和空类大小是1这个问题,首先这是一个C++问题,在C语言下空结构体大小为0(当然这是编译器相关的)。这里的空类和空结构体是指类或结构体中没有任何成员。
在C++下,空类和空结构体的大小是1(编译器相关),这是为什么呢?为什么不是0?
这是因为,C++标准中规定,“no object shall have the same address in memory as any other variable” ,就是任何不同的对象不能拥有相同的内存地址。 如果空类大小为0,若我们声明一个这个类的对象数组,那么数组中的每个对象都拥有了相同的地址,这显然是违背标准的。

int *a; int &a; int & *a; int * &a
int i;
int *a = &i;//这里a是一个指针,它指向变量i
int &b = i;//这里b是一个引用,它是变量i的引用,引用是什么?它的本质是什么?下面会具体讲述
int * &c = a;//这里c是一个引用,它是指针a的引用
int & *d;//这里d是一个指针,它指向引用,但引用不是实体,所以这是错误的


数组指针和指针数组的区别:
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。

指针数组
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。

这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
优先级:()>[]>*


指针函数与函数指针的区别:
1、指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的指针
类型标识符 *函数名(参数表)
int *f(x,y);
首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。

2、函数指针是指向函数的指针变量,即本质是一个指针变量。
int (*f) (int x); /* 声明一个函数指针 */
f=func; /* 将func函数的首地址赋给指针f */
指向函数的指针包含了函数的地址,可以通过它来调用函数。声明格式如下:
类型说明符 (*函数名)(参数)
其实这里不能称为函数名,应该叫做指针的变量名。这个特殊的指针指向一个返回整型值的函数。指针的声明笔削和它指向函数的声明保持一致。
指针名和指针运算符外面的括号改变了默认的运算符优先级。如果没有圆括号,就变成了一个返回整型指针的函数的原型声明。
例如:
void (*fptr)();
把函数的地址赋值给函数指针,可以采用下面两种形式:
fptr=&Function;
fptr=Function;
取地址运算符&不是必需的,因为单单一个函数标识符就标号表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。
可以采用如下两种方式来通过指针调用函数:
x=(*fptr)();
x=fptr();
第二种格式看上去和函数调用无异。但是有些程序员倾向于使用第一种格式,因为它明确指出是通过指针而非函数名来调用函数的。


Const
修饰指针:
(位于星号左侧,用来修饰指针所指的变量,即指针指向为常量;位于星号右侧,用来修饰指针本身,即指针本身是常量)
const int *A; 或 int const *A;
//const修饰指向的对象,A可变,A指向的对象不可变
int *const A;
//const修饰指针A, A不可变,A指向的对象可变
const int *const A;
//指针A和A指向的对象都不可变


指针和引用区别:(都是地址的概念)
非空区别;
合法性区别;
可修改性区别;
应用区别。


C++中为什么用模板类?
(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型
模板就是实现代码重用机制的一种工具。它实现了将类型参数化,就是将类型定义为参数,实现了真正的代码可重用性。模板分为两大类:函数模板和类模板。由于类模板包含类型参数,所以类模板又称作参数化的类。如果说类是对象的抽象,抽象是类的实例;那么可以说类模板是类的抽象,而类是类模板的实例。利用类模板可以建立各种数据类型的类。


构造函数与析构函数的调用顺序
http://blog.csdn.net/bresponse/article/details/6914155


编写strcpy函数(10分)
已知strcpy函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest是目的字符串,strSrc是源字符串。
(1)不调用C++/C的字符串库函数,请编写函数 strcpy
char *strcpy(char *strDest, const char *strSrc);
{
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != ‘/0’ ) // 2分
NULL ;
return address ; // 2分
}

(2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值?
答:为了实现链式表达式。 // 2分
例如 int length = strlen( strcpy( strDest, “hello world”) );

//===================================================================
// 利用栈来解决一个字符串之中使用的括号是否匹配的问题
//===================================================================

#include <iostream>
using namespace std;

#define stacksize 100   // 定义栈的空间大小

// 定义栈的结构体
struct stack
{      
    char strstack[stacksize];
    int top;     
};

// 定义一个新栈s,初始化栈顶为-1
void InitStack(stack &s)
{    
    s.top = -1;
}

char Push(stack &s, char a)
{
    if (s.top == stacksize - 1)
        return 0;
    s.top++;
    s.strstack[s.top] = a;
    return a;
}

char Pop(stack &s)
{
    if (s.top == -1)
        return 0;
    char a = s.strstack[s.top];
    s.top--;
    return a;
}

// 栈为空时返回值为1
int Empty(stack &s)
{
    if (s.top == -1)
        return 1;
    else
        return 0;
}

// 检查括号是否匹配的函数
int Check(char * str)
{   
    stack s;
    InitStack(s);
    int strn = strlen(str);
    for (int i = 0; i < strn; i++)
    {
        char a = str[i];
        switch (a)
        {
        case '{':
        case '[':
        case '(':
            Push(s, a);        //若是左括号,则进行入栈操作
            break;
        case ')':            //若是右括号,则进行出栈操作,若出栈元素不是与输入相对应的左括号,则字符串括号中不匹配
            if (Pop(s) != '(')
                return 0;
            break;
        case ']':
            if (Pop(s) != '[')
                return 0;
            break;
        case '}':
            if (Pop(s) != '{')
                return 0;
            break;
        default:break;
        }
    }
    int re = Empty(s);
    if (re == 1)
        return 1;    //栈为空
    else
        return 0;    //栈不为空,有左括号,即存在‘(’或'['或'{'未匹配
}

int main()
{
    char str[100];
    cout << "请您输入一个长度小于100的字符串:" << endl;
    cin >> str;
    int re = Check(str);
    if (re == 1)
        cout << "您输入的字符串中的括号完全匹配!" << endl;
    else if (re == 0)
        cout << "您输入的字符串中的括号不匹配!" << endl;

    return 0;
}
//===================================================================
// 用户输入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
", p->data);
    }
}

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("
");
}

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);
    return 0;
}
原文地址:https://www.cnblogs.com/ht-beyond/p/5201504.html