1. char p[]和char *p的区别。
(1)
char *GetString(void)
{
char p[] = "Hello world";
return p;
}
void main()
{
char *str = NULL;
str = GetString();
cout << str << endl;
}
(2)
char *GetString(void)
{
char *p = "Hello world";
return p;
}
void main()
{
char *str = NULL;
str = GetString();
cout << str << endl;
}
为什么(1)输出的是乱码,(2)能够输出 hello world?
"Hello world"作为静态字符串实际上存储在文字常量区[参见:栈和堆的区别],但写程序的人不知道这个地址,而程序本身知道。当某一函数以 { char p[] = "Hello world"; ...} 方式使用此静态字符串时,实际上相当于:
char p[12];
strcpy(p, "Hello world");
p[12]是在栈里临时分配的。虽然p指向的内容是"Hello world", 但是这是复制品,不是原件。当函数结束,char p[]就被程序回收了,所以p[]的内容就不再是"Hello world"了。
但如果以char *p="Hello world"的方式使用,p指向的是静态字符串存储的位置,也就是说指向"Hello world"的原件,当然没有问题了。(注意此种形式不能在程序中修改字符串。)
通过单步调试可以看到两者的区别。
2.malloc和calloc的区别。
void* malloc(unsigned size);
void* calloc(size_t nelem, size_t elsize);
void* realloc(void* ptr, unsigned newsize);
三个函数都在stdlib.h函数库内,是C语言的标准内存分配函数。
1. 函数malloc()和calloc()都可以用来动态分配内存空间。
malloc()函数有一个参数,即要分配的内存空间的大小,malloc 在分配内存时会保留一定的空间用来记录分配情况,分配的次数越多,这些记录占用的空间就越多。另外,根据 malloc 实现策略的不同,malloc 每次在分配的时候,可能分配的空间比实际要求的多些,多次分配会导致更多的这种浪费。当然,这些都和 malloc 的实现有关;calloc()函数有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小。如果调用成功,它们都将返回所分配内存空间的首地址。
2. 函数malloc()和函数calloc()的主要区别是前者不能初始化所分配的内存空间,而后者能。
3. realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变。当然,对于缩小,则被缩小的那一部分的内容会丢失。
4. realloc 并不保证调整后的内存空间和原来的内存空间保持同一内存地址。相反,realloc 返回的指针很可能指向一个新的地址。所以在代码中,我们必须将realloc返回的值,重新赋值给 p :
p = (int *) realloc (p, sizeof(int) *15);
3 C语言宏定义的高级应用
关于#和##在C语言的宏中的应用
1.#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。
2.##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。
#include <stdio.h>
#define myprint(x) printf("The param"#x" is %d/n", param ## x)
int main()
{
int param1=2;
int param2=3;
int param3=4;
myprint(1);
myprint(2);
myprint(3);
}
结果:
The param1 is 2
The param2 is 3
The param3 is 4
Press any key to continue
参考:C语言宏的高级应用
4.交换任意类型的两个数,不用中间变量。
a=a+b;
b=a-b;
a=a-b;
5.不用循环判断一个int型数是否是2的幂。
方法一:n&(n-1)
#include<stdio.h>
int main()
{
int n;
while(1)
{
scanf("%d", &n);
(n&(n-1)) ? printf("no/n") : printf("yes/n");
}
return 0;
}
方法二:递归,不算好方法(和循环类似)。
#include <stdio.h>
int fun(int a)
{
if(a==1)return 1;
if(a%2==0 && fun(a/2))return 1;
else return 0;
}
int main()
{
int a;
while(1)
{
scanf("%d", &a);
printf("%d/n", fun(a));
}
return 0;
}
6.cache(缓存)和虚拟存储技术。
CPU缓存(Cache Memory)位于CPU与内存之间的临时存储器,它的容量比内存小但交换速度快。在缓存中的数据是内存中的一小部分,但这一小部分是短时间内CPU即将访问的,当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。由此可见,在CPU中加入缓存是一种高效的解决方案,这样整个内存储器(缓存+内存)就变成了既有缓存的高速度,又有内存的大容量的存储系统了。缓存对CPU的性能影响很大,主要是因为CPU的数据交换顺序和CPU与缓存间的带宽引起的。
缓存的工作原理是当CPU要读取一个数据时,首先从缓存中查找,如果找到就立即读取并送给CPU处理;如果没有找到,就用相对慢的速度从内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。
正是这样的读取机制使CPU读取缓存的命中率非常高(大多数CPU可达90%左右),也就是说CPU下一次要读取的数据90%都在缓存中,只有大约10%需要从内存读取。这大大节省了CPU直接读取内存的时间,也使CPU读取数据时基本无需等待。总的来说,CPU读取数据的顺序是先缓存后内存。
虚拟存储器是一个逻辑模型,并不是一个实际的物理存储器。若允许逻辑地址空间大于主存的实际空间,那么好像计算机系统为用户提供了一个比主存的实际容量大的主存储器。当主机内存中应用程序过多时,可以把暂时不用的程序区域复制到外存储器上,把用户当前要运行的程序快调度到内存中。在这过程中用户查觉不到这一调度过程,这样就好像为用户提供了一个远大于实际内存的空间。
虚拟存储的调度策略分为:页式、段式、段页式。
cache和虚拟存储都基于程序的局部性原理。
7.不用多余存储空间降字符串后面3位挪到字符串前面。
#include <stdio.h>
#include <string.h>
int main()
{
int i;
char str[] = "zhaokeXYZ";
for(i=strlen(str)-4; i>=0; i--)
{
str[i] ^= str[i+3];
str[i+3] ^= str[i];
str[i] ^= str[i+3];
//str[i] ^= str[i+3] ^= str[i] ^= str[i+3];
}
printf("%s/n", str);
return 0;
}
8. 类重载、隐藏与覆盖区别
成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无.
覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字.