Sun Studio 12:C 用户指南

第 2 章 特定于 Sun 实现的 C 编译器信息

本章讨论特定于 C 编译器方面的内容。该信息已编入语言扩展和环境。

C 编译器与新的 ISO C 标准 (ISO/IEC 9899-1999) 中描述的 C 语言的某些功能兼容。如果您要编译与以前的 C 标准(ISO/IEC 9889-1990 标准及修订版 1)兼容的代码,请使用 -xc99=none,编译器会忽略 ISO/IEC 9899-1999 标准的增强标准。

2.1 常量

本节包含与特定于 Sun C 编译器的常量相关的信息。

2.1.1 整型常量

十进制、八进制和十六进制的整型常量可加后缀以指示类型,如下表所示。

表 2–1 数据类型后缀

后缀 

类型 

uU

unsigned

lL

long

llLL

long long [long longunassigned long long-xc99=none-Xc 模式下不可用。]

luLULulUuluLUlUL

unsigned long

lluLLULLullUullULLuLL Ull

unsigned long long

如果设置 -xc99=all,编译器将根据常量大小,使用以下列表中可以表示该值的第一项:

如果值超过 long long int 可表示的最大值,编译器会发出警告。

如果设置 -xc99=none,则为无后缀常量指定类型时,编译器将根据常量大小,使用以下列表中可以表示该值的第一项:

2.1.2 字符常量

一个多字符常量,它不是具有从每个字符的数值派生的值的换码序列。例如,常量 '123' 的值为:

0

'3'

'2'

'1'

0x333231

使用 -Xs 选项并且在 C 的其他非 ISO 版本中,该值为:

0

'1'

'2'

'3'

0x313233

2.2 链接程序作用域说明符

使用以下声明说明符有助于隐藏外部符号的声明和定义。通过使用这些说明符,您不必再使用链接程序作用域的 mapfile。此外,还可以通过在命令行中指定 -xldscope 来控制变量作用域的缺省设置。有关更多信息,请参见B.2.96 -xldscope={v}

表 2–2 声明说明符

值 

含义 

__global

该符号具有全局链接程序作用域,并且是限制最少的链接程序作用域。该符号的所有引用都绑定到在第一个动态模块中定义该符号的定义上。该链接程序作用域是外部符号的当前链接程序作用域。

__symbolic

该符号具有符号链接程序作用域,该作用域的限制比全局链接程序作用域的限制更多。从所链接的动态模块内部对符号的所有引用都绑定到在模块内部定义的符号上。在模块外部,符号也显示为全局符号。该链接程序作用域对应于链接程序选项 -Bsymbolic。有关链接程序的更多信息,请参见 ld(1)。

__hidden

该符号具有隐藏的链接程序作用域。隐藏链接程序作用域具有比符号和全局链接程序作用域更高的限制。动态模块内部的所有引用都绑定到该模块内部的一个定义上。符号在模块外部是不可视的。

对象或函数可以用限制较多的说明符重新声明,但不能用限制较少的说明符重新声明。符号定义后,不可以用不同的说明符声明符号。

__global 是限制最少的作用域,__symbolic 是限制较多的作用域,而 __hidden 是限制最多的作用域。

2.3 线程局部存储说明符

通过声明线程局部变量,可以利用线程局部存储。线程局部变量声明由一个标准变量声明外加变量说明符 __thread 组成。有关更多信息,请参见B.2.146 -xthreadvar[= o]

您必须在所编译的源文件中线程变量的第一个声明中包含 __thread 说明符。

在具有静态存储持续时间的对象的声明中,只能使用 __thread 说明符。您可以如初始化任何其他静态存储持续时间的对象一样静态地初始化线程变量。

使用 __thread 说明符声明的变量与不使用 __thread 说明符声明的变量具有相同的链接程序绑定。这包括临时定义,如无初始化函数的声明。

线程变量的地址不是常量。因此,线程变量的地址操作符 (&) 在运行时求值,并返回当前线程的线程变量的地址。结果,静态存储持续时间的对象被动态地初始化为线程变量的地址。

线程变量的地址在相应线程的生命周期中是稳定的。进程中任何线程都可以在线程变量的生命周期任意使用该变量的地址。不能在线程终止后使用线程变量的地址。线程终止后,该线程的变量的所有地址都无效。

2.4 浮点,非标准模式

缺省情况下,IEEE 754 浮点运算“不停止”,并且下溢是“渐进的”。下面给出了概括性说明,有关详细信息,请参见《数值计算指南》。

