程序员面试常见问题-整理-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\n", 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("\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); return 0; }