VC++编程之道读书笔记
第二篇
缪误21:位图数据是按照红绿蓝顺序存储的
大家都知道位图的颜色是由红、绿、蓝三个分量构成的,但是位图颜色数据存储的方式则不是按照这个顺序存储的,而是按照蓝、绿、红的顺序存储的。并且对于真彩色位图来说,位图的颜色数据是倒序存储的,即位图的第一行数据位于位图数据的最底部。
第三篇
细节12 :内存中的数组
在C++中通过数组可以操作内存,创建数组时需要为数组分配内存空间,操作数组时就是对内存空间中的数组元素进行操作。数组创建后,数组引用和数组元素分别存储在栈内存和堆内存中,并通过数组引用与数组元素的下标对数组元素进行操作。(数组引用即数组名)
细节18:使用省略号作为函数的参数
省略号参数代表的含义是函数的参数是不固定的,可以传递一个或多个参数。对于printf函数可以输出一项信息,也可以输出多项信息。
定义带省略号参数的函数如下:
void OutPutInfo(int num, ...)
{}
对于上述方式的函数,在编写函数体时需要一一读取用户传递的实际参数。可以使用va_list类型、va_start、va_arg、va_end 3个宏读取传递到函数中的参数值。
细节19:函数的重载
1、函数的返回值类型不能作为区分重载函数的一部分。
2、对于普通函数参数来说const关键字不作为区分重载函数的标识,但是如果参数的类型是指针或引用类型,则const关键字将作为重载函数的标识。因此,下面的函数重载是合法的:
bool Validate(const int *x){}
bool Validate(int *x){}
3、参数的默认值不作为区分重载函数的标识。
4、使用typedef自定义的类型不作为重载的标识
5、局部域中声明的函数将隐藏而不是重载全局域中的函数。
bool Validate(float x)
{
printf("浮点参数函数\n");
return (x>0) ? true : false;
}
int main(int argc, char **argv)
{
bool Validate(double x);
Validate(10.5f);
return 0;
}
此时main函数中的Validate将全局函数隐藏而不是重载了全局函数,需要注意的是,函数重载是发生在同一作用域下的同名函数。如果此时需要使用全局函数,可以使用全局作用域符::。
细节25:析构函数
注意析构函数的调用顺序与构造函数相反。
细节42:指针引用作为函数参数
void TestPtr(int* &pArray);
第七章
细节26:TCP粘包的烦恼
TCP协议为传输数据提供了可靠的保障。但是,对于技术开发人员来说,使用TCP协议有一个最大的烦恼,就是出现粘包的情况。通常,用户在对一组数据分组发送的时候,系统为了提高性能会将多个数据包组合到一起,然后统一发送。用户在接收数据包的时候,有时会一次接收到多个数据包,有时也会接收到一个不完整的数据包(数据包的余下部分在下次接收数据中),这要视当时的网络情况而定。这就是说用户在发送数据时,虽然逐个数据包发送,但是系统为了提高性能,会批量地发送多个数据包,而接收端每次接受的内容可能包含多个数据包,也可能包含一个数据包的一部分数据。例如,一次接受3个数据包数据、2.2个数据包数据或者0.4个数据包数据。这就要求开发人员在设计接收端时需要自己对各种情况进行处理。但是通常使用一个原则是自己定义的数据包的结构不要太大,也就是在接收端首次结束数据时,能够获取到一个完整的数据包结构。
细节29:套接字的阻塞模式
依据套接字执行方式的不同,可以将套接字分为两类,即阻塞套接字和非阻塞套接字。在阻塞套接字中,套接字函数的执行会一直等待,直到函数调用完成才返回。在非阻塞套接字中,套接字函数的调用会立即返回,将控制权交给程序。
默认情况下,套接字为阻塞套接字。为了将套接字设置为非阻塞套接字,需要使用ioctlsocket函数。例如,下面的代码在创建一个套接字后,将套接字设置为非阻塞套接字。
unsigned long nCmd;
SOCKET clientSock = socket(AF_INET, SOCK_STREAM, 0);
int nState = ioctlsocket(clientSock, FIONBIO, &nCmd);
if(0 != nState)
{
MessageBox("设置套接字非阻塞模式失败");
}
细节30:定义用户通信协议
协议是通信双方进行交互的基石。它定义了双方通信的规则。我们在开发网络应用程序时,通常都需要自己定义一个协议。例如,设计一个聊天程序,用户可能传递文本信息、文件数据或表情信息。每一种信息我们在封装数据包时都需要有一个标志位来标记数据的类型。接收端读取数据包中的协议,根据不同类型的数据进行相关操作。这就是一个简单的网络通信协议。其实,我认为http、ftp、telnet等等,这些就是这样定义出来的。
细节31:抓取网络数据包
当用户在局域网中传递数据时,处于同一网段内的所有计算机的网卡都会收到这些数据,尽管它不是传递数据的目的地。利用这一特点,我们就可以截获局域网中传递的数据。
为了能获取通过网卡的数据,需要创建原始套接字。用户可以将socket函数的第二个参数设置为SOCK_RAW来创建原始套接字。例如:
m_sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
由原始套接字获取的数据是IP数据报,而不仅仅是一个用户数据。它还包含IP数据头和相应协议的数据头,最后才是用户数据。为了获得用户数据,需要逐一去掉IP数据头及各个协议的数据头。
细节39:任务栏托盘菜单
要设计任务栏托盘菜单,需要使用Shell_NotifyIcon函数。