不停止意味着遇到除数为零、浮点下溢或无效操作异常时执行不会停止。例如,考虑以下算式,其中 x 为零,y 为正数:

z = y / x;

缺省情况下,z 设置为值 +Inf,执行不会停止。但是,如果设置 -fnonstd 选项,此代码会导致退出,如核心转储。

下面是渐进下溢的工作方式。假设您有下列代码:


x = 10;
for (i = 0; i < LARGE_NUMBER; i++)
x = x / 10;

第一次执行循环时,x 设置为 1;第二次执行循环时,设置为 0.1;第三次执行循环时,设置为 0.01,依此类推。最后,x 达到比较低的值,以致机器无法表示其值。下次循环运行时将出现什么情况?

假设可表示的最小数为 1.234567e-38

下次循环运行时,将通过去掉一位尾数并且指数减去 1 来修改该数,因此新值为 1.23456e-39,然后为 1.2345e-40,依此类推。这称为“渐进下溢”,它是缺省行为。在非标准模式下,不会发生这种“去位”情况;通常,x 被简单地设置为零。

2.5 作为值的标签

C 编译器可识别称为计算转移 (computed goto) 语句的 C 扩展。使用计算转移 (computed goto) 语句能够在运行时确定分支目标。通过使用 '&&' 运算符可以获取标签的地址,并且可以将标签地址指定给 void * 类型的指针:


void *ptr;
...
ptr = &&label1;

后面的 goto 语句可以通过 ptr 转到 label1


goto *ptr;

由于 ptr 在运行时进行计算,因此 ptr 可以接受作用域内任何标签的地址,而 goto 语句可以转到该位置。

使用计算转移 (computed goto) 语句的一种方法是用于转移表的实现:


static void *ptrarray[] = { &&label1, &&label2, &&label3 };

现在可以通过索引来选择数组元素:


goto *ptrarray[i];

标签的地址只能通过当前函数作用域计算。尝试在当前函数外部获取标签的地址会产生不可预测的结果。

转移表和开关语句的作用相似(虽然存在某些主要差异),转移表使跟踪程序流更加困难。一个显著的差异是:开关语句转移目标全都从开关保留字开始正向转移;使用计算转移 (computed goto) 语句实现转移表可进行正向和反向分支。


#include <stdio.h>
void foo()
{
  void *ptr;

  ptr = &&label1;

  goto *ptr;

  printf("Failed!\n");
  return;

  label1:
  printf("Passed!\n");
  return;
}

int main(void)
{
  void *ptr;

  ptr = &&label1;

  goto *ptr;

  printf("Failed!\n");
  return 0;

  label1:
  foo();
  return 0;
}

以下示例也使用转移表控制程序流:


#include <stdio.h>

int main(void)
{
  int i = 0;
  static void * ptr[3]={&&label1, &&label2, &&label3};

  goto *ptr[i];

  label1:
  printf("label1\n");
  return 0;

  label2:
  printf("label2\n");
  return 0;

  label3:
  printf("label3\n");
  return 0;
}

%example: a.out
%example: label1

计算转移 (computed goto) 语句的另一个应用是作为线程代码的解释程序。解释程序函数内部的标签地址可以存储在线程代码中以便快速分发。

下面是编写以上示例的另一种方法:


static const int ptrarray[] = { &&label1 - &&label1, &&label2 - &&label1, &&label3 - &&label1 };
goto *(&&label1 + ptrarray[i]);

这对于共享库代码效率更高,因为它减少了所需的动态重定位数量,因此,允许数据(ptrarray 元素)为只读。

2.6 long long 数据类型

使用 -xc99=none 进行编译时,Sun C 编译器包含数据类型 long longunsigned long long,它们与数据类型 long 类似。long long 数据类型存储 64 位信息;在 SPARC V8 和 x86 上,long 存储 32 位信息。在 SPARC V9 上,long 数据类型存储 64 位信息。long long 数据类型在 -Xc 模式下不可用。

2.6.1 打印 long long 数据类型

打印或扫描 long long 数据类型,请在转换说明符前面加字母 ll。例如,要以带符号十进制格式打印 llvarlong long 数据类型的变量),请使用:


printf("%lld\n", llvar);

2.6.2 常见算术转换

某些二元运算符将其操作数的类型转换为普通类型,这也是结果的类型。下面这些转换称为常见算术转换:

2.7 断言

以下形式的一行内容:


#assert predicate (token-sequence)

token-sequence 和断言名称空间(与用于宏定义的空间不同)中的谓词相结合。谓词必须为标识符标记。


