GNU C扩展

GNU C是在C89标准(ANSI  C 标准)的基础上的扩展,以下内容仅作为《GNU C用户手册》中,区别于C89标准的记录,以供自查。《GNU C 用户手册》参考网址:

http://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Initializing-Pointers

 

1、标识符扩展

GUN C 中可以使用 美元字符“$”

2、关键字扩展

以下是ANSI C89认可的关键字列表:

auto break case char const continue default do double else enum extern

float for goto if int long register return short signed sizeof static

struct switch typedef union unsigned void volatile while

ISO C99添加了以下关键字:

inline _Bool _Complex _Imaginary

GNU扩展添加以下关键字:

__FUNCTION__ __PRETTY_FUNCTION__ __alignof __alignof__ __asm

__asm__ __attribute __attribute__ __builtin_offsetof __builtin_va_arg

__complex __complex__ __const __extension__ __func__ __imag __imag__

__inline __inline__ __label__ __null __real __real__

__restrict __restrict__ __signed __signed__ __thread __typeof

__volatile __volatile__

在具有GNU扩展的ISO C99和C89中,以下内容也被识别为关键字:

restrict

 

3、数据类型扩展

ISO C99和GNU C扩展都添加整数类型long long int 和unsigned long long int。你可以用两个“L”得到一个 long long int常量; 添加一个'U'表示一个 unsigned long long int常量。例如:45ULL。

\e  <ESC>字符。(这是GNU扩展。)

long long int

64位long long int数据类型可以保持-9223372036854775808 到9223372036854775807 范围内的整数值。您也可以参考这个数据类型long long, signed long long int或signed long long。此类型不是C89的一部分,但它既是C99的一部分,也是GNU C扩展的一部分。

unsigned long long int

64位unsigned long long int数据类型可以包含至少0到18446744073709551615范围内的整数值。您也可以将此数据类型称为unsigned long long。此类型不是C89的一部分,但它既是C99的一部分,也是GNU C扩展的一部分。

 

4、 复数类型

GCC引入了复数类型作为C89的扩展。C99 中也引入了类似的功能,但存在许多差异。我们首先描述标准复数类型。

(1) 标准复数类型

复杂类型在C99中引入。有三种复数类型:

float _Complex

double _Complex

long double _Complex

这里的名称以下划线和大写字母开头,以避免与现有程序的标识符冲突。但是,C99标准头文件<complex.h>引入了一些宏,这使得使用复杂类型更容易。

complex:扩展为_Complex。 这允许将变量声明为double complex,这似乎更自然。

I:const float _Complex类型的常量,该常量含有一个虚部,通常称为i。

该<complex.h>头文件还声明了许多具有执行复数计算功能的函数,例如 creal和cimag分别具有返回一个double complex类型复数的实部和虚部的功能。还提供了其他功能的函数,例如:

#include <complex.h>   

#include <stdio.h> 

void example (void)
{   

  complex double z = 1.0 + 3.0*I;

  printf ("Phase is %f, modulus is %f\n", carg (z), cabs (z));        

} 

(2)  复数类型的GNU扩展

GCC还引入了复数类型作为C89的GNU扩展,但拼写不同。GCC C89扩展中的浮点复杂类型是:

__complex__ float

__complex__ double

__complex__ long double

GCC的扩展允许浮点以外的复杂类型,因此您可以声明复杂的字符类型和复杂的整数类型; 实际上__complex__可以与任何原始数据类型一起使用。我们不会为您提供所有可能性的完整列表,但这里有一些示例:

__complex__ float:__complex__ float数据类型有两个组成部分:一个实部和一个虚部,这两者都是的float数据类型。

__complex__ int: __complex__ int数据类型也有两个组成部分:一个实部和一个虚部,这两者都是的int数据类型。

要提取复值表达式的实部,请使用关键字 __real__,然后使用表达式。同样,用于__imag__ 提取虚部。

__complex__ float a = 4 + 3i;

float b = __real__ a; / * b现在是4. * /

float c = __imag__ a; / * c现在是3. * /

此示例创建一个复数的浮点变量a,并将其实部定义为4,将其虚部定义为3。然后,将实部分配给浮点变量b,将虚部分配给浮点变量c。

 

5、结构体成员初始化

初始化成员时,GNUC允许指定要初始化的成员的名称。这样,您可以按照您喜欢的任何顺序初始化成员,甚至可以保留其中一些未初始化的成员。您可以使用以下方法。

第一种方法在C99中可用,在GCC中作为C89扩展:

struct point first_point = { .y = 10, .x = 5 };

您也可以省略句号并使用冒号而不是'=',虽然这是一个GNU C扩展:

struct point first_point = { y: 10, x: 5 };

在结构定义期间声明变量时,还可以初始化结构变量的成员:

struct point
{
    int x, y;

} first_point = { 5, 10 };

您还可以初始化少于所有结构变量的成员:

struct pointy
{

    int x, y;

    char *p;

};

struct pointy first_pointy = { 5 };

这里,x被初始化为5, y初始化为0,p初始化为NULL。这里的规则是,y 和p像静态变量一样初始化。

 

6、数组定义和初始化、零长度数组

使用带有GNU扩展的ISO C99或C89时,可以通过指定要初始化的数组索引来无序地初始化数组元素。要执行此操作,请在值前包括括号中的数组索引以及可选的赋值运算符。这是一个例子:

int my_array [5] = {[2] 5,[4] 9};

或者,使用赋值运算符:

int my_array [5] = {[2] = 5,[4] = 9};

这两个例子都相当于:

int my_array [5] = {0,0,5,0,9};

使用GNU扩展时,可以通过在表单中​​指定一个索引的范围,将一系列元素初始化为相同的值 。这是一个例子: [first] ... [last]

