由于最近去找工作,面过了很多题目,想起了当年学习C语言的很多往事。面试回来后精心整理了一些面试题目,并进行了分类,
一是对思路的整理,二是为后来者造福。
以下是各种题目:
#语法问题:
1.用typedef定义一个包含10个整型的类型。
注:typedefintNUMBER[10];
NUMBER声明为含有10个元素的数组类型。
NUMBERn;
n[0]=1;
n为含有10个元素的数组。
2.如何引用一个已经定义过的全局变量?
答:extern
可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个编写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
3.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
答:可以,在不同的C文件中以static形式来声明同名全局变量。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错.
4.static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?Static函数与普通函数有什么区别?
注:
(1)把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。
(2)把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。
(3)static函数与普通函数作用域不同,仅在本文件。
综上所述:
static全局变量与普通的全局变量有什么区别:
static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:
static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
4.结构体的位域
C语言允许在一个结构体重以位为单位来使用内存,这种以为为单位的成员称为位域或位段
structbit_data
{
inta:6;
intb:4;
intc:4;
intd;
}
注:0~5的6位存放a,6~9的4位存放b,10~13的4位存放c,14~31的18位空闲,32~63的32位存放d.
若某一位段要从另外一个存储单元开始存放,结构体中的成员可以定义成如下形式:
inta:6;
intb:4;
int:0;
intc:4;
intd;
注:a使用0~5的6位,b使用6~9的4位。c从下一个存储单元开始存放,即存放在32~35位,10~31位空闲。
structbit_data
{
inta:6;
intb:4;
int:8;
intc:4;
intd;
};
注:0~5位存放a,6~9位存放b,10~17位的8位强制空闲,18~21位存放c,22~31位的10位也空闲。32~63的32位存放d。
#运算符优先级,结合性:
位运算符号:& | ~ 异或^
1、
inta=5;
a=(a=3*5,a*4),a+5;
a=?
注:赋值运算符要优先于逗号运算符,括号优于赋值,故a=60;
如:
a=(1,2,3,4),5;
a=?
2.
inta=3;
a+=a-=a*a;
a=?
注:
“=”为自右向左结合。
第一步:
a=3;
a+=a-=9;
第二步:
a=-6;
a+=-6;
a=-12;
#
1.交换两个变量(整型)的值,且不允许使用中间值
inta,b;
a^=b;
b^=a;
a^=b;
注:a,b必须为整型变量
2.假设你只知道一个数组的数组名(a[]),如何确定这个数组的长度?
intlength=sizeof(a)/sizeof(a[0]);
#指针
1.区别int(*p)[5]和int*p[5]。
前者是一个指针,它指向一个含有5个元素的数组。后者是一个数组,它的长度为5,数组中每一个元素指向一个整型变量。
2.区别int*f(inti,intj)和int(*p)(inti,intj)
前者是返回指针的函数,它是一个函数的声明,后者是指向函数的指针,它定义了一个指针。
即前者是函数,后者为指针。
注:(1)不能对指向函数的指针做任何运算,如p++、p–、p+n、p-n都是错误的。
3.下面这个程序编译时会报错,请指出错误并改正。
#include<stdio.h>
int main(void)
{
int **p;
int arr[100];
p = &arr;
return 0;
}
注:数组名arr的&arr是一个指向长度为100的数组的指针,而p是指向指针(该指针指向的是int型变量)的指针。改成这样就正确了:
#include<stdio.h>
int main()
{
int (*p)[100];
int arr[100];
p = &arr;
return 0;
}
#内存
1.
charstring[]=“LinuxC”;
char*p=“LinuxC”;
string[0]='a';
p[0]='a';
注:”LinuxC”是一个字符串常量。C语言对于字符串常量通常是这样处理的:在内存中开辟一个字符数组来存储该字符串常量,并把开辟出的字符数组的首地址赋给p.
注:string[0]=‘a’是可以的,而p[0]=‘a’是非法的,因为p指向的是字符串常量,常量的内容不可改变。把p指向一个字符串常量或字符数组时合法的,例如:p=“HelloWorld!”;p=string;
2.下面这段小程序的输出是什么?
#include<stdio.h>
void main()
{
int a[5] = {1,2,3,4,5};
int *ptr = (int *)(&a+1);
printf(“%d,%d\n”,*(a+1),*(ptr-));
}
注:&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)。对于int*ptr=(int*)(&a+1),ptr是&(a[5]0,也就是a+5。ptr与(&a+1)类型是不一样的,所以ptr-1只会减去sizeof(int*)。a,&a的地址是一样的,但含义不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5]。*(a+1)就是a[1],*(ptr-1)是a[4],执行结果是2,5。
#逻辑编程题
1.用逻辑表达式、for循环语句求解逻辑题。5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果
A选手说:B第一,我第三。
B选手说:我第二,E第四。
C选手说:我第一,D第二。
D选手说:C最后,我第三。
E选手说:我第四,A第一。
比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。
结果是:A:2B:1C:1D:3E:4
程序如下:
#include <iostream>
using namespace std;
int main()
{
int p[6];//p[0] not used
int t[6];//t[0] not used
int i,j,k,m,n;
for(i = 1 ; i <= 5 ; i++){
for(j = 1 ; j <= 5 ; j++){
for(k = 1 ; k <= 5 ; k++){
for(m = 1 ; m <= 5 ; m++){
for(n = 1 ; n <= 5 ; n++){
p[1] = i;
p[2] = j;
p[3] = k;
p[4] = m;
p[5] = n;
t[1] = (p[2] == 1) + (p[1] == 3) ;
t[2] = (p[2] == 2) + (p[5] == 4) ;
t[3] = (p[3] == 1) + (p[4] == 2) ;
t[4] = (p[3] == 5) + (p[4] == 3) ;
t[5] = (p[5] == 4) + (p[1] == 1) ;
int flg = 1;
//检查:中间名次不能缺少,位操作
unsigned int res = 0;
for(int w = 1; w <=5 ; w++){
res |= 1 << (p[w]-1); //create id
}
while(res > 0){
if(res%2 == 0){
flg = 0;
}
res >>= 1;
}
//检查是否每人说对一半
for(w = 1; w <= 5; w++){
if(t[w] != 1){
flg = 0;
break;
}
}
if(flg == 1){// found Answer!
cout << "A:" << p[1] << " B:" << p[2] << " C:" << p[3] << " D:" << p[4] << " E:" << p[5] <<endl;
}
}
}
}
}
}
return 0;
}
2.日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。以下为4个嫌疑犯的供词。
A说:不是我。
B说:是C。
C说:是D。
D说:C在胡说
已知3个人说了真话,1个人说的是假话。现在请根据这些信息,写一个程序来确定到底谁是凶手。
int i,sum=0,flag=0;
char killer;
for(i=1;i<=4;i++)
{
killer =64 +i;
sum = (killer !=’A')+(killer ==’C')+(killer == ‘D’) +(killer!=’D');
if(sum ==3)
{
flag=1;
printf(“%c is the killer.\n”,killer);
break;
}
}
if(flag ==0)
printf(“Can not find\n”);
return 0;
3.??约瑟夫问题:
古代某法官要判决n个犯人死刑,他有一条荒唐的逻辑,将犯人首尾的相接排成圆圈,然后从第s个人开始数起,每数到第m个犯人,就拉出来处决;然后又数m个,数到的犯人又拉出来处决,依次类推。剩下的最后一人可以豁免。
编写程序,给出处决顺序,并给出哪一个人可以活下来。
参考:http://www.cppblog.com/Victordu/archive/2008/02/22/43082.html
http://blog.csdn.net/solofancy/article/details/4211770
#基本概念
1.模块化准则:把一个大问题分解为许多小问题,每个小问题由一个函数来解决,每个函数都完成一个特定的功能,各个小问题应该尽量独立,即所谓高内聚,低耦合。函数内部应该是高内聚,完成一些紧密相关的任务,函数之间应该只有一些非常必要的联系,即低耦合。
#简单编程题目
1.写一个程序,以递归方式反序输出一个字符串。如给定字符串“abc”输出“cba”。
#include<stdio.h>
void reverse(char *p)
{
if( *p == ‘\0′)
return;
reverse(p+1);
printf(“%c”,*p);
}
int main()
{
reverse(“abc”);
printf(“\n”);
return 0;
}
2.用递归的方法求一个有n个元素的int型数组的最大值。
int max(int a[],int n)
{
if(n == 0){
return a[0];
}else{
return max(a,n-1)>a[n]?max(a,n-1):a[n];
}
}
3.写一个实现字符串拷贝的函数。给定字符串拷贝函数strcpy的原型:
char*strcpy(char*dest,constchar*src);
要求:(1)不调用任何库函数。(2)说明函数为什么返回char*.
char *strcpy(char *dest,char *src)
{
if( (dest == NULL) || (src == NULL) )
{
return NULL;}
char *ret_string = dest;
while( *dest ++ = *src++)!=’\0′);
return ret_string;
}
4.利用数组可以实现高精度计算,方法是将大整数每位上的数字存储为数组的一个元素。对于
m=88200807199688
n=345678912345678
编写函数,实现大整数m、n的加、减、乘运算。
5.快速统计给定整数二进制形式中1的个数
int func( int x)
{
int count = 0;
while (x){
count ++;
x = x & (x-1);
}
return count;
}
6.请找出下面代码中的所有错误(题目不错,值得一看)
说明:以下代码是把一个字符串倒序,如“abcd”倒序后变为“dcba”
#include"string.h"
main()
{
char* src="hello,world";
char* dest=NULL;
int len=strlen(src);
dest=(char*)malloc(len);
char* d=dest;
char* s=src[len];
while(len--!=0)
d++=s--;
printf("%s",dest);
return 0;
}
注:
方法1:一共有4个错误;
int main()
{
char* src = "hello,world";
int len = strlen(src);
char* dest = (char*)malloc(len+1);//要为分配一个空间 char* d = dest;
char* s = &src[len-1]; //指向最后一个字符
while( len-- != 0 )
*d++=*s--;
*d = 0; //尾部要加’\0’
printf("%sn",dest);
free(dest); // 使用完,应当释放空间,以免造成内存汇泄露
dest = NULL; //防止产生野指针
return 0;
}