清华科技园笔试面试
来源:互联网 发布:用c语言编写科学计算器 编辑:程序博客网 时间:2024/04/27 15:54
先是笔试:
指向数组的指针和指针数组
<storage>type
int*pai[5];
<storage>type(*arr_pointer)[constant]//数组指针
关于const
const char *pc="Pointer toconstant!"
指向常量的字符型指针
char *consr pc1="Aconstant Pointer"!
字符型的常指针
win32中:
char1字节
int
long4字节
double8字节
编程memcpy
#include<stdio.h>
2
3
4
5
6
7
8
9
10
11
12
13
14
在c++中引用C编译的函数:
:如何在同一程序中混合使用C++和C
许多年来,你一直担心编制程序时一部分使用C++一部分使用C,就如同在全部用C编程的年代同时使用多个编译器来生成程序一样。没办法多编译器编程的,除非不同的编译器在与实现相关的特性(如int和double的字节大小,传参方式)上相同。但这个问题在语言的标准化中被忽略了,所以唯一的办法就是两个编译器的生产商承诺它们间兼容。C++和C混合编程时同样是这个问题,所以在实体混合编程前,确保你的C++编译器和C编译器兼容。
确认兼容后,还有四个要考虑的问题:名变换,静态初始化,内存动态分配,数据结构兼容。
* 名变换
名变换,就是C++编译器给程序的每个函数换一个独一无二的名字。在C中,这个过程是不需要的,因为没有函数重载,但几乎所有C++程序都有函数重名(例如,流运行库就申明了几个版本的operator<<和operator>>)。重载不兼容于绝大部分链接程序,因为链接程序通常无法分辨同名的函数。名变换是对链接程序的妥协;链接程序通常坚持函数名必须独一无二。
如果只在C++范围内,名变换不会影响你。如果你你有一个函数叫drawline而编译器将它变换为xyzzy,你总使用名字drawLine,不会注意到背后的obj文件引用的是xyzzy的。
如果drawLine位于C运行库中,那就是一个不同的故事了。你的C++源文件包含的头文件中申明为:
void drawLine(int x1, int y1, int x2, int y2);
代码体中通常也是调用drawLine。每个这样的调用都被编译器转换为调用名变换后的函数,所以写下的是
drawLine(a, b, c,d);
obj文件中调用的是:
xyzzy(a, b, c,d);
但如果drawLine是一个C函数,obj文件(或者是动态链接库之类的文件)中包含的编译后的drawLine函数仍然叫drawLine;没有名变换动作。当你试图将obj文件链接为程序时,将得到一个错误,因为链接程序在寻找一个叫xyzzy的函数,而没有这样的函数存在。
要解决这个问题,你需要一种方法来告诉C++编译器不要在这个函数上进行名变换。你不期望对用其它语言写的函数进行名变换,如C、汇编、Fortran、LISP、Forth或其它。(是的,这“其它”中应该包括COBOL,但那时你将得到什么?(Yes,what-have-you would include COBOL, but then what would you have?))总之,如果你调用一个名字为drawLine的C函数,它实际上就叫drawLine,你的obj文件应该包含这样的一个引用,而不是引用进行了名变换的版本。
要禁止名变换,使用C++的extern 'C'指示:
// declare a function called drawLine; don't mangle
// its name
extern "C"
void drawLine(int x1, int y1, int x2, int y2);
不要以为有一个extern 'C',那么就应该同样有一个extern 'Pascal'和extern'FORTRAN'。没有,至少在C++标准中没有。不要将extern'C'看作是申明这个函数是用C语言写的,应该看作是申明在个函数应该被当作好象C写的一样而进行调用。(使用术语就是,extern'C'意思是这个函数有C链接,但这个意思表达实在不怎么清晰。不管如何,它总意味着一件事:名变换被禁止了。)
例如,如果不幸到必须要用汇编写一个函数,你也可以申明它为extern 'C':
// this function is in assembler - don't mangle its name
extern "C" void twiddleBits(unsigned char bits);
你甚至可以在C++函数上申明extern'C'。这在你用C++写一个库给使用其它语言的客户使用时有用。通过禁止这些C++函数的名变换,你的客户可以使用你选择的自然而直观的名字,而不用使用你的编译生成的变换后的名字:
// the following C++ function is designed for use outside
// C++ and should not have its name mangled
extern "C" void simulate(int iterations);
经常,你有一堆函数不想进行名变换,为每一个函数添加extern 'C'是痛苦的。幸好,这没必要。extern'C'可以对一组函数生效,只要将它们放入一对大括号中:
extern "C"{
}
这样使用extern 'C'简化了维护那些必须同时供C++和C使用的头文件的工作。当用C++编译时,你应该加extern'C',但用C编译时,不应该这样。通过只在C++编译器下定义的宏__cplusplus,你可以将头文件组织得这样:
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
顺便提一下,没有标准的名变换规则。不同的编译器可以随意使用不同的变换方式,而事实上不同的编译器也是这么做的。这是一件好事。如果所有的编译器使用同样的变换规则,你会误认为它们生成的代码是兼容的。现在,如果混合链接来自于不同编译器的obj文件,极可能得到应该链接错误,因为变换后的名字不匹配。这个错误暗示了,你可能还有其它兼容性问题,早些找到它比以后找到要好。
* 静态初始化
在掌握了名变换后,你需要面对一个C++中事实:在main执行前和执行后都有大量代码被执行。尤其是,静态的类对象和定义在全局的、命名空间中的或文件体中的类对象的构造函数通常在main被执行前就被调用。这个过程称为静态初始化(参见ItemE47)。这和我们对C++和C程序的通常认识相反,我们一直把main当作程序的入口。同样,通过静态初始化产生的对象也要在静态析构过程中调用其析构函数;这个过程通常发生在main结束运行之后。
为了解决main()应该首先被调用,而对象又需要在main()执行前被构造的两难问题,许多编译器在main()的最开始处插入了一个特别的函数,由它来负责静态初始化。同样地,编译器在main()结束处插入了一个函数来析构静态对象。产生的代码通常看起来象这样:
int main(int argc, char *argv[])
{
}
不要注重于这些名字。函数performStaticInitializat
有时看起来用C写main()更有意义--比如程序的大部分是C的,C++部分只是一个支持库。然而,这个C++库很可能含有静态对象(即使现在没有,以后可能会有--参见ItemM32),所以用C++写main()仍然是个好主意。这并不意味着你需要重写你的C代码。只要将C写的main()改名为realMain(),然后用C++版本的main()调用realMain():
extern"C"
int realMain(int argc, char*argv[]);
int main(int argc, char*argv[])
{
}
这么做时,最好加上注释来解释原因。
如果不能用C++写main(),你就有麻烦了,因为没有其它办法确保静态对象的构造和析构函数被调用了。不是说没救了,只是处理起来比较麻烦一些。编译器生产商们知道这个问题,几乎全都提供了一个额外的体系来启动静态初始化和静态析构的过程。要知道你的编译器是怎么实现的,挖掘它的随机文档或联系生产商。