int new_array [100] = {[0 ... 9] = 1,[10 ... 98] = 2,3};

初始化元素0到9为1,元素10到98为2,元素99为3.(你也可以明确地写 [99] = 3。)另外,请注意你必须在'...'两边都有空格。

如果初始化数组的每个元素,则不必指定其大小; 其大小由您初始化的元素数决定。这是一个例子:

int my_array [] = {0,1,2,3,4};

虽然这没有明确说明数组有五个元素使用my_array[5],但它初始化了五个元素,因此它有多少元素。

或者,如果指定要初始化的元素,则数组的大小等于初始化的最高元素数加一。例如:

int my_array [] = {0,1,2,[99] = 99};

在该示例中,仅初始化了四个元素,但是最后一个元素下标是99,因此有100个元素。

 

GNU扩展中,元素的数量可以小到零。零长度数组作为结构体的最后一个元素很有用,它实际上是一个可变长度对象的头:

struct line
{
  int length;
  char contents[0];
};

{
  struct line *this_line = (struct line *)malloc (sizeof (struct line) + this_length);
  this_line -> length = this_length;
}

另一个GNU扩展不仅允许使用常量声明数组的大小,而且允许使用变量。例如,这是一个函数定义,它使用其参数作为元素数来声明一个数组:

int
my_function (int number)
{
  int my_array[number];
  …;
}

7、switch/case 语句扩展

作为GNU C扩展,您还可以在单​​个case标签中指定一系列连续的整数值,如下所示:

case low ... high:

这与相应数量的单个case 标签具有相同的效果。

此功能对ASCII字符代码范围特别有用:

case 'A' ... 'Z':

case的 “...”包括周围的空格; 否则,当您使用整数值时,它可能会被错误地解析。例如,写下这个:

case 1 ... 5:

而不是这个:

case 1...5:

通常使用switch语句来处理各种可能的errno值。在这种情况下,应该注意两个宏的errno 值实际上具有相同值的可能性,例如EWOULDBLOCK和 EAGAIN。

 

8、goto语句扩展

作为扩展,GCC允许goto语句跳转到void*变量指定的地址。为了使其工作,您还需要使用一元运算符&&(而不是&)来获取标签的地址 。这是一个人为的例子:

enum Play { ROCK=0, PAPER=1, SCISSORS=2 };
enum Result { WIN, LOSE, DRAW };


static enum Result turn (void)
{

  const void * const jumptable[] = {&&rock, &&paper, &&scissors};
  enum Play opp;                /* opponent’s play */

  goto *jumptable[select_option (&opp)];

rock:
    return opp == ROCK ? DRAW : (opp == PAPER ? LOSE : WIN);

paper:
    return opp == ROCK ? WIN  : (opp == PAPER ? DRAW : LOSE);

scissors:
    return opp == ROCK ? LOSE : (opp == PAPER ? WIN  : DRAW);

}

9、表达式的扩展

作为GNU C扩展,您可以使用括在括号中的复合语句来构建表达式。这允许您在表达式中包含循环,交换和局部变量。

回想一下,复合语句(也称为块)是由大括号包围的语句序列。在这个结构中,括号围绕括号。这是一个例子:

({ int y = function (); int z;

    if (y > 0) z = y;

   else z = - y;

   z; })

这是求function ()函数返回值的绝对值的表达式(虽然比较复杂)。

复合语句中的最后一件事应该是一个后跟分号的表达式; 此子表达式的值用作整个复合表达式的值。(如果在大括号中最后使用其他类型的语句,则构造具有类型void,因此实际上没有值。)

此功能在使宏定义“安全”方面是很有用的(因此它们只能评估每个操作数一次)。例如,“最大”函数通常被定义为标准C中的宏,如下所示:

#define max(a,b)  ((a)>(b)?(a):( b))

但是这个定义会计算a或b两次,如果操作数有副作用,结果会很糟糕。在GNU C中,如果您知道操作数的类型(这里我们假设int),您可以安全地定义宏,如下所示:

#define maxint(a,b)\
    ({int _a =(a),_ b =(b); _a> _b?_a:_b;})

如果您不知道操作数的类型,您仍然可以执行此操作,但必须使用typeof表达式或键入类型名称。typeof的作用是获取变量的类型。内核(版本:3.6.5)中的例子如下:

#define min(x, y) ({				\
	typeof(x) _min1 = (x);			\
	typeof(y) _min2 = (y);			\
	(void) (&_min1 == &_min2);		\
	_min1 < _min2 ? _min1 : _min2; })

内核中的另一种用法是:

#define min_t(type, x, y) ({			\
	type __min1 = (x);			\
	type __min2 = (y);			\
	__min1 < __min2 ? __min1 : __min2; })

常量表达式中不允许嵌入语句,例如枚举常量的值,位段的宽度或静态变量的初始值。

 

10、内建函数

作为GNU C扩展,您可以在其他函数中定义函数,这种技术称为内建函数。

以下是使用内建函数定义的尾递归因子函数的示例:

int

factorial (int x)
{

  int

  factorial_helper (int a, int b)
  {

    if (a < 1)
    {
      return b;
    }
    else
    {
      return factorial_helper ((a - 1), (a * b));
    }
  }

  return factorial_helper (x, 1);
}

注意,内建函数必须在函数的开头与变量声明一起定义,所有其他语句都必须在后面。

除了以上说明外,可变参数宏也被认为是GNUC的扩展,但是本人在《GNU C用户手册》中并未看到相关的说明,只有对可变参数函数的说明。例如:


//include/linux/kernel.h

#define pr_debug(fmt,arg...) printk(KERN_DEBUG fmt,##arg)

详细说明请参考:https://blog.csdn.net/shangzh/article/details/39398577

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值