#assert predicate

断言 predicate 存在,但是未与任何标记序列相结合。

缺省情况下,编译器提供以下预定义谓词(不在 -Xc 模式下):


#assert system (unix)
#assert machine (sparc)
#assert machine (i386)(x86)
#assert cpu (sparc)
#assert cpu (i386)(x86)

缺省情况下,lint 提供以下预定义谓词(不在 -Xc 模式下):


#assert lint (on)

任何断言均可使用 #unassert 进行删除,该命令的语法与 assert 的语法相同。使用不带参数的 #unassert 将删除关于谓词的所有断言;指定一个断言将只删除该断言。

可以使用以下语法在 #if 语句中测试断言:


#if #predicate(non-empty token-list)

例如,可以使用以下行测试预定义谓词 system


#if #system(unix)

其结果为真。

2.8 Pragma

以下形式的预处理行:


#pragma pp-tokens

指定实现定义的操作。

编译系统可识别以下 #pragma。编译器忽略未识别的 pragma。如果使用 -v 选项,将针对无法识别的 pragma 发出警告。

2.8.1 align

#pragma align integer (variable[, variable] )

align pragma 会使所有提及的变量内存与整数字节对齐,从而覆盖缺省值。请遵循以下限制:


#pragma align 64 (aninteger, astring, astruct)
int aninteger;
static char astring[256];
struct astruct{int a; char *b;};

2.8.2 c99

#pragma c99(“implicit” | “ no%implicit”)

pragma 控制隐式函数声明的诊断。如果将 c99 pragma 值设置为 "implicit"(请注意使用了引号),则编译器在找到隐式函数声明时将生成一条警告。如果将 c99 pragma 值设置为 "no%implicit"(请注意使用了引号),则编译器将无提示地接受隐式函数声明,直到该 pragma 值重置。

-xc99 选项的值会影响该 pragma。如果 -xc99=all,则该 pragma 设置为 #pragma c99("implicit");如果 -xc99=none,则该 pragma 设置为 #pragma c99("no%implicit")

缺省情况下,该 pragma 设置为 c99=("implicit")

2.8.3 does_not_read_global_data

#pragma does_not_read_global_data (funcname [, funcname])

该 pragma 断言指定列表中的例程不直接或间接读取全局数据。允许对调用这些例程的代码进行更好的优化。具体来讲,赋值语句或存储可以围绕这样的调用移动。

必须在该 pragma 之前使用原型或空参数列表声明指定的函数。如果全局访问的断言不为真,那么程序的行为就是未定义的。

2.8.4 does_not_return

#pragma does_not_return (funcname [, funcname])

pragma 向编译器断言,将不会返回对指定例程的调用。这样编译器可以执行与假定一致的优化。例如,寄存器生命周期将在调用点终止,进而允许更多的优化。

如果指定的函数不返回,程序的行为就是未定义的。只有在使用原型或空参数列表声明指定的函数之后才允许使用该 pragma,如以下示例所示:


extern void exit(int);
#pragma does_not_return(exit)

extern void __assert(int);
#pragma does_not_return(__assert)

2.8.5 does_not_write_global_data

#pragma does_not_write_global_data (funcname [, funcname])

该 pragma 断言指定列表的例程不直接或间接写全局数据。允许对调用这些例程的代码进行更好的优化。具体来讲,赋值语句或存储可以围绕这样的调用移动。

必须在该 pragma 之前使用原型或空参数列表声明指定的函数。如果全局访问的断言不为真,那么程序的行为就是未定义的。

2.8.6 error_messages

#pragma error_messages (on|off| default, tag… tag)

错误消息 pragma 提供源程序内部对 C 编译器和 lint 发出的消息的控制。对于 C 编译器,pragma 只对警告消息有效。C 编译器的 -w 选项通过禁止显示所有警告消息来覆盖该 pragma。

2.8.7 fini

#pragma fini (f1[, f2…,fn]

使实现在调用 main() 例程之后调用函数 f1fn(完成函数)。此类函数的类型应为 void,并且不接受任何参数,当程序正常终止或所含共享对象从内存中删除时会调用这些函数。和“初始化函数”一样,完成函数按链接编辑器的处理顺序执行。

如果完成函数影响全局程序的状态,则应当小心操作。例如,除非接口明确声明您使用系统库完成函数时会发生什么情况,否则您应捕获和恢复所有全局状态信息,如系统库完成函数可能更改的 errno 的值。

2.8.8 hdrstop

#pragma hdrstop

