读书笔记之知识杂点
1.x=x+1,x+1,x++,哪个效率最高?为什么?
x=x+1最低,因为它的执行过程如下:
(1)读取右x的地址
(2)x+1
(3)读取左x的地址
(4)将右值传给左边的x(编译器并不认为左右的x的地址相同)
x+=1其次,其执行过程如下:
(1)读取右x的地址
(2)x+1
(3)将得到的值传给x(因为x的地址已经读出)
x++效率最高,其执行过程如下
(1)读取右x的地址
(2)x自增1
2.
int main()
{
int i = 3,j,k;
j = product(i++);
k = product(++i);
printf(“j = %d,k = %d",j,k);
return 0;
}
输出结果是什么?
product(i++) = i++*i++;i = 3,先取i计算,然后i自加2次,所以j等于9,此时i等于5.product(++i)要求先累加i,累积后i等于7,所以product(++i)的结果是49.
3.A://a is a variable
写法1:
{
a++;
}
写法2:
{
a++;
}
B:
写法1:
{
X = i+Y+J*7;
printf(“%d”,X);
}
写法2:
for(i = 0;i<8;i++)
{
printf(“%d”,i+S);
}
A和B中各有两种写法,其中那一种写法比较好?
A.写法1好,'A’ == a,如果把"=="误写为"="的话,因为编译器不允许对常量赋值,就可以检查到错误。
B.写法2好,将部分加法运算放到了循环体外,提高了效率。缺点是程序不够简洁。
3.
int b = –5;
(a+b>0)?a++:b++;
cout<<a<<endl;
cout<<b<<endl;
则输出a,b的值为多少?
a = 4;
b = –5;
a是unsigned int型的,再与b相加时,b转换为unsigned型的,故结果为4294967295而不是-2。
4.在C++程序中调用被C编译器编译后的函数,为什么要加extern “C”?
C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int x,int y)。该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C连接交换指定符号extern “C” 解决名字匹配问题。
5.如何判断一段程序是由C编译程序还是由C++编译程序编译的?
C++编译时定义了_cplusplus。
C编译时定义了STDC。
6.main主函数执行完毕后,是否可能会在执行一段代码?
如果需要加入一段在main退出后执行的代码,可以使用atexit()函数注册一个函数:
#include<stdlib.h>
using namespace std;
//int atexit(void (*function)(void));
void fun1(),fun2(),fun3(),fun4();
int main()
{
atexit(fun1);
atexit(fun2);
atexit(fun3);
atexit(fun4);
printf("This is excute first.\n");
}
void fun1()
{
printf("Next.\n");
}
void fun2()
{
printf("excute ");
}
void fun3()
{
printf("is ");
}
void fun4()
{
printf("This ");
}
atexit()用来设置一个程序正常结束前调用的函数。当程序通过调用exit()或从main中返回时,参数function所指定的函数会先被调用,然后才真正由exit()结束程序。
如果执行成功则返回0,否则返回-1,失败原因存于errno中。
7.宏定义,下面的代码输出结果是什么?
main()
{
int a,b=3;
a = SQR(b + 2);
printf(“\n%d”,a);
}
结果应为11
宏定义展开时容易造成二义性问题。a = SQR(b + 2)这一语句展开后为"b + 2 * b + 2”,而不是想象中的"(b + 2)*(b + 2)"。要是先得到这个结果必须把宏定义语句改为如下的形式:
#define SQR(x) ((x)*(x))
这样输出的结果为25。
8.程序的输出结果是什么?
using namespace std;
char* GetMemory()
{
char p[] = “Hello world”;
return p;
}
int main()
{
char *ch = NULL;
ch = GetMemory();
cout<<ch;
return 0;
}
程序输出可能是乱码,也可能正常输出,因为GetMemory返回的是指向"栈内存"的指针,该指针的地址不是NULL,但其原来的内容已经被清除,新内容不可知。
9.函数指针:void (*p)()
函数返回指针:void * p()
const指针:const int *
指向const的指针:int * const
指向const的const指针:const int * const
10.int a[9] = {0,1,2,3,4,5,6,7,8};
int *p = a;
数组名代表数组的首地址,是一个常量指针,是不可以修改的,a++是错误的,p是个指针变量是可以修改的,如同时表示a[1]的话,可表示为*(a+1),*(p+1),*(++p)
11.
for(x = 0;x<3;x++)
y += *(p – 4 * x);
printf(“\n%d”,y);
输出结果是什么?
27,三次循环相加的数分别为17 + 9 + 1,p - n相当于p的地址减去n*4(整型类型的大小).
12.float (**def)[10]
def是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是float。
double *(*gh)[10]
gh是一个指针,它指向一个一维数组,数组元素都是double*。
double (*f[10])()
f是一个数组,f有10个元素,元素都是函数的指针,指向的函数类型是没有参数且返回double的函数。
int *((*b)[10])
就跟"int *(*b)[10]”是一样的,b是一维数组的指针。
Long (*fun)(int)
函数指针。
int (*(*F)(int,int))(int)
F是一个函数的指针,指向的函数的类型是有两个int参数并且返回一个函数指针的函数,返回的函数指针指向又一个int参数且返回int的函数。
13.句柄地址(稳定)->记载着对象在内存中的地址->对象在内存中的地址(不稳定)->实际对象。
14.一个射击运动员打靶,靶一共有10环,连开10枪打中90环的可能性有多少种?使用递归实现
using namespace std;
int sum;
int store[10];
void Output()
{
for(int i = 9;i>=0;--i)
{
cout<<store[i]<<" ";
}
cout<<endl;
++sum;
}
void Cumput(int score,int num)
{
if(score<0||score>(num+1)*10)
{
return ;
}
if(num == 0)
{
store[num] = score;
Output();
return ;
}
for(int i = 0;i<=10;i++)
{
store[num] = i;
Cumput(score - i,num - 1);
}
}
int main()
{
Cumput(90,9);
cout<<"总数:"<<sum<<endl;
return 0;
}
15.C++中空类默认产生那些类成员函数?
构造函数、析构函数、赋值函数、拷贝构造函数。
16.
using namespace std;
class A
{
public:
void virtual f()
{
cout<<”A”<<endl;
}
};
class B:public A
{
public:
void virtual f()
{
cout<<”B”<<endl;
}
};
int main()
{
A* pa = new A();
pa->f();
B* pb = (B*)pa;
pb->f();
delete pa,pb;
pa = new B();
pa->f();
pb = (B*)pa;
pb->f();
return 0;
}
程序输出A A B B
这是一个虚函数覆盖虚函数的问题。A类里的f函数是一个虚函数,虚函数是被子类同名函数所覆盖的。而B类里的f函数也是一个虚函数,它覆盖A类f函数的同时,也会被它的子类覆盖。但是在B* pb = (B*)pa;里面,该语句的意思是转化pa为B类型并新建一个指针pb,将pa复制到pb,但这里有一点请注意,就是pa的指针始终没有发生变化。所以pb也指向pa的f函数。这里并不存在覆盖的问题。
delete pa,pb;删除了pa和pb所指向的地址,但pa,pb指针并没有删除,也就是我们通常所说的悬浮指针。现在重新给pa指向新地址,所指向的位置是B类的,而pa指针类型是A类的,所以就产生了一个覆盖。pa->f();的值是B。
pb = (B*)pa;转化pa为B类指针给pb赋值,但pa所指向的f函数是B类的f函数,所以pb所指向的f函数是B类的f函数。pb->f();的值是B。
17.我们知道,大多数主流的语言或运行环境都支持3种最基本的内存分配方式,他们分别是:
静态分配:静态变量和全局变量的分配形式。我们可以把静态分配的内存看成是家里的耐用家具。通常,他们无需释放和回收,因为没有人会天天把大衣柜当做垃圾扔到窗外。
自动分配:在栈中为局部变量分配内存的方法,栈中的内存可以随着代码块退出时的出栈操作被自动释放。这类似于到家中串门的访客,天色一晚就要各自回各家,除了个别不识时务者以外,我们一般没必要把客人捆在垃圾袋里扫地出门。
动态分配:在堆中动态分配内存空间以存储数据的方式。堆中的内存块好像我们日常使用的餐巾纸,用过了就得扔到垃圾箱里,否则屋内就会满地狼藉。像我这样的懒人做梦都想有一台家用机器人跟在身边打扫卫生。在软件开发中,如果你懒得释放内存,那么你也需要一台类似的机器人——这其实就是一个有特定算法实现的垃圾收集器。
18.OSI七层模型
1>.物理层涉及到在信道上传输的原始比特流
2>.数据链路层的主要任务是加强物理层传输原始比特流的功能,使之对应的网络层显现为一条无错线路。发送包把输入数据封装在数据帧,按顺序传送出去并处理接受方回送的确认帧。
3>.网络层关系到子网的运行控制,其中一个关键问题是确认从源端到目的端如何选择路由。
4>.传输层的基本功能是从会话层接受数据而且把其分成较小的单元传递给网络层。
5>.会话层允许不同机器上的用户建立会话关系。
6>.表示层用来完成某些特定的功能。
7>.应用层包含着大量人们普遍需要的协议。
19.存储过程和函数的区别是什么?
存储过程是用户定义的一系列SQL语句的集合,涉及特定表或其他对象的任务,用户可以调用存储过程。而函数通常是数据库已定义的方法,它接受参数并返回某种类型的值,并且不涉及特定用户表。
游标的作用是什么?如何知道游标已经到了最后?
游标用于定位结果集的行。通过判断全局变量@@FETCH_STATUS可以判断其是否到了最后。通过此变量不等于0表示出错或到了最后。
20.多维数组和指针
char (*p)[4];
char *p1
char **p2;
p = a; //正确
p1 = *a; //正确
p2 = a; //错误
a是一个二维数组名,一个指向一维数组的指针
p是一个指向含有4个元素的一维数组的指针
p1是一个指向字符型变量的指针,*a相当于a[0],而a[0]是一个一维数组名,代表一维数组首元素的地址即&a[0][0],a[0][0]是一个字符型变量,所以正确
p2是一个指向字符型指针的指针