协调

ODBC 应用程序中的对齐问题通常并不不同于任何其他应用程序中的对齐问题。 也就是说,大多数 ODBC 应用程序在对齐时很少或没有问题。 地址不对齐的惩罚因硬件和操作系统而异,可能轻则是性能上的轻微损失,重则会导致致命的运行时错误。 因此,特别是 ODBC 应用程序和可移植 ODBC 应用程序,应注意正确对齐数据。

ODBC 应用程序遇到对齐问题的一个示例是,当它们分配大型内存块并将该内存的不同部分绑定到结果集中的列时。 当泛型应用程序必须在运行时确定结果集的形状并相应地分配和绑定内存时,很可能发生这种情况。

例如,假设应用程序执行用户输入的 SELECT 语句,并从此语句中提取结果。 由于编写程序时此结果集的形状未知,因此应用程序必须在创建结果集并相应地绑定内存后确定每列的类型。 执行此作的最简单方法是分配一个大型内存块,并将该块中的不同地址绑定到每一列。 若要访问列中的数据,应用程序会将与该列绑定的内存进行类型转换。

下图显示了一个示例结果集,以及如何使用每个 SQL 数据类型的默认 C 数据类型绑定内存块。 每个“X”表示单个字节内存。 (此示例仅显示绑定到列的数据缓冲区。这样做是为了简单起见。在实际代码中,长度/指示器缓冲区也必须对齐。

默认情况下,将 C 数据类型绑定到 SQL 数据类型

假设绑定地址存储在 Address 数组中,应用程序使用以下表达式访问绑定到每列的内存:

(SQLCHAR *)       Address[0]  
(SQLSMALLINT *)   Address[1]  
(SQLINTEGER *)    Address[2]  

请注意,绑定到第二列和第三列的地址以奇数字节开头,并且绑定到第三列的地址不能被四列分割,这是 SDWORD 的大小。 在某些计算机上,这不会是个问题;对其他人来说,这将导致轻微的性能损失:在其他人中,它将导致致命的运行时错误。 更好的解决方案是在其自然对齐边界上对齐每个绑定地址。 假设这是 UCHAR 的 1,SWORD 为 2,SDWORD 为 4,则会给出下图所示的结果,其中“X”表示使用的内存字节,而“O”表示未使用的内存字节。

按自然对齐边界 绑定

虽然此解决方案不使用应用程序的所有内存,但它不会遇到任何对齐问题。 遗憾的是,实现此解决方案需要大量代码,因为每个列必须根据其类型单独对齐。 更简单的解决方案是将所有列对齐到最大对齐边界的大小,在下图所示的示例中,该大小为 4。

按最大对齐边界 绑定

尽管此解决方案留下了较大的漏洞,但实现它的代码相对简单且快速。 在大多数情况下,这抵消了未使用内存的代价。 有关使用此方法的示例,请参阅 Using SQLBindCol