hdrstop pragma 必须放在最后一个头文件之后,以标识要共享相同预编译头文件的每个源文件中活前缀的结束。例如,考虑以下文件:


example% cat a.c
#include "a.h"
#include "b.h"
#include "c.h"
#include <stdio.h>
#include "d.h"
.
.
.
example% cat b.h
#include "a.h"
#include "b.h"
#include "c.h"

活动源代码前缀在 c.h 结束,因此您需要在每个文件中在 c.h 后面插入 #pragma hdrstop。

#pragma hdrstop 必须只在使用 cc 命令指定的源文件的活前缀的末尾出现。不要在任何 include 文件中指定 #pragma hdrstop。

2.8.9 ident

#pragma ident string

string 放在可执行文件的 .comment 部分。

2.8.10 init

#pragma init (f1[, f2…,fn] )

使实现在调用 main() 之前调用函数 f1fn(初始化函数)。此类函数的类型应为 void,并且不接受任何参数,在开始执行时构造程序的内存映像时会调用这些函数。如果初始化函数在共享对象中,则在执行将共享对象放入内存的操作(无论是程序启动,还是某些动态装入操作,如 dlopen())时执行它们。调用初始化函数的唯一顺序是链接编辑器处理它们的顺序,静态和动态均可。

如果初始化函数影响全局程序的状态,则应当小心操作。例如,除非接口明确声明您使用系统库初始化函数时会发生什么情况,否则您应捕获和恢复所有全局状态信息,如系统库初始化函数可能更改的 errno 的值。

2.8.11 inline

#pragma [no_]inline (funcname[, funcname])

该 pragma 控制 pragma 的参数中列出的例程名称的内联。该 pragma 的作用域针对整个文件。该 pragma 只允许全局内联控制,不允许特定于调用点的控制。

如果您使用 #pragma inline,它会提示编译器内联当前文件中与 pragma 中列出的例程列表匹配的调用。在某些情况下,此建议可能被忽略。例如,当函数的主体在另一个模块并且未使用交叉文件选项时,忽略该建议。

如果您使用 #pragma no_inline,它会提示编译器不要内联当前文件中与 pragma 中列出的例程列表匹配的调用。

只有在使用原型或空参数列表声明函数之后,才允许使用 #pragma inline#pragma no_inline,如下例所示:


static void foo(int);
static int bar(int, char *);
#pragma inline(foo, bar)

另请参见 -xldscope-xinline-xO-xcrossfile

2.8.12 int_to_unsigned

#pragma int_to_unsigned (funcname )

对于返回类型 unsigned 的函数,在 -Xt-Xs 模式下,将函数返回值的类型更改为 int

2.8.13 MP serial_loop

(SPARC) #pragma MP serial_loop


注 –

Sun 特定的 MP pragma 已过时,并且不再受支持。但是,编译器改为支持 OpenMP 2.5 标准指定的 API。有关标准的指令的迁移信息,请参见《OpenMP API 用户指南》。


有关详细信息,请参阅3.8.3.1 串行 Pragma

2.8.14 MP serial_loop_nested

(SPARC) #pragma MP serial_loop_nested


注 –

Sun 特定的 MP pragma 已过时,并且不再受支持。但是,编译器改为支持 OpenMP 2.5 标准指定的 API。有关标准的指令的迁移信息,请参见《Sun Studio 12: OpenMP API User’s Guide》


有关详细信息,请参阅3.8.3.1 串行 Pragma

2.8.15 MP taskloop

(SPARC) #pragma MP taskloop


注 –

Sun 特定的 MP pragma 已过时,并且不再受支持。但是,编译器改为支持 OpenMP 2.5 标准指定的 API。有关标准的指令的迁移信息,请参见《OpenMP API 用户指南》。


有关详细信息,请参阅3.8.3.2 并行 Pragma

2.8.16 nomemorydepend

(SPARC) #pragma nomemorydepend

该 pragma 指定,对于某个循环的任何迭代,不存在内存依赖性。也就是说,在某个循环的任何迭代内部,不存在对相同内存的引用。该 pragma 将允许编译器(流水线化程序)在某个循环的单次迭代内更有效地调度指令。如果某个循环的任何迭代内部存在任何内存依赖性,则程序的执行结果未定义。编译器在优化级别 3 或更高级别上使用此信息。

此 pragma 的作用域从它自身开始,在以下任何一种情况最先出现时结束:下一块的开始部分、当前块内部的下一个 for 循环、当前块的结尾。该 pragma 应用于 pragma 作用域结束前的下一个 for 循环。

2.8.17 no_side_effect

