跳过导航链接 | |
退出打印视图 | |
Oracle Solaris Studio 12.3 发行版的新增功能 Oracle Solaris Studio 12.3 Information Library (简体中文) |
本节介绍了此发行版中编译器的已知问题、问题和解决方法。
下面列出了已发布编译器文档中的错误。
cc(1)、CC(1) 和 f95(1) 手册页没有列出 —xarch=sse3a 标志,此标志会将 AMD 指令集(包括 3dnow)添加到 SSE3 指令集中。
C 和 C++ 文档没有指明 —xMF 选项只可用于 —xMD 或 —xMMD,而不可用于 —xM 或 —xM1。如果指定,它会覆盖用于这些选项的缺省 .d 文件名称。
安装在 Solaris 10u10 和更早版本以及 Solaris 11 最初发行版中的 Apache stdcxx 库在头文件 stdcxx4/loc/_moneypunct.h 中有语法错误。早期版本的编译器没有发现此错误,但 Oracle Solaris Studio 12.3 C++ 编译器发现了此错误。无法禁用错误检测。
对此错误的修复在 Solaris 10 的修补程序和首个 Solaris 11 SRU 中提供。Solaris 10u11 和 Solaris 11u1 推出时将包含此修复。
某些 C++ 语句有可能解释成声明或者表达式语句。C++ 消除歧义规则为:如果一个语句可以处理成声明,那么它就是声明。
早期版本的编译器会错误解释类似于下面的代码:
struct S { S(); }; struct T { T( const S& ); }; T v( S() ); // ???
编程人员也许本来打算在最后一行定义变量 v,并且用类型为 S 的临时变量对它进行初始化。早期版本的编译器会这样解释这条语句。
但是在声明环境里,构造符号 "S()" 也可以是抽象声明符(不带标识符),表示“没有返回值类型为 S 的参数的函数”。在这种情况下,该语句会自动转换为函数指针 "S(*)()"。这样该语句仍可作为函数 v 的声明,该函数有一个函数指针类型的参数,返回类型为 T 的值。
当前版本的编译器可以正确地解释该语句,但这未必是编程人员所需要的结果。
可以使用两种方法来修改上述代码以便不产生歧义:
T v1( (S()) ); // v1 is an initialized object T v2( S(*)() ); // v2 is a function
第一行中另加的圆括号表明它不是 v1 作为函数声明的有效语法,所以它的唯一可能解释是“利用类型为 S 的临时值进行初始化的类型为 T 的目标”。
同样,构造符号 "S(*)()" 不可能是一个值,所以它的唯一可能解释是函数声明。
第一行也可以改写为:
T v1 = S();
虽然这时语句含义非常清楚,但这种形式的初始化有时会创建一个额外的临时变量,而一般情况下不会发生这种情况。
建议不要编写与下面语句类似的代码,因为它的含义不清楚,不同的编译器可能会提供不同的结果。
T v( S() ); // not recommended
模板选项 -instances=static(或 -pto)在与 -xcrossfile 或 -xipo 选项组合时无效。使用该组合的程序会经常发生链接失败。
如果使用 -xcrossfile 或 -xipo 选项,请使用缺省的模板编译模型 -instances=global 进行替代。
通常,不要使用 -instances=static(或 -pto)。它不再有任何优点,此外,C++ 用户指南中还对其缺点进行了说明。
以下情况会导致链接问题。
函数在一个地方声明带有一个 const 参数,而在另一个地方又声明带有一个非 const 参数。
示例:
void foo1(const int); void foo1(int);
这两个声明是等效的,但编译器会将其改编为两个不同的名称。要避免这个问题,则不应将值参数声明为 const。例如,在任何位置都使用 void foo1(int),包括该函数定义体。
函数有两个具有相同复合类型的参数,但只有一个参数是用 typedef 声明的。
示例:
class T; typedef T x; // foo2 has composite (that is, pointer or array) // parameter types void foo2(T*, T*); void foo2(T*, x*); void foo2(x*, T*); void foo2(x*, x*);
所有的 foo2 声明都是等效的,并且应该改编相同的名称。但是,编译器只会改编部分声明的名称。为了避免这个问题,应该统一使用 typedef。
如果您无法统一使用 typedef,解决方法是:在定义该函数的文件中使用弱符号,使得声明与函数的定义一致。例如:
#pragma weak "__1_undefined_name" = "__1_defined_name"
请注意,某些改编名称依赖于目标体系结构。(例如,在 SPARC V9 体系结构 (-m64) 中,size_t 是 unsigned long,而在其他体系结构中是 signed int。)在这种情况下,会出现两个版本的改编名称,分别对应两个模式。这时必须使用两个 pragma,并用适当的 #if 指令对其进行控制。
如果您使用 -instances=extern 编译,则使用模板和静态对象的程序会出现未定义符号的链接时错误。使用缺省设置 -instances=global 则不会出现问题。编译器不支持对模板中的非全局名称空间作用域目标的引用。请看以下示例:
static int k; template<class T> class C { T foo(T t) { ... k ... } };
在本示例中,一个模板类的成员引用了静态名称空间作用域的变量。请记住,名称空间作用域包含文件作用域。编译器不支持模板类的成员引用静态名称空间作用域的变量。另外,如果模板在其他的编译单元实例化,那么每个实例都会指向不同的 k,这破坏了 C++ 一次定义规则,代码的行为将会不可预测。
下面的方法也是可行的,但这取决于您如何使用 k,以及它应有的功能。第二个选项仅可供属于类成员的函数模板使用。
可以为变量提供外部链接属性:
int k; // not static
所有的实例都使用同一个 k。
也可以使这个变量成为类的静态成员:
template<class T> class C { static int k; T foo(T t) { ... k ... } };
静态类成员具有外部链接属性。每个 C<T>::foo 的实例都使用不同的 k。而 C<T>::k 的一个实例可以被其他函数共享。此选项可能是您需要的选项。
在名称空间内使用 #pragma align 时,必须使用改编名称。例如,在下面的代码中,#pragma align 语句是无效的。要更正此问题,应将 #pragma align 语句中的 a, b 和 c 替换为其改编名称。
namespace foo { #pragma align 8 (a, b, c) // has no effect //use mangled names: #pragma align 8 (__1cDfooBa_, __1cDfooBb_, __1cDfooBc_) static char a; static char b; static char c; }
在此 f95 编译器发行版中应注意以下问题:
不换行打印行末尾之前的空格不会影响输出位置 (7087522)。
输出语句格式末尾的 X 编辑描述符不会影响输出记录中后续字符的位置。如果输出语句中有 ADVANCE='NO' 并且有更多字符要通过后续输出语句传送到同一记录,则会导致差异。
在很多情况下,可以通过添加空白字符串编辑描述符,而不是 nX 编辑描述符来解决此问题。它们并不完全相同,因为空白字符串编辑描述符实际上会将空白字符包含在记录中,而 nX 仅跳过后 n 个字符,缺省情况下通常导致跳过的位置中产生空白。
一行中有两个连续 & 号的有效代码会被拒绝 (7035243)。
Fortran 标准禁止有一个 & 号的空续行。但是,仍可以创建同一行中有两个 & 号的空续行,这不在标准限制范围之内。编译器不会处理这种情况,而会显示一条错误消息。解决方法是删除该行,这只会影响程序可读性而并不添加任何语义。
BOZ 常量有时会被截断 (6944225)。
在某些相对更复杂的情况下,如数组构造,BOZ 常量可能会被截成 4 个字节的缺省整数大小,即使它所应指定给的相应项是一个 8 个字节的整数实体。解决方法是在数组构造中使用正确类型和种类的常量,而不要使用 BOZ 常量。
Fortran 编译器的早期发行版引入了某些不兼容性,并被此编译器发行版所继承,如果您是从 Fortran 编译器早期发行版进行更新,应注意这一点。请注意下面的不兼容性:
需注意 Oracle Solaris Studio 12.2 发行版已删除了废弃的 FORTRAN 77 库。这意味着使用依赖于共享库 libF77、libM77 和 libFposix 的传统 Sun WorkShop f77 编译器编译的旧的可执行文件将不会运行。
数组内部函数 ANY, ALL, COUNT, MAXVAL, MINVAL, SUM, PRODUCT、DOT_PRODUCT 和 MATMUL 针对相应 SPARC 平台体系结构进行了高度优化。因此,它们使用全局寄存器 %g2, %g3 和 %g4 作为临时寄存器。
如果调用了上述所列的数组内在函数,则用户代码不应该认为这些寄存器可用于暂时存储。当调用数组内在函数时,这些寄存器中的数据将被覆盖。
调试器 dbx 要求编译中使用的所有对象文件都包含在可执行文件中。通常,无需用户执行额外操作,程序即可满足此要求。但使用含有模块的归档文件时例外。如果程序使用了一个模块,但没有引用模块中的任何过程或变量,则产生的对象文件不会包含对模块中定义的符号的引用。只有对目标文件中定义的符号具有引用时,链接器才会链接归档文件中的对象文件。如果不存在此类引用,对象文件将不包括在可执行文件中。当 dbx 尝试查找与使用的模块相关联的调试信息时,将发出警告。对于缺少调试信息的符号,则无法提供有关这些符号的信息。
使用 -u 链接程序选项可以解决这个问题。此选项使用一个符号作为其选项参数。它会将该符号添加到未定义的链接程序符号集中,这就需要解析此符号。与模块关联的链接程序符号通常是模块名称,其所有字母均为小写,后面跟有一条下划线。
例如,为了强制包含模块 MODULE_1 的对象文件被归档文件采用,请指定链接程序选项 -u module_1_。如果使用 f95 命令进行链接,请在命令行上使用 -Qoption ld -umodule_1_。
启用系统节能功能时,没有可靠方式可以精确地获得 AMD 处理器上的时钟速率。因此,使用基于 gethrtime(3F) 的计时函数(Fortran 编译器的 Linux 版 Solaris gethrtime(3C) 函数)在 Linux 平台上获得高精度实际时间的方法只有在禁用了节能功能的 AMD 系统才会精确。要禁用节能功能,可能需要重新引导系统。