JavaScript is required to for searching.
跳过导航链接
退出打印视图
Oracle Solaris Studio 12.3:C 用户指南     Oracle Solaris Studio 12.3 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  C 编译器介绍

2.  特定于 C 编译器实现的信息

3.  并行化 C 代码

4.  lint 源代码检验器

5.  基于类型的别名分析

6.  转换为 ISO C

7.  转换应用程序以适用于 64 位环境

7.1 数据模型差异概述

7.2 实现单一源代码

7.2.1 派生类型

7.2.1.1 <sys/types.h>

7.2.1.2 <inttypes.h>

定宽整型

诸如 unintptr_t 的有用类型

常量宏

限制

格式字符串宏

7.2.2 使用 lint 检查

7.3 转换为 LP64 数据类型模型

7.3.1 整型和指针长度更改

7.3.2 整型和长型长度更改

7.3.3 符号扩展

7.3.4 指针运算而不是整数

7.3.5 结构

7.3.6 联合

7.3.7 类型常量

7.3.8 注意隐式声明

7.3.9 sizeof( ) 是无符号 long

7.3.10 使用强制类型转换显示您的意图

7.3.11 检查格式字符串转换操作

7.4 其他转换注意事项

7.4.1 注意:长度增长的派生类型

7.4.2 检查更改的副作用

7.4.3 检查直接使用 long 是否仍有意义

7.4.4 对显式 32 位与 64 位原型使用 #ifdef

7.4.5 调用转换更改

7.4.6 算法更改

7.5 入门指导核对表

8.  cscope:交互检查 C 程序

A.  按功能分组的编译器选项

B.  C 编译器选项参考

C.  实现定义的 ISO/IEC C99 行为

D.  C99 的功能

E.  实现定义的 ISO/IEC C90 行为

F.  ISO C 数据表示法

G.  性能调节

H.  Oracle Solaris Studio C:K&R C 与 ISO C 之间的差异

索引

7.3 转换为 LP64 数据类型模型

以下示例说明在转换代码时可能遇到的某些常见问题。适当时会显示相应的 lint 警告。

7.3.1 整型和指针长度更改

由于整型和指针在 ILP32 编译环境中长度相同,因此某些代码依赖以下假定。通常会将指针强制转换为 intunsigned int 以进行地址运算。但是,由于 long 和指针在 ILP32 和 LP64 数据类型模型中长度相同,因此可以将指针强制转换为 long。请改用 uintptr_t,而不是显式使用 unsigned long。这样可以更详细地表达您的意图,并且使代码更易于移植,避免受到未来更改的影响。请看以下示例:

char *p;
p = (char *) ((int)p & PAGEOFFSET);
%
warning: conversion of pointer loses bits

修改后的版本为:

char *p;
p = (char *) ((uintptr_t)p & PAGEOFFSET);

7.3.2 整型和长型长度更改

由于整型和长型在 ILP32 数据类型模型中从未真正加以区分,因此现有代码可能会不加区分地使用它们。修改交换使用整型和长型的任何代码,使其可同时符合 ILP32 和 LP64 数据类型模型的要求。整型和长型在 ILP32 数据类型模型中均为 32 位,而 long 在 LP64 数据类型模型中为 64 位。

请看以下示例:

int waiting;
long w_io;
long w_swap;
...
waiting = w_io + w_swap;

%
warning: assignment of 64-bit integer to 32-bit integer

此外,相对于 intunsigned int 数组,大型的 longunsigned long 数组可能会导致 LP64 数据类型模型出现严重的性能下降。大型的 longunsigned long 数组还可能会导致显著增加缓存未命中的情况,并占用更多的内存。

因此,如果对于应用程序而言 int 与 long 的效果一样好,最好使用 int,而不要使用 long

此参数也适用于使用 int 数组而不要使用指针数组。某些 C 应用程序在转换为 LP64 数据类型模型后出现显著的性能下降,这是因为它们依赖于很多较大的指针数组。

7.3.3 符号扩展

转换到 64 位编译环境时,经常会遇到符号扩展问题,这是因为类型转换和提升规则有些模糊。为防止出现符号扩展问题,请使用显式强制类型转换以取得预期结果。

要了解发生符号扩展的原因,请考虑 ISO C 的转换规则。执行以下操作期间,导致 32 位和 64 位编译环境之间所发生大多数符号扩展问题的转换规则将生效:

以下示例被编译为 64 位程序时,即使 addra.base 均为无符号类型,addr 变量仍可成为带符号扩展变量。

%cat test.c
struct foo {
unsigned int base:19, rehash:13;
};

main(int argc, char *argv[])
{
  struct foo a;
  unsigned long addr;

  a.base = 0x40000;
  addr = a.base << 13;  /* Sign extension here! */
  printf("addr 0x%lx\n", addr);

 addr = (unsigned int)(a.base << 13); /* No sign extension here! */
 printf("addr 0x%lx\n", addr);
}

发生此符号扩展的原因是按以下方式应用了转换规则:

% cc -o test64 -m64 test.c
% ./test64
addr 0xffffffff80000000
addr 0x80000000
%

如果将同一示例编译为 32 位程序,则不显示任何符号扩展:

cc -o test -m32 test.c
%test

addr 0x80000000
addr 0x80000000