(SPARC) #pragma no_side_effect(funcname[, funcname…])

funcname 指定当前转换单元内部某个函数的名称。必须在该 pragma 之前使用原型或空参数列表声明的函数。必须在函数的定义之前指定 pragma。对于命名的函数 funcname,该 pragma 声明函数无任何副作用。这意味着,funcname 返回一个只依赖传递参数的结果值。另外,funcname 和任何已调用的子函数具有以下特性:

使用函数进行优化时,编译器使用此信息。如果函数具有副作用,执行调用该函数的程序的结果是未定义的。编译器在优化级别 3 或更高级别上使用此信息。

2.8.18 opt

#pragma opt level (funcname[, funcname])

funcname 指定当前转换单元内定义的函数的名称。level 值指定用于所指定函数的优化级别。可以指定优化级别 0、1、2、3、4、5。可以通过将 level 设置为 0 来关闭优化。必须在 pragma 之前使用原型或空参数列表声明函数。pragma 则必须对要优化的函数进行定义。

pragma 中所列任何函数的优化级别都降为 -xmaxopt 值。-xmaxopt=off 时,忽略 pragma。

2.8.19 pack

#pragma pack(n)

使用 #pragma pack(n) 将影响结构或联合的成员封装。缺省情况下,结构或联合的成员按其自然边界对齐;一个字符型 (char) 数据占一个字节,一个短整型 (short) 数据占两个字节,一个整型 (integer) 数据占四个字节,等等。如果存在 n,它必须为 2 的幂,并且为任何结构或联合成员指定最严格的自然对齐。不接受零。

您可以使用 #pragma pack(n) 为结构或联合成员指定对齐边界。例如,#pragma pack(2) 会使 int、long、long long、float、double、long double 和指针与双字节边界对齐,而不是与其自然边界对齐。

如果 n 等于或大于您使用的平台上最严格的对齐(在 x86 上为 4,在 SPARC v8 上为 8,在 SPARC v9 上为 16),则该指令具有自然对齐的效果。同样,如果省略 n,成员对齐将恢复为自然对齐边界。

#pragma pack(n) 指令应用于它后面的所有结构或联合定义,直到出现下一个 pack 指令。如果在具有不同包装的不同转换单元中定义了相同的结构或联合,那么程序会因某种原因而失败。特别是,不应在包含定义了预编译库的接口的头文件之前使用 #pragma pack(n)。#pragma pack(n) 的建议用法是,将它放在程序代码中紧挨在要封装的任何结构或联合的前面。在封装的结构后面紧跟 #pragma pack( )。

请注意,使用 #pragma pack 时,封装的结构或联合本身的对齐方式与其更严格对齐的成员相同。因此,该结构或联合的任何声明将使用压缩对齐。例如,只包含 char 数据的 struct 无对齐限制,而包含 double 数据的 struct 将按 8 字节边界对齐。


注 –

如果您使用 #pragma pack 将结构或联合成员与其自然边界以外的边界对齐,则访问这些字段通常会导致 SPARC 出现总线错误。为避免发生此类错误,请确保同时还指定了 -xmemalign 选项。有关编译此类程序的最佳方法,请参见B.2.111 -xmemalign=ab


2.8.20 pipeloop

(SPARC) #pragma pipeloop(n )

对于参数 n,该 pragma 接受正整数常量值或 0。该 pragma 指定,循环可流水线化,并且循环携带的依赖性的最小依赖距离为 n。如果该距离为 0,则循环实际上是 Fortran 风格的 doall 循环,并且应在目标处理程序上流水线化。如果该距离大于 0,则编译器(流水线化程序)将只尝试流水线化 n 次连续迭代。编译器在优化级别 3 或更高级别上使用此信息。

此 pragma 的作用域从它自身开始,在以下任何一种情况最先出现时结束:下一块的开始部分、当前块内部的下一个 for 循环、当前块的结尾。该 pragma 应用于 pragma 作用域结束前的下一个 for 循环。

2.8.21 rarely_called

#pragma rarely_called(funcname[, funcname] )

该 pragma 提示编译器,指定的函数很少被调用。这样,编译器可以在此类例程的调用点执行配置文件反馈样式的优化,而没有配置文件收集阶段的开销。因为该 pragma 只是建议,所以编译器不执行基于该 pragma 的任何优化。

必须在该 pragma 之前使用原型或空参数列表声明指定的函数。以下是 #pragma rarely_called 的示例:


extern void error (char *message);
#pragma rarely_called(error)

2.8.22 redefine_extname

