分类: C/C++
2013-10-18 16:06:21
1、指针和引用的区别?
答:引用是在C++中引入的。它们之间的区别有:
(1) 非空区别:指针可以为空,而引用不能为空
(2) 可修改区别:如果指针不是常指针,那么就可以修改指向,而引用不能
(3) 初始化区别:指针在定义时可以不用初始化,而引用在定义的同时必须初始化
2、为什么构造函数不能声明为虚函数?
答:因为虚函数采用的是虚调用的方法,虚调用是指允许在只知道部分信息的情况下的工作机制,特别允许我们调用一个只知道接口而不知道其对象的准确类型的函数。但是如果我们要调用构造函数创建对象时,必须要知道对象的准确类型,因此构造函数不能为虚函数。
3、char str1[]=”abc”; char str2[] = “abc”; str1==str2为FALSE,因为str1和str2是位于堆栈上的,它们占用不同的内存空间。Const char str3[] = “abc”; const char str4[] = “abc”;str3==str4为FALSE,同样它们是位于堆栈上的内存空间,是不同的。Const char *str5=”abc”, const char *str6=”abc”;char *str7=”abc”,char *str8 = “abc”,str5==str6 str7==str8为TRUE,因为”abc”是位于文字常量区的,系统会将几个“abc”进行优化,使它们位于同一块内存区,因此指针的指向也就相同了。
4、以下函数能求出数组的长度吗?
void fun(char str[])
{
int len = sizeof(str)/sizeof(str[0]);
}
答:不能,数组作为参数传递给函数时,数组名被退化为指针,因此函数中的sizeof(str)实际是在求一个指针的sizeof,答案为4,因此不能计算出数组的长度。
5、一个32位的机器,该机器的指针是多少位?
答:指针是多少位只要看地址总线的位数就行了,80386以后的机子都是32的数据总线。所以指针的位数就是4个字节。即有void *p; 则sizeof(p) = 4。
6、C和C++中的struct和class有什么不同?
答:C和C++中struct的区别是C中的struct不能有成员函数,而C++中的struct可以。C++中struct和class的主要区别是默认的存取权限,struct的默认存取权限为public,而class的默认存取权限为private。
7、类的静态成员和非静态成员有何区别?
答:类的静态成员每个类只有一个,静态成员为所有类的实例对象共享,静态成员有静态成员变量和静态成员函数,静态成员变量使用前必须初始化,静态成员变量可以被静态成员函数和非静态成员函数访问,而静态成员函数只能访问静态成员变量,因为静态成员函数属于类,其没有this指针。非静态成员每个对象都有一个。
8、纯虚函数的定义?
答:virtual void fun()=0;含有纯虚函数的类为抽象类,抽象类不能实例化对象,但是可以定义指针,纯虚函数是接口,由子类实现。
9、请讲一讲析构函数和虚函数的用法和作用?
答:析构函数是用于在撤销对象时完成对对象的清理工作,比如在创建对象时,如果在构造函数中动态申请了内存,那么在对象释放时,应该在析构函数中对动态申请的内存进行释放,避免造成内存泄露,如果在这个时候还不释放就没有机会释放内存了,会造成内存泄露,总之需要在释放对象之前完成的工作都可以放在析构函数中完成,析构函数不需要用户显示调用,它会在释放对象前由系统自动调用。虚函数是实现多态性的方式,虚函数在子类中被重写,因此可以通过基类指针调用不同子类的虚函数,实现一个接口,多种实现。正是因为多态性的存在,为了使子类的析构函数随时都能够执行,基类的析构函数一般都声明为虚析构函数。
10、全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
答:区别在于它们的作用域不同,全局变量可以在整个程序被使用,局部变量只能在子程序或函数中使用,函数执行完后,局部变量的也被销毁了。操作系统和编译器可能是通过它们所分配的内存区来知道的,全局变量被放在全局数据区,而局部变量放在堆栈中。
11、若类A和类B没有继承关系,对于函数void func(A&),请至少用两种不同方法说明如何才能传递一个非常量的B类对象非func函数。
答:可在A类中定义一个构造函数 A(const B&)
或在B类中定义一个自动转换函数:operator A() const
12、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
答:static全局变量与普通的全局变量的区别:前者在主函数之前就要被初始化,而后者没有要求,两者的作用域不同,前者的作用域只限于子模块(子程序或函数),而后者在整个程序中都可以被使用。
static局部变量和普通局部变量的区别:一个函数中的static变量值会保留到该函数下次调用来改变它,而后者在函数运行完后就被销毁了,两者的存储区域不同,前者存储在静态区(全局区),后者的内存位于堆栈上。
static函数与普通函数:static函数可以直接通过类调用,不需要在此之前实例化对象,而普通函数需要先定义对象。static函数不能用非static成员。static在循环中定义并赋值时,定义过程只进行一次,而不是每个循环1次。
13、写程序,从键盘上输入一系列整数,以输入-1为结束,将输入的数据保存到文件data.txt中。
答:C语言实现:
#include
int main()
{
FILE *fp;
if ((fp=fopen("data.txt","wb")) == NULL)
{
cout<<"open error"<
exit(1);
}
int x;
scanf(“%d”, &x);
while (x!=-1)
{
fputc(x, fp);
cin>>x;
}
fclose(fp);
return 0;
}
C++实现:
#include
#include
using namespace std;
int main()
{
ofstream fout("data1.txt");
if (!fout)
{
exit(1);
}
int x;
cin>>x;
while (x != -1)
{
fout<
cin>>x;
}
fout.close();
return 0;
}
14、写程序,将一个字符串倒序?
答:直接在main函数中实现的
void main()
{
char *source = "hello";
char *des;
int len = strlen(source);
des = (char *)malloc(len+1); //申请空间必须是len+1,加1是为了放结束符
if (!des)
{
exit(1);
}
char *s = &source[len-1];
char *d = des;
while (len--!=0)
{
*d++ = *s--;
}
*d = '\0'; //必须要
cout<
cout<
}
15、static的用途?
答:(1)函数体内的静态变量,其值在函数的调用过程中保持不变。跟局部变量的区别。
(2)在函数体外定义的静态变量,限制了它的使用范围只在于该子模块,该子模块内的函数都能访问它,但是子模块外不能访问,实际就类似于是一个本地的全局变量。与一般全局变量的区别。
(3)类的静态成员函数。
本质上来说,static就是声明了对象的生成期,限制了对象的作用域。
或 (1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只能被分配一次,因此其值在下次函数调用时仍维持上次的值。
(2)在模块内的static全局变量可以被模块内的所有函数访问,但不能被模块外其他函数访问。
(3)在模块内的static函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块。
(4)在类中的static成员变量属于整个类所有,对类的所有对象只有一份拷贝。
(5)在类中的static成员函数属于整个类所有,这个函数不接受this指针,因而只能访问类的static成员变量。
16、在C++程序中调用C编译后的函数,为什么要加extern C的声明?
答:因为C++支持函数重载,而C不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号 extern C来解决名字匹配问题。
17、float x与零值的比较,指针p与NULL的比较,布尔变量flag与TRUE的比较
答:(1)const float EPSION = 0.00001;
if (x >= -EPSION && x <= EPSION)
(2) if (p == NULL)
(3) if (flag)
18、C++中哪些函数不能被声明为虚函数?
答:普通函数(非成员函数),构造函数,内联成员函数、静态成员函数、友元函数。
(1)虚函数用于基类和派生类,普通函数所以不能
(2)构造函数不能是因为虚函数采用的是虚调用的方法,允许在只知道部分信息的情况的工作机制,特别允许调用只知道接口而不知道对象的准确类型的方法,但是调用构造函数即使要创建一个对象,那势必要知道对象的准确类型。
(3)内联成员函数的实质是在调用的地方直接将代码扩展开
(4)继承时,静态成员函数是不能被继承的,它只属于一个类,因为也不存在动态联编等
(5)友元函数不是类的成员函数,因此也不能被继承
19、include
答:<>是从标准库路径搜索, “”是从用户当前工作目录开始,找不到,在到标准库开始
20、void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:输出“hello”, 注意GetMemory函数中的p参数是指向指针的指针,如果是一级指针则出现错误,不会得到申请的地址空间。
21.void GetMemory(char *p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:运行出错,注意与20中参数p的类型比较,20中是char **p,而本题是char *p。而且像这种情况还会导致每次都泄露一块内存的危险。
22、char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:无效的指针,输出不确定,因为p是函数GetMemory中申请的是一个临时变量,函数调用完后,其空间已经不再存在。
23.char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
请问运行Test 函数会有什么样的结果?
答:输出”hello”,因为是返回动态申请的内存,只要不释放内存,即调用free函数,就可以使用该段内存。
24.char *GetString2(void)
{
char *p = "hello world";
return p;
}
void Test5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}
答:函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。如果此时还想利用strcpy往str中写数据时,将出现错误。如strcpy(str,”hello world”)。
25、编写strlen函数
答:
int Strlen(const char *str)
{
int len = 0;
assert(str != NULL);
while (*str++ != '\0')
{
len++;
}
return len;}
非空判断是必须进行的操作,可以使用断言的方式assert(str) != NULL才会继续
26、编写strcpy函数
答: char *StrCopy(char *strDes, const char *strSrc)
{
assert((strDes != NULL) && (strSrc != NULL));
char *address = strDes;
while ((*strDes++ = *strSrc++) != '\0')
;
return address;
}
首先必须判断两个指针是否为空,由于复制后的指针需要返回,因此需要一个指针来记录地址的初始值,最后将复制的结果返回是为了进行链式操作。
27、C语言的关键字
符号 |
意义 |
符号 |
意义 |
符号 |
意义 |
auto |
声明自动变量 |
int |
声明整形变量 |
short |
声明短整型变量 |
long |
声明长整形变量 |
float |
声明单精度浮点型变量 |
double |
声明双精度浮点型变量 |
char |
声明字符型变量 |
signed |
声明有符号变量 |
unsigned |
声明无符号变量 |
struct |
声明结构体变量 |
union |
声明共用体变量 |
enum |
声明枚举型变量 |
static |
声明静态变量 |
if |
条件语句 |
else |
条件语句 |
switch |
声明开关语句 |
case |
|
break |
|
while |
|
do |
|
continue |
|
register |
|
const |
|
volatile |
|
typedef |
|
extern |
|
return |
|
void |
|
for |
|
goto |
|
sizeof |
|
default |
|
|
|
28、什么是定义?什么是声明?它们的区别?
答:定义创建了对象并为这个对象分配了内存,声明没有分配内存。
29、最宽宏大量的关键字 auto
最快的关键字 register
最名不副实的关键字static,其作用包括,定义静态全局变量,静态局部变量,修饰函数限制函数的作用域为该函数所在的文件,其他文件不能访问。
30、字符串与整数之间的转换。
31、字符串循环右移n位。
32、栈的生长方向是向低地址扩展,而堆是向高地址扩展。
33、什么是“引用”?申明和使用“引用”要注意哪些问题?
答:引用就是某个目标变量的“别名”(alias),对引用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
34、将“引用”作为函数参数有哪些特点?
答:(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
35、在什么时候需要使用“常引用”?
答:如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名。
36、Heap和Stack的区别?
答:Heap是堆,Stack是栈。
栈的空间由操作系统自动分配和回收,而堆上的空间由程序员申请和释放。
栈的空间大小较小,而堆的空间较大。
栈的地址空间往低地址方向生长,而堆向高地址方向生长。
栈的存取效率更高。
程序在编译期间对变量和函数的内存分配都在栈上,且程序运行过程中对函数调用中参数的内存分配也是在栈上。
37、宏定义,将整形变量a的第n位清零,其他位不变。
答:#define clear_bit(a,n) a=((a|(int)pow(2,n-1))!=a)?a:(a^(int)pow(2,n-1))
首先判断数a的第n位本身是否为零,如果为零,则与2的n-1次方或运算后肯定不等于数a,此时就不需要清零,直接返回a即可,否则,将数a与2的n-1次方进行异或运算。
38、unsigned short A = 10;
printf(“%u\n”, ~A);
char ch = 128;
printf(“%d\n”, ch);
输出的结果是多少,并分析过程?
答:~A=4294967285,首先将A转化为int类型,即对应的二进制数值为:00000000 00000000 00000000 00001010,~A=11111111 11111111 11111111 11110101,其实这种情况最高位是1,认为是负数,但是在输出中指定以无符号数输出,于是结果为=4294967295(四字节表示的最大数)-10.
ch = 128对应的二进制为:10000000,在输出中以整数形式输出,由于最高位是1,于是就是负数,10000000是该负数的补码,根据求补码的反步骤计算,先-1,得到01111111,在取反得10000000=128,由于本身是负数,即为-128.
38、sizeof和strlen之间的区别?
答:(1)sizeof操作符的结果类型是size_t,它在头文件中的typedef为unsigned int类型,该类型保证能容纳实现所建立的最大对象的字节大小。
(2)sizeof是运算符,strlen是函数
(3)sizeof可以用类型做参数,strlen只能用char *做参数,且必须是以’\0’结尾的。
(4)数组做sizeof的参数不退化,传递给strlen就退化为指针。
(5)大部分编译程序在编译的时候就把sizeof计算过了,是类型或是变量的长度。
(6)strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占用内存的大小。
(7)sizeof后如果是类型必须加括号,如果是变量名可以不加括号。
(8)当使用了一个结构类型或变量时,sizeof返回实际的大小。
(9)数组作为参数传递给函数时传的是指针而不是数组,传递的是数组的首地址。
(10)计算结构变量的大小就必须讨论数组对齐问题。
(11)sizeof操作符不能用于函数类型,不完全类型或位字段。
39、sizeof的使用场合?
答:(1)sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。
(2)用它可以看看某种类型的对象在内存中所占的单元字节。
(3)在动态分配一对象时,可以让系统知道要分配多少内存。
(4)便于一些类型的扩充。
(5)由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof代替常量计算。
(6)如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。
40、内联函数和宏的差别?
答:内联函数和普通函数相比可以加快程序运行的速度,因为不需要中断调用,在编译的时候内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的替换。内联函数要做参数类型检查,这是与宏相比的优势。
Inline是指嵌入代码,就是在调用函数的地方不是跳转,而是把代码直接写到那里去。对于短小的代码来说,inline可以带来一定效率的提升,而且和C时代的宏函数相比,inline更安全可靠。可是这是以增加空间消耗为代价的。
Inline一般只适用于:一个函数被不断地重复调用;函数只有简单的几行,且函数内不能含有for while switch语句。
41、文件操作函数?
答:FILE *fp
fopen(“filename”, “rwatb+”)
char ch ch = fgetc(fp) fputc(ch, fp)
char str[n] fgets(str, n, fp) fputs(str, fp)
fread(buffer, size, count, fp) fwrite(buffer, size, count, fp)
fscanf(fp, “%c”, &ch) fprintf(fp, “%d”, ch)
fclose(fp)
rewind(fp)
fseek(fp, 偏移长度,SEEK_SET/SEEK_CUR/SEEK_END)
feof(fp)