一。 内联函数和宏的差别
内联函数和普通函数相比可以加快程序运行的速度,因为不需要中断调用,在编译的时候内联函数可以直接被镶嵌到目标代码中。而宏只是一个简单的替换。
内联函数要做参数类型检查,这是内联函数跟宏相比的优势。
inline是指嵌入代码,就是在调用函数的地方不是跳转,而是把代码直接写到那里去。对于短小的代码来说,
inline可以带来一定的效率提升,而且和C时代的宏函数相比,inline更安全可靠。可是这个是以争价空间消耗为代价的。至于是否需要inline
函数,就需要根据你的实际情况取舍了。
inline一般只用于如下情况:
(1)一个函数不断被重复调用
(2)函数只有简单的几行,且函数内不包含for、while、 switch语句。
一般来说,我们些写小程序没有必要定义成inline,但是如果要完成一个工程项目,当一个简单函数被调用多次时,则应该考虑用inline。
宏在C语言里极其重要,而在C++用得就少多了。关于宏的第一规则是:绝不应该去使用它,除非你不得不这样做。几
乎每个宏都表明了程序设计语言里或者程序里或者程序员的一个缺陷,因为它将在编译器看到程序的正文之前重新白布这些正文。宏也是许多程序设计工具的主要麻
烦。所以,如果你使用了宏,你就应该准备着只能从各种工具(如推错系统、交叉引用系统、轮廓程序等)中得到较少的服务。
宏是在代码处不加任何验证的简单替换,而内联函数是将代码直接插入调用处,而减少了普通函数调用时的资源消耗。
宏不是函数,只是在编译前(编译预处理阶段)将程序中有关字符串替换成宏体。
inline函数是函数,但在编译中不单独产生代码,而是将有关代码嵌入到调用处。
inline fact(float i) {return i*i}; //没有些返回值的
printf("bb= %d",fact(8)); //调用就是执行printf("bb= %d", 8*8);
|
二. 用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)(美国某著名计算机嵌入式公司2005年面试题)
#define SECONDS_PER_YEAR (60*60*24*365)UL
意识到这个表达式将使一个16位机的整型数溢出,因此要用到长整型符号L,告诉编译器这个常数是长整型数。
三. const
1. What does the keyword "const" means in C program? Please at least
make two examples about the usage of const.(中国台湾某著名CPU生产公司2005年面试题)
在C程序中,const的用法主要有定义常量、修饰函数参数、修饰函数返回值等3个用处。在C++程序中,它还可以修饰函数的定义体,定义类中某个成员函数为恒态函数,即不改变类中的数据成员。
(1)可以定义const 常量(2)const可以修饰函数的参数和返回值,甚至函数的定义体。被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
2. const与 #define相比有什么不同?
C++语言可以用const定义常量,也可以用#define定义常量,但是前者比后者有更多的优点:
(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意料不到的错误(边际效应)
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。
常量的引进是在早期的C++版本中,当时标准C规范正在制定。那时,常量被看作一个好的思想而被包含在C中。但是,C中的const的意识是“一个
不能被改变的普通变量”。在C中,它总是占用内存,而且它的名字是全局符。C编译器不能把const看成是一个编译期间的常量。在C中,如果写:
const bufsize = 100;
char buf[bufsize];
尽管看起来好像做了一件合理的事情,但这将得到一个错误的结果。因为bufsize占用内存的某个地方,所以C编译器不知道它在编译时的值。在C语言中可以选择这样书写:
const bufsize;
这样写在C++中是部队的,而C编译器则把它作为一个声明,这个声明指明在别的地方有内存分配。因此C默认const是外部连接的,C++默认const是内部连接的。这样,如果在C++中想完成与C中同样的事情,必须用extern把内部连接改写外部连接:
extern
const bufsize;
这种方法也可用在C语言中。在C语言中使用限定符const不是很有用,即使是在常数表达式里(必须在编译期间被求出)想使用一个已命名的值,使用const也不是很有用的。C迫使程序员在预处理里使用#define。
四. 各种指针的定义
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that
take an integer argument and return an integer
五. const 的用法:
- const int a; (== int const a) (a不可更改)
- const int *a (a 指向的整形数不可改, 但a可以)
- int const *a (a 不可改, 但a指向的整型数可以)
- const int const *a (a 和 a指向的整型数都不可改)
六. volatile
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
- 并行设备的硬件寄存器(如:状态寄存器)
- 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
- 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
一个参数既可以是const还可以是volatile吗?解释为什么。
一个指针可以是volatile 吗?解释为什么。
下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
这段代码有点变态。这段代码的目的是用来返指针*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;
}
七. typedef 和 #define
在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么? 这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一个扩展为
struct s * p1, p2;
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。
八. 关于堆和栈
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456");
//123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
阅读(668) | 评论(0) | 转发(0) |