#pragma redefine_extname old_extname new_extname

该 pragma 导致目标代码中名称 old_extname 的各个外部定义具体值被 new_extname 替换。结果,链接程序在链接时只看到名称 new_extname。如果在第一次使用 old_extname 作为函数定义、初始化函数或表达式之后遇到 #pragma redefine_extname,则该作用未定义。(在 – Xs 模式下不支持该 pragma。)

如果 #pragma redefine_extname 可用,编译器会提供预定义宏 __PRAGMA_REDEFINE_EXTNAME 的定义,这样您可以编写在有无 #pragma redefine_extname 的条件下均可运行的可移植代码。

#pragma redefine_extname 的目的是,提供一种在函数名称无法更改时重新定义函数接口的有效方法。例如,当某个库中必须保留初始函数定义时,为了与现有程序兼容,创建同一函数的新定义以供新程序使用。这可以通过用新名称将新函数定义增加到库中来完成。因此,声明函数的头文件使用 #pragma redefine_extname,以便对函数的所有使用均与该函数的新定义链接。


#if    defined(__STDC__)

#ifdef __PRAGMA_REDEFINE_EXTNAME
extern int myroutine(const long *, int *);
#pragma redefine_extname myroutine __fixed_myroutine
#else /* __PRAGMA_REDEFINE_EXTNAME */

static int
myroutine(const long * arg1, int * arg2)
{
    extern int __myroutine(const long *, int*);
    return (__myroutine(arg1, arg2));
}
#endif /* __PRAGMA_REDEFINE_EXTNAME */

#else /* __STDC__ */

#ifdef __PRAGMA_REDEFINE_EXTNAME
extern int myroutine();
#pragma redefine_extname myroutine __fixed_myroutine
#else /* __PRAGMA_REDEFINE_EXTNAME */

static int
myroutine(arg1, arg2)
    long *arg1;
    int *arg2;
{
    extern int __fixed_myroutine();
    return (__fixed_myroutine(arg1, arg2));
}
#endif /* __PRAGMA_REDEFINE_EXTNAME */

#endif /* __STDC__ */

2.8.23 returns_new_memory

#pragma returns_new_memory (funcname[, funcname])

该 pragma 断言指定函数的返回值在调用点上不使用任何内存作为别名。实际上,该调用返回一个新存储单元。该信息使优化器更好地跟踪指针值并澄清存储单元。这将导致循环的调度、流水线化和并行化的改进。然而,如果断言为假,则程序的行为未定义。

只有在使用原型或空参数列表声明指定的函数之后才允许使用该 pragma,如以下示例所示:


void *malloc(unsigned);
#pragma returns_new_memory(malloc)

2.8.24 unknown_control_flow

#pragma unknown_control_flow (funcname[, funcname])

为了描述改变调用程序的流程图的过程, C 编译器提供了 #pragma unknown_control_flow 指令。通常,该指令带有 setjmp() 等函数的声明。在 Sun 系统上,include 文件 <setjmp.h> 包含以下内容:


extern int setjmp();
#pragma unknown_control_flow(setjmp)

同样,必须声明具有 setjmp() 类似属性的其他函数。

原则上,识别该属性的优化器可在控制流程图中插入相应的界限,从而在调用 setjmp() 的函数中安全地处理函数调用,同时保持优化流程图的未受影响部分的代码的能力。

必须在该 pragma 之前使用原型或空参数列表声明指定的函数。

2.8.25 unroll

(SPARC) #pragma unroll (unroll_factor )

对于参数 unroll_factor,该 pragma 接受正整数常量值。当解开因子不为 1 时,该指令作为对编译器的建议,指出指定的循环应由给定的因子解开。如果可能,编译器将使用该解开因子。如果解开因子值为 1,该指令作为一个命令,向编译器指出不解开该循环。编译器在优化级别 3 或更高级别上使用此信息。

此 pragma 的作用域从它自身开始,在以下任何一种情况最先出现时结束:下一块的开始部分、当前块内部的下一个 for 循环、当前块的结尾。该 pragma 应用于 pragma 作用域结束前的下一个 for 循环。

2.8.26 warn_missing_parameter_info

#pragma [no_]warn_missing_parameter_info

如果指定 #pragma warn_missing_parameter_info,编译器将针对函数声明不包含任何参数类型信息的函数调用发出一条警告。请看以下示例:


exmaple% cat -n t.c
     1    #pragma warn_missing_parameter_info
     2    
     3    int foo();
     4    
     5    int bar () {
     6    
     7       int i;
     8    
     9       i = foo(i);
    10    
    11       return i;
    12    }
