对几家的面试题凭记忆做个总结,基本全部拿到offer,由于时间比较长,题目只写大体意思,然后给出自己当时的答案(不保证一定正确):
-
abstract类不可以被实例化
-
蛋糕算法: 平面分割空间:(n-1)n(n+1)/6+n+1切后:2^n
-
直线分割平面:n(n+1)/2+1
平面分割和空间分割的相关公式ACM & 算法 n条直线分割平面问题的公式推导:
设n条直线将平面分成Kn个部分,
当n=1时,K1=2;当n=2时,K2=4;当n=3时,K3=7;
当n=4时,K4=11;……K(n)= K(n-1) n, K(n)=1+n*(n+1)/2.
那n=1,2,3,4,5,6,7,8,9,
2,4,7,11,...
2 2,4 3,7 4,11 5 ...
1 1 2 3 4 5 6 7 ...
n=2 2 3 4 5 6 7 8 ... n=1 1 2 3 ... n=1 (n 1)*n/2(部分)
所以Kn=1+(n+1)*n/2(部分)
n条直线分割平面数:1+(n+1)*n/2
HDOJ 2050
n条折线分割平面数:2*n*n-n+1
n条折线可以看做是2n条直线分割平面,然后减去多余的区域
2n条直线分割平面的区域数就是n(2n+1)+1=2n^2+n+1
多余的区域是2n个,于是有效区域数就是2n^2-n+1个
n条闭合曲线分割平面 1 n=1;
f(n) = f(n-1)+2(n-1) 其他n;
n条闭合曲线分割平面数:fn(n)=n^2-n+2;
平面最大化分割空间
递归关系f(n)=f(n-1)+n(n-1)/2+1
f(n)=(n^3+5n)/6+1
设n-1个平面已经最大化分割了某固定空间
则第n个平面要最大分割空间的话就要与所有平面都有交线
从第n个平面看,和n-1个平面相交的话,等同于第n个平面被n-1条直线最大化分割,数量为s
从空间上看,就是把空间多划分出来了s个子空间
于是递归关系就是f(n)=f(n-1)+s
而平面被n条直线最大化分割的公式就是s=n(n+1)/2+1
那么
f(n)
=n(n-1)/2+f(n-1)+1
=n(n-1)/2+(n-1)(n-2)/2+f(n-2)+2
=[n(n-1)+(n-1)(n-2)+(n-2)(n-3)+...+3*2+2*1]/2+f(1)+(n-1)
=[1*2+2*3+3*4+...+(n-3)(n-2)+(n-2)(n-1)+(n-1)n]/2+f(1)
={[1+2+3+4+...+(n-3)+(n-2)+(n-1)]+[1+2*2+3*3+4*4+...+(n-2)(n-2)+(n-1)(n-1)]}/2+(n-1)+f(1)
=[n(n-1)/2+(n-1)n(2n-1)/6]/2+(n-1)+f(1)
=[n(n-1)3/6+n(n-1)(2n-1)/6]/2+(n-1)+f(1)
=n(n-1)(n+1)/6+(n-1)+f(1)
=n(n^2-1)/6+n+1
=(n^3+5n)/6+1
4.不用if判定条件:
string[] res = {"优","优","良","良","中","中","差","差","差","差"};
return res[10-int];
5.sql建表查询:
create database [databaseTest]
go
USE [databaseTest]
GO
CREATE TABLE [dbo].[Table](
[id] [nchar](10) COLLATE Chinese_PRC_CI_AS NULL,
[names] [nchar](10) COLLATE Chinese_PRC_CI_AS NULL
)
insert into [dbo].[Table] values('01','大王')
insert into [dbo].[Table] values('02','小王')
insert into [dbo].[Table] values('03','张三')
insert into [dbo].[Table] values('04','李四')
insert into [dbo].[Table] values('05','王五')
insert into [dbo].[Table] values('06','大王')
insert into [dbo].[Table] values('07','大王')
insert into [dbo].[Table] values('08','小王')
select * from [dbo].[Table]
USE [databaseTest]
GO
SELECT count(id) - COUNT(DISTINCT names)
FROM [dbo].[Table]
create database [testdatabase]
go
USE testdatabase
GO
CREATE TABLE [dbo].[Table](--Table是关键字
[日期] [nchar](20) COLLATE Chinese_PRC_CI_AS NULL,
[胜负] [nchar](20) COLLATE Chinese_PRC_CI_AS NULL
)
insert into [dbo].[Table] values('2013-10-28','胜')
insert into [dbo].[Table] values('2013-10-28','负')
insert into [dbo].[Table] values('2013-10-28','胜')
insert into [dbo].[Table] values('2013-10-28','负')
insert into [dbo].[Table] values('2013-10-29','胜')
insert into [dbo].[Table] values('2013-10-29','负')
insert into [dbo].[Table] values('2013-10-29','负')
insert into [dbo].[Table] values('2013-10-29','负')
select * from [dbo].[Table]
--建立建立登陆用户.sql
select Name, sum(case when Score = '胜' then 1 else 0 end),
sum(case when Score = '负'' then 1 else 0 end)
from T_score group by Name
go
USE testdatabase
GO
--table是关键字
--select 日期 as '日期',count(*) as '胜',(select count(*) from [dbo].[Table] where 胜负 = '负'group by 日期)
--from [dbo].[Table]
--where 胜负 = '胜'
--group by 日期
select 日期 as '日期',
sum(case when 胜负 = '胜' then 1 else 0 end) as '胜',
sum(case when 胜负 = '负' then 1 else 0 end) as '负'
from [dbo].[Table]
group by 日期
6, <center><a href="http://www.baidu.com"><img src="aa.jpg"/></a></center>
7.ListViewItem中添加数据:
string[] str = {"张三 30" ,"李四 40","王五 50","赵六 60"};
foreach(string itm in str)
{
string[] chars = itm.Split(' ');
ListViewItem item = new ListViewItem(chars);
this.listView1.Items.Add(item);
}
8.C语言
#include <stdio.h>
int main()
{
int a; printf("int a; is %d ",sizeof(a));//4
char b = 'H'; printf("char b = 'H'; is %d ",sizeof(b));//1
char * c; printf("char * c; is %d ",sizeof(c));//4
int *d; printf("int *d; is %d ",sizeof(d));//4
char e = 204;
char f = 48; printf("e + f (204+48) = %d ",e+f);//-4 252-256
double g; printf("double g; is %d ",sizeof(g));//8
float h; printf("float h; is %d ",sizeof(h));//4
return 0;
}
用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 无符号长整型
用变量a给出下面的定义
a) 一个整型数(An integer)-a) int a; // An integer
b)一个指向整型数的指针( A pointer to an integer)-b) int *a; // A pointer to an integer
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r -c) int **a; // A pointer to a pointer to an integer
d)一个有10个整型数的数组( An array of 10 integers) -d) int a[10]; // An array of 10 integers
e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers) -e) int *a[10]; // An array of 10 pointers to integers
f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers) -f) int (*a)[10]; // A pointer to an array of 10 integers
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)-g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )-h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
关键字static的作用是什么?在C语言中,关键字static有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
下面的声明都是什么意思?
const int a;
int const a;前两个的作用是一样,a是一个常整型数。
const int *a;第三个意味着a是一个指向常整型数的指针(也就是,整型数[值]是不可修改的,但指针可以)。
int * const a;第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
int const * a const;最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
为什么还要如此看重关键字const呢?如下的几下理由:
1)关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
2)通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3)合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
关键字volatile有什么含意?并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量
1)一个参数既可以是const还可以是volatile吗?解释为什么。-1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2); 一个指针可以是volatile 吗?解释为什么。-2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3); 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
3)这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
#define BIT3 (0x1 << 3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
1)ISR不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2)ISR不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4)与第三点一脉相承,printf()经常有重入和性能上的问题。
1. char * const p;
char const * p
const char *p
上述三个有什么区别?
char * const p; //常量指针,p的值不可以修改
char const * p;//指向常量的指针,指向的常量值不可以改
const char *p; //和char const *p
------------------------------------------------------
2. char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
打印结果是什么?
解答:结果是:0 0 1 1
str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域
-----------------------------------------------
3. 以下代码中的两个sizeof用法有问题吗?
void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母
{
for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )
if( 'a'<=str[i] && str[i]<='z' )
str[i] -= ('a'-'A' );
}
char str[] = "aBcDe";
cout << "str字符长度为: " << sizeof(str)/sizeof(str[0]) << endl;
UpperCase( str );
cout << str << endl;
答:函数内的sizeof有问题。
根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。
函数外的str是一个静态定义的数组,因此其大小为6,
函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。
-------------------------------------------------
4. main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
输出结果是什么?
答案:输出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int *ptr=(int *)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
a是长度为5的int数组指针,所以要加 5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,
a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].
--------------------------------------------
5. 请问以下代码有什么问题:
int main()
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return 0;
}
答案:没有为str分配内存空间,将会发生异常。问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
---------------------------------------------
6. char* s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
有什么错?
答案:
"AAA"是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。
cosnt char* s="AAA";
然后又因为是常量,所以对是s[0]的赋值操作是不合法的。
---------------------------------------------
7. int (*s[10])(int) 表示的是什么?
答案:int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。
---------------------------------------------
8. 有以下表达式:
int a=248; b=4;
int const c=21;
const int *d=&a;
int *const e=&b;
int const *f const =&a;
请问下列表达式哪些会被编译器禁止?为什么?
*c=32;d=&b;*d=43;e=34;e=&a;f=0x321f;
答案:
*c 这是个什么东东,禁止
*d 说了是const, 禁止
e = &a 说了是const 禁止
const *f const =&a; 禁止
------------------------------------------
9. #include <stdio.h>
#include <stdlib.h>
void getmemory(char *p)
{
p=(char *) malloc(100);
strcpy(p,"hello world");
}
int main( )
{
char *str=NULL;
getmemory(str);
printf("%s/n",str);
free(str);
return 0;
}
分析一下这段代码
答案:程序崩溃,getmemory中的malloc 不能返回动态内存, free()对str操作很危险
-----------------------------------------
10. char szstr[10];
strcpy(szstr,"0123456789");
产生什么结果?为什么?
答案:长度不一样,会造成非法的OS "/0"
------------------------------------------
11.要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;
那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
答案:*((void (*)( ))0x100000 ) ( );
首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
用typedef可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();
------------------------------------------
12. 分析下面的程序:
void GetMemory(char **p,int num)
{
*p=(char *)malloc(num);
}
int main()
{
char *str=NULL;
GetMemory(&str,100);
strcpy(str,"hello");
free(str);
if(str!=NULL)
{
strcpy(str,"world");
}
printf(" str is %s",str); 软件开发网 www.mscto.com
getchar();
}
问输出结果是什么?
答案:输出str is world。
free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.
此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,
尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。
这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。
当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存 前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。
-------------------------------------------
13.char a[10];
strlen(a)为什么等于15?
#include "stdio.h"
#include "string.h"
void main()
{
char aa[10];
printf("%d",strlen(aa));
}
答案:sizeof()和初不初始化,没有关系;
strlen()和初始化有关。
--------------------------------------------
14.char (*str)[20];/*str是一个数组指针,即指向数组的指针.*/
char *str[20];/*str是一个指针数组,其元素为指针型数据.*/
---------------------------------------------
15.
#include<iostream.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct AA
{
int b1:5;
int b2:2;
}AA;
void main()
{
AA aa;
char cc[100];
strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz");
memcpy(&aa,cc,sizeof(AA));
cout << aa.b1 <<endl;
cout << aa.b2 <<endl;
}
输出结果是多少?
答案:-16和1
首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.经过strcpy和memcpy后,aa的4个字节所存放的值是: 0,1,2,3的ASC码,即00110000,00110001,00110010,00110011所以,最后一步:显示的是这4个字节的前5位,和 之后的2位分别为:10000,和01,因为int是有正负之分
-----------------------------------------------
位域--b1作为一个int型只占5bits; b2更可怜,只占2bits;
字母'0'在内存中是0011 0000(二进制,ascii码)
内存拷贝后b1占后5位,即10000(二进制), 因为b1是符号数, 所以是-16
b2占接下来的2位,即01(二进制),所以是1
//void *memcpy(void *dest, const void *src, size_t n);
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
1.source和destin所指的内存区域可以重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针。
2.strcpy和memcpy主要有以下3方面的区别。
2.1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2.2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符" "才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
2.3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。
注意:source和destin都不一定是数组,任意的可读写的空间均可。
在计算机系统中,数值一律用补码来表示(存储). 使用补码,可以将符号位和其他位统一处理;同时,减法也可按加法来处理.另外,两个用补码表示的数据相加时候,如果最高位(符号位)有进位,则进位被舍弃.
2 补码与原码的转换过程几乎是相同的 数值的补码表示也分两种情况:
(1)正数的补码:与原码相同. 例如,+9的补码是00001001
(2)负数的补码:符号位为1,其余位为该数绝对值的原码按位取反;然后整个数加1 例如,-7的补码:因为是负数,则符号位为"1",整个为10000111;其余7位为-7的绝对值+7的原码 0000111按位取反为1111000;再加1,所以-7的补码是11111001. 已知一个数的补码,求原码的操作分两种情况:
(1)如果补码的符号位为"0",表示是一个正数,所以补码就是该数的原码.
(2)如果补码的符号位为"1",表示是一个负数,求原码的操作可以是:符号位为1,其余各位取反,然后再整个数加1.
例如,已知一个补码为11111001,则原码是10000111(-7):
因为符号位为"1",表示是一个负数,所以该位不变,仍为"1";
其余7位1111001取反后为0000110;再加1,所以是10000111.
在"闲扯原码、反码、补码"文件中,
没有提到一个很重要的概念"模".
我在这里稍微介绍一下"模" 的概念: "模"是指一个计量系统的计数范围.如时钟等.计算机也可以看成一个计量机器, 它也有一个计量范围,即都存在一个"模".
例如:时钟的计量范围是0~11,模=12. 表示n位的计算机计量范围是0~2^n-1,模=2^n.
【注:n表示指数】"模"实质上是计量器产生"溢出"的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数.任何有模的计量器,均可化减法为加法运算.
例如: 假设当前时针指向10点,而准确时间是6点,调整时间可有以下两种拨法:
一种是倒拨4小时,即:10-4=6
另一种是顺拨8小时:10+8=12+6=6
在以12模的系统中,加8和减4效果是一样的,因此凡是减4运算,都可以用加8来代替.对"模"而言,8和4互为补数.实际上以12模的系统中,11和1,10和2,9和3,7和5,6和6都有这个特性.
共同的特点是两者相加等于模,对于计算机,其概念和方法完全一样,n位计算机,设n=8, 所能表示的最大数是11111111,若再 加1称为100000000(9位),但因只有8位,最高位1自然丢失.又回了00000000,所以8位二进制系统的模为2^8. 在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了.把补数用到计算机对数的处理上,就是补码.
if(x==0.0)转换为if((x>=-EPSINON)&&(X<=EPSINON) //EPSINON为允许的误差(精度)