有关转换规则的详细讨论,请参见 ISO C 标准。此标准中还包含对普通算术转换和整型常量有用的规则。

7.3.4 指针运算而不是整数

通常,由于指针运算独立于数据模型,而整数不可以,因此使用指针运算比整数好。此外,通常可以使用指针运算简化代码。请看以下示例:

int *end;
int *p;
p = malloc(4 * NUM_ELEMENTS);
end = (int *)((unsigned int)p + 4 * NUM_ELEMENTS);

%
warning: conversion of pointer loses bits

修改后的版本为:

int *end;
int *p;
p = malloc(sizeof (*p) * NUM_ELEMENTS);
end = p + NUM_ELEMENTS;

7.3.5 结构

检查应用程序中的内部数据结构有无漏洞。在结构中的字段之间使用额外填充,以满足对齐要求。对于 LP64 数据类型模型,当 long 或指针字段增至 64 位时,会分配此额外填充。在 SPARC 平台上的 64 位编译环境中,所有类型的结构均与结构中最长成员的长度对齐。当您重组结构时,请遵循将 long 和指针字段移到结构开头的简单规则。考虑以下结构定义:

struct bar {
   int i;
   long j;
   int k;
   char *p;
};   /* sizeof (struct bar) = 32 */

以下示例显示在结构开头定义了 long 和指针数据类型的相同结构:

struct bar {
  char *p;
  long j;
  int i;
  int k;
};   /* sizeof (struct bar) = 24 */

7.3.6 联合

请确保对联合进行检查,因为其字段的长度在 ILP32 和 LP64 数据类型模型之间可能会发生变化。请看以下示例:

typedef union {
   double _d;
   long _l[2];
} llx_t;

修改后的版本为:

typedef union {
   double _d;
   int _l[2];
} llx_t;

7.3.7 类型常量

在某些常量表达式中,缺少精度会导致数据丢失。请在常量表达式中显式指定数据类型。通过增加 {u,U,l,L} 的组合指定每个整型常量的类型。您也可以使用强制类型转换来指定常量表达式的类型。请看以下示例:

int i = 32;
long j = 1 << i; /* j will get 0 because RHS is integer */
                              /* expression */

修改后的版本为:

int i = 32;
long j = 1L << i;

7.3.8 注意隐式声明

如果使用 -xc99=none,C 编译器会假定在模块中使用却未在外部定义或声明的函数或变量为整型。编译器的隐式整型声明会将以此方式使用的任何 long 和指针数据截断。将函数或变量的相应 extern 声明置于头文件而非 C 模块中。在使用函数或变量的 C 模块中包含此头文件。即使函数或变量由系统头文件定义,您还需要在代码中包含正确的头文件。请看以下示例:

int
main(int argc, char *argv[])
{
  char *name = getlogin();
  printf("login = %s\n", name);
  return (0);
}

%
warning: improper pointer/integer combination: op "="
warning: cast to pointer from 32-bit integer
implicitly declared to return int
getlogin        printf

以下修改的版本中现在有正确的头文件

#include <unistd.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
  char *name = getlogin();
  (void) printf("login = %s\n", name);
  return (0);
}

7.3.9 sizeof( ) 是无符号 long

在 LP64 数据类型模型中,sizeof() 的有效类型为 unsigned long。有时,sizeof() 会传递给需要使用类型为 int 参数的函数,或者赋值给整型或强制转换为整型。有些情况下,这种截断会导致数据丢失。

long a[50];
unsigned char size = sizeof (a);

%
warning: 64-bit constant truncated to 8 bits by assignment
warning: initializer does not fit or is out of range: 0x190

7.3.10 使用强制类型转换显示您的意图

关系表达式可能会因为转换规则而显得错综复杂。您应该通过在必要的地方增加强制类型转换很明确地指定表达式的求值方式。

7.3.11 检查格式字符串转换操作

确保 printf(3S)、sprintf(3S)、scanf(3S) 和 sscanf(3S) 的格式字符串可以容纳 long 或指针参数。对于指针参数,格式字符串中提供的转换操作应为 %p,以便能在 32 位和 64 位编译环境中运行。请看以下示例:

char *buf;
struct dev_info *devi;
...
(void) sprintf(buf, "di%x", (void *)devi);

%
warning: function argument (number) type inconsistent with format
sprintf (arg 3)     void *: (format) int

修改后的版本为:

char *buf;
struct dev_info *devi;
...
(void) sprintf(buf, ”di%p", (void *)devi);

对于 long 参数,long 长度规范 l 应前置于格式字符串中的转换操作字符前面。另外,还要检查以确保 buf 指向的存储器足以包含 16 个数字。

size_t nbytes;
u_long align, addr, raddr, alloc;
printf("kalloca:%d%%%d from heap got%x.%x returns%x\n",
nbytes, align, (int)raddr, (int)(raddr + alloc), (int)addr);

%
warning: cast of 64-bit integer to 32-bit integer
warning: cast of 64-bit integer to 32-bit integer
warning: cast of 64-bit integer to 32-bit integer

修改后的版本为:

size_t nbytes;
u_long align, addr, raddr, alloc;
printf("kalloca:%lu%%%lu from heap got%lx.%lx returns%lx\n",
nbytes, align, raddr, raddr + alloc, addr);