% cc t.c -c -errtags
"t.c", line 9: warning: function foo has no prototype (E_NO_MISSED_PARAMS_ALLOWED)
example%

#pragma no_warn_missing_parameter_info 将使任何以前的 #pragma warn_missing_parameter_info 无效。

缺省情况下,#pragma no_warn_missing_parameter_info 是有效的。

2.8.27 weak

#pragma weak symbol1 [= symbol2]

定义弱全局符号。该 pragma 主要在源文件中用于生成库。如果链接程序无法解析弱符号,它并不生成错误消息。


#pragma weak symbol

symbol 定义为弱符号。如果链接程序找不到 symbol 的定义,它不会生成错误消息。


#pragma weak symbol1 = symbol2

symbol1 定义为弱符号,它是符号 symbol2 的别名。只能在已定义 symbol2(可以在源文件中定义,也可以在其中一个包含的头文件中定义)的同一转换单元中使用这种形式的 pragma。否则,将导致编译错误。

如果程序调用但未定义 symbol1,并且 symbol1 在所链接的库中是弱符号,则链接程序使用该库中的定义。但是,如果程序定义自己的 symbol1 版本,则使用该程序的定义,而不使用库中 symbol1 的弱全局定义。如果程序直接调用 symbol2,则使用库中的定义;symbol2 的重复定义会导致错误。

2.9 预定义的名称

以下标识符预定义为类似对象的宏:

表 2–3 预定义标识符

标识符 

说明 

__STDC__

__STDC__ 1 -Xc

__STDC__ 0 -Xa-Xt

未定义 -Xs

如果 __STDC__ 未定义 (#undef __STDC__),编译器将发出警告。__STDC__-Xs 模式下未定义。

预定义(在 -Xc 模式下无效):

以下预定义在所有模式下均有效:

编译器还预定义类似对象的宏 __PRAGMA_REDEFINE_EXTNAME,表示可识别该 pragma。以下内容仅在 -Xa-Xt 模式下预定义:

__RESTRICT

2.10 errno 的值

如果指定 -fast,则不应依赖 errno 的值,因为该值会因代码优化而改变。要避免此问题,最简单的方法是不指定 -fast

但是,如果指定 -fast,并且依赖 errno 的值,请执行以下操作:

2.11 _Restrict 关键字

C 编译器支持 _Restrict 关键字,该关键字与 C99 标准中的 restrict 关键字等效。_Restrict 关键字可与 -xc99=none-xc99=all 一起使用,而 restrict 关键字只能与 -xc99=all 一起使用。

有关支持的 C99 特性的更多信息,请参见表 C–6

2.12 _ _asm 关键字

_ _asm 关键字(注意开头的两个下划线)asm 关键字的同义字。如果您使用 asm 而不是 _ _asm,并且在 – Xc 模式下编译,则编译器会发出警告。如果您在 – Xc 模式下使用 _ _asm,则编译器不会发出警告。_ _asm 语句采用以下形式:


_ _asm("string");

其中 string 是有效的汇编语言语句。_ _asm 语句必须在函数主体内部出现。

2.13 环境变量

本节列出用于控制编译和运行环境的环境变量。

2.13.1 OMP_DYNAMIC

启用或禁用线程数的动态调整。

2.13.2 OMP_NESTED

启用或禁用嵌套并行操作。

2.13.3 OMP_NUM_THREADS

设置执行过程中要使用的线程数。

2.13.4 OMP_SCHEDULE

设置运行时调度类型和块大小。

2.13.5 PARALLEL

(SPARC) 指定可供程序进行多处理器执行的处理器数。如果目标机器具有多个处理器,线程可以映射到独立的处理器。运行该程序将导致创建执行程序的并行化部分的两个线程。

2.13.6 SUN_PROFDATA

控制 -xprofile=collect 命令在其中存储执行频率数据的文件的名称。

2.13.7 SUN_PROFDATA_DIR

控制 -xprofile=collect 命令在其中放置执行频率数据文件的目录。

2.13.8 SUNPRO_SB_INIT_FILE_NAME

包含 .sbinit(5) 文件的目录的绝对路径名。只有在使用 -xsb-xsbfast 标志时,才能使用该变量。

2.13.9 SUNW_MP_THR_IDLE

控制每个帮助器线程的任务结束状态,可设置为 spin nssleep n ms。缺省值为 sleep。有关详细信息,请参见《OpenMP API 用户指南》。

2.13.10 TMPDIR

cc 通常在目录 /tmp 中创建临时文件。可以通过将环境变量 TMPDIR 设置为您选定的目录,来指定其他目录。但是,如果 TMPDIR 不是有效目录,cc 将使用 /tmp-xtemp 选项优先于 TMPDIR 环境变量。

如果您使用 Bourne shell,请键入:


$ TMPDIR=dir; export TMPDIR

如果您使用 C shell,请键入:


% setenv TMPDIR dir

2.14 如何指定 include 文件

包含随 C 编译系统提供的任何标准头文件,请使用以下格式:


#include <stdio.h>

尖括号 (<>) 使预处理程序在系统中头文件的标准位置(通常为 /usr/include 目录)搜索头文件。

对于您已存储在您自己的目录中的头文件,格式不同:


#include "header.h"

对于 #include "foo.h" 形式的语句(其中使用了引号),编译器按以下顺序搜索 include 文件:

  1. 当前目录(即放置“包含”文件的目录)

  2. -I 选项命名的目录(如果有)

  3. /usr/include 目录

如果头文件所在的目录与包含该头文件的源文件所在的目录不同,请指定使用 cc– I 选项存储头文件时所用目录的路径。例如,假设在源文件 mycode.c 中已包含 stdio.hheader.h


#include <stdio.h>
#include "header.h"

进一步假设 header.h 存储在目录 ../defs 中。命令:


% cc– I../defs mycode.c

指示预处理程序首先在包含 mycode.c 的目录中搜索 header.h,然后在目录 ../defs 中搜索,最后在标准位置搜索。它还指示预处理程序首先在 ../defs 中搜索 stdio.h,然后在标准位置搜索。不同之处在于:仅对于其名称用引号括起的头文件,才查找当前目录。

您可以在 cc 命令行上多次指定 – I 选项。预处理程序按指定目录出现的顺序查找它们。您可以在同一个命令行上为 cc 指定多个选项:


% cc– o prog– I../defs mycode.c

2.14.1 使用 -I- 选项更改搜索算法

新的 -I- 选项提供对缺省搜索规则的更多控制。只有命令行上的第一个 -I- 选项的作用如本节所述。当命令行上出现 -I- 时:

对于 #include "foo.h" 形式的 include 文件,按以下顺序搜索目录:

1. 使用 -I 选项指定的目录(在 -I- 前后)。

2. 编译器提供的 C++ 头文件、ANSI C 头文件和专用文件的目录。

3. /usr/include 目录。

对于 #include <foo.h> 形式的 include 文件,按以下顺序搜索目录:

1. 使用 -I 选项指定的目录(在 -I- 后面)。

2. 编译器提供的 C++ 头文件、ANSI C 头文件和专用文件的目录。

3. /usr/include 目录。

下例显示在编译 prog.c 时使用 -I- 的结果。


prog.c
#include "a.h"

#include <b.h>

#include "c.h"


c.h
#ifndef _C_H_1

#define _C_H_1

int c1;

#endif


int/a.h
#ifndef _A_H

#define _A_H

#include "c.h"

int a;

#endif


int/b.h
#ifndef _B_H

#define _B_H

#include <c.h>

int b;

#endif
int/c.h
#ifndef _C_H_2

#define _C_H_2

int c2;

#endif

以下命令显示了在当前目录(包含文件的目录)中搜索 #include "foo.h" 形式的包含语句的缺省行为。当处理 inc/a.h 中的 #include "c.h" 语句时,预处理程序包含 inc 子目录中的 c.h 头文件。当处理 prog.c 中的 #include "c.h" 语句时,预处理程序包含具有 prog.c 的目录中的 c.h 文件。请注意,-H 选项指示编译器打印所包含文件的路径。


example% cc -c -Iinc -H prog.c
inc/a.h
            inc/c.h
inc/b.h
            inc/c.h
c.h

以下命令显示了 -I- 选项的效果。当预处理程序处理 #include "foo.h" 形式的语句时,它并不首先在包含目录中查找,而是按照通过 -I 选项指定的目录在命令行上的显示顺序搜索这些目录。当处理 inc/a.h 中的 #include "c.h" 语句时,预处理程序包含 ./c.h 头文件而不是 inc/c.h 头文件。


example% cc -c -I. -I- -Iinc -H prog.c
inc/a.h
            ./c.h
inc/b.h
            inc/c.h
./c.h

2.14.1.1 警告

任何时候都不要将编译器安装区域 /usr/include/lib/usr/lib 指定为搜索目录。

有关更多信息,请参见B.2.34 -I[-| dir]