GCC-3.4.6源代码学习笔记(32)

4.1.3.1.2.            读入文件

上面439行,如果文件找到并成功打开,_cpp_find_file将对应的_cpp_file对象返回,交由pfile->main_file保存。由此,可以开始读入文件的内容。

 

cpp_read_main_file (continue)

 

473      _cpp_stack_file (pfile, pfile->main_file, false);

474   

475      /* For foo.i, read the original filename foo.c now, for the benefit

476        of the front ends.  */

477      if (CPP_OPTION (pfile, preprocessed))

478      {

479        read_original_filename (pfile);

480        if (!pfile->map)

481          return NULL;

482        fname = pfile->map->to_file;

483      }

484      return fname;

485    }

 

在以下函数中,参数importtrue,如果文件是由#import指示包含的。关于#import指示,【6】给出如下解释。

GNU CPP支持2种方式来表示一个头文件只应被读入一次。但它们都不如#ifdef那样具有可移植性,建议在新的程序中不要使用。

Objective-C语言中,有#include的一个变体,叫#import,它引入(include)一个文件,但最多只引入一次。如果你使用了#import而不是#include,那么你不需要在头文件中加入条件语句来防止多次包含。GCC也允许在CC++中使用#import。然而它不是CC++的标准,因此不应为可移植程序使用。

#import不是一个实现得很好的特性。它要求头文件的使用者知道这个文件只应被包含一次。头文件的实现者写的头文件不需要使用者知道这些的话,要比这好的多。#ifndef能实现这一目标。

在当前的实现中,单个#import就会使得文件通过#import#include都不能被重新读入。 但你不能依赖这一点,不要同时使用#import#include来引用同一个头文件。

另一个防止头文件被多次引入的方法是#pragma once指示。如果在扫描一个头文件时,看到#pragm once,不管怎样,这个文件以后不会再读入了。

#pragma once没有#import的问题,但不是所有的预处理器都能识别它,因此不要在可移植程序中依赖它。

 

643    bool

644    _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, bool import)                  in cppfiles.c

645    {

646      cpp_buffer *buffer;

647      int sysp;

648   

649      if (!should_stack_file (pfile, file, import))

640        return false;

 

这里,对于由#import指示包含的头文件,once_onlyseen_once_only563行被_cpp_mark_file_once_only设置,因此下次该文件又开始被读入时,它将在556行退出。而这在572行,则是测试文件是否定义了守卫宏(header guard)。

 

549    static bool

550    should_stack_file (cpp_reader *pfile, _cpp_file *file, bool import)                      in cppfiles.c

551    {

552      _cpp_file *f;

553   

554      /* Skip once-only files.  */

555      if (file->once_only)

556        return false;

557   

558      /* We must mark the file once-only if #import now, before header

559        guard checks. Otherwise, undefining the header guard might

560        cause the file to be re-stacked.  */

561      if (import)

562      {

563        _cpp_mark_file_once_only (pfile, file);

564   

565        /* Don't stack files that have been stacked before.  */

566        if (file->stack_count)

567          return false;

568      }

569   

570      /* Skip if the file had a header guard and the macro is defined.

571        PCH relies on this appearing before the PCH handler below.  */

572      if (file->cmacro && file->cmacro->type == NT_MACRO)

573        return false;

574   

575      /* Handle PCH files immediately; don't stack them.  */

576      if (include_pch_p (file))

577      {

578        pfile->cb.read_pch (pfile, file->path, file->fd, file->pchname);

579        close (file->fd);

580       file->fd = -1;

581        return false;

582      }

4.1.3.1.2.1.      PCH文件的案例

上面576行,include_pch_p通过检查_cpp_filepch域来确定文件是否是PCH,这个域在pch_open_file中设置,如果这个PCH文件是有效的。

 

375    void

376    c_common_read_pch (cpp_reader *pfile, const char *name,                        in c-pch.c

377                   int fd, const char *orig_name ATTRIBUTE_UNUSED)

378    {

379      FILE *f;

380      struct c_pch_header h;

381      char *buf;

382      unsigned long written;

383      struct save_macro_data *smd;

384     

385      f = fdopen (fd, "rb");

386      if (f == NULL)

387      {

388        cpp_errno (pfile, CPP_DL_ERROR, "calling fdopen");

389        return;

390      }

391   

392      cpp_get_callbacks (parse_in)->valid_pch = NULL;

393   

394      if (fread (&h, sizeof (h), 1, f) != 1)

395      {

396        cpp_errno (pfile, CPP_DL_ERROR, "reading");

397        return;

398      }

399   

400      buf = xmalloc (16384);

401      for (written = 0; written < h.asm_size; )

402      {

403        long size = h.asm_size - written;

404        if (size > 16384)

405          size = 16384;

406        if (fread (buf, size, 1, f) != 1

407          || fwrite (buf, size, 1, asm_out_file) != 1)

408        cpp_errno (pfile, CPP_DL_ERROR, "reading");

409        written += size;

410      }

411       free (buf);

412   

413      cpp_prepare_state (pfile, &smd);

414   

415      gt_pch_restore (f);

416   

417      if (cpp_read_state (pfile, name, f, smd) != 0)

418        return;

419   

420      fclose (f);

421    }

 

注意到此处fd指向已经打开的有效的PCH文件,在上面385行的fdopen,我们可以继续从文件中target_data后的位置读入文件。在那个位置中应该包含c_pch_header,而这个结构仅含有一个域asm_size,它表示这个PCH文件的汇编形式内容大小。这部分内容直接写入asm_out_file

如果预处理头文件仅是包含汇编代码,它与库文件有何区别呢?我们又如何能使用在其中定义的数据结构和函数呢?因此,在汇编代码部分的后面,PCH文件还包括了中间树形式的内容,读入这部分内容并与当前编译单元的中间树合并,PCH文件的行为即与普通头文件无异。

 

589    void

590    cpp_prepare_state (cpp_reader *r, struct save_macro_data **data)                in cpppch.c

591    {

592      struct save_macro_data *d = xmalloc (sizeof (struct save_macro_data));

593     

594      d->macros = NULL;

595      d->count = ARRAY_SIZE (d->macros->macs);

596      cpp_forall_identifiers (r, save_macros, d);

597      d->saved_pragmas = _cpp_save_pragma_names (r);

598      *data = d;

599    }

 

cpp_reader中,它包含了域hash_table,这个域首先由_cpp_init_hashtable初始化。在预处理阶段,它用作文件中可见宏的哈希表。cpp_reader的实例一直代表当前处理的文件,因此现在我们正要处理PCH头文件,我们需要更新cpp_reader实例,首先需要保存当前文件中已见过的宏。

 

546    struct save_macro_item {                                                                          in cpppch.c

547      struct save_macro_item *next;

548      struct cpp_hashnode macs[64];

549    };

 

551    struct save_macro_data

552    {

553      struct save_macro_item *macros;

554      size_t count;

555      char **saved_pragmas;

556    };

 

注意在cpp_prepare_state595行,对于新建的缓存(buffercount64

 

112     void

113     cpp_forall_identifiers (cpp_reader *pfile, cpp_cb cb, void *v)                            in cpppch.c

114     {

115       /* We don't need a proxy since the hash table's identifier comes

116         first in cpp_hashnode.  */

117       ht_forall (pfile->hash_table, (ht_cb) cb, v);

118     }

 

209    void

210    ht_forall (hash_table *table, ht_cb cb, const void *v)                           in hashtable.c

211     {

212      hashnode *p, *limit;

213   

214      p = table->entries;

215      limit = p + table->nslots;

216      do

217        if (*p)

218        {

219          if ((*cb) (table->pfile, *p, v) == 0)

220            break;

221        }

222      while (++p < limit);

223    }

 

下面HT_STRHT_LEN分别访问cpp_hashnodeht_identifier域中的strlen域。注意ht_identifiercpp_hashnode的主体,并且是哈希表的规范节点。

 

561    static int                                                                                                 in cpppch.c

562    save_macros (cpp_reader *r ATTRIBUTE_UNUSED, cpp_hashnode *h, void *data_p)

563    {

564      struct save_macro_data *data = (struct save_macro_data *)data_p;

565      if (h->type != NT_VOID

566          && (h->flags & NODE_BUILTIN) == 0)

567      {

568        cpp_hashnode *save;

569        if (data->count == ARRAY_SIZE (data->macros->macs))

570        {

571          struct save_macro_item *d = data->macros;

572          data->macros = xmalloc (sizeof (struct save_macro_item));

573          data->macros->next = d;

574          data->count = 0;

575        }

576        save = data->macros->macs + data->count;

577        data->count++;

578        memcpy (save, h, sizeof (struct cpp_hashnode));

579        HT_STR (&save->ident) = xmemdup (HT_STR (HT_NODE (save)),

580                                     HT_LEN (HT_NODE (save)),

581                                     HT_LEN (HT_NODE (save)) + 1);

582      }

583      return 1;

584    }

4.1.3.1.2.1.1.              预处理阶段的#pragma指示

以下段落抽取自文献【6】中有关#pargma指示的部分

#pragma指示是C标准指定的方式,为编译器提供语言自身所不能携带的信息。1999 C标准指定了这个指示的3个形式(通常所说的pragmas)。C编译器可以自由为其他形式加上它所乐意的含义。

对于这个目的(注:即编译器引入语言标准外新的含义),GCC有倾向于使用语言语义扩展的传统,例如__attribute__。不过,GCC仍然定义了自己的几个pragmas。这些pragmas大多数会影响整个编译单元或源文件。

GCC 3版本,所有的GNU定义,支持的pragmas都被加上GCC前缀。这与C9定义的pragmas都带有STDC前缀做法一致。出于向后兼容的目的,被前一版本所识别的pragmas仍然可以不使用GCC前缀,但这个用法已经过时。一些更旧的pragmas整个都过时了。它们不能带有GCC前缀。

C99引入了_Pragma操作符。这个特性针对#pragma的一个主要问题:作为一个指示,它不能由宏展开来生成。

_Pragma的语法是_Pragma (string-literal),其中的字符串可以是普通的或宽字符字符串。对于string-literal,通过用‘/’替换‘//’‘”’替换‘/”’,实现非字符串化。其结果就如同出现在#pragma指示右手边那样被处理。例如,

_Pragma (“GCC dependency /”parse.y/””)

其作用等同于#pragma GCC dependency “parse.y”。这个效果可以通过宏来实现,例如

#define DO_PRAGMA(x) _Pragma (#x)

DO_PRAGMA (GCC dependency “parse.y”)

标准并未指明操作符何处可以出现。预处理器不接受在预处理条件指示如#if中的_pragma。出于安全,你最好不要在除#define外的指示中使用它,并让其自成一行。

用于预处理器的pragmas有:

 

#pragma GCC dependency

#pragma GCC dependency允许检查当前文件和其他文件的相对日期。如果其他文件比当前文件更新,给出一个警告。如果当前文件从其他文件派生,并由此需要重新生成,这个特性是有用的。其他文件使用通常的头文件查找路径来查找。可选的结尾文本可用于在警告消息中给出更多的信息。

#pragma GCC dependency “parse.y”

#pramga GCC dependency “/usr/include/time.h” rerun fuxincludes

 

#pragma GCC poison

有时,你希望从你的程序中完全移除一个识别符,并确保它不会回来。要确保实现这一点,你可以用该pragma“毒死”识别符。#pragma GCC poison后接一串要“毒害”的识别符。在这个指示后在源代码中,任何一个这些识别符的出现都会导致错误。例如,

#pragma GCC poison printf sprintf fprintf

sprintf (some_string, “hello”);

将产生一个错误。

如果一个“中毒”的识别符出现在其被“毒害”前定义的宏的扩展中,将不会导致错误。这使得你可以“毒害”一个识别符而不需要担心系统头文件定义的宏会使用它。例如,

#define strrchr rindex

#pragma GCC poison rindex

strrchr (someth_string, ‘h’);

将不会产生错误。

 

#pragma GCC system_header

这个pragma不带任何参数。它使得当前文件中余下的代码被视为来自系统头文件。

 

1095 char **

1096 _cpp_save_pragma_names (cpp_reader *pfile)                                             in cpplib.c

1097 {

1098   int ct = count_registered_pragmas (pfile->pragmas);

1099   char **result = xnewvec (char *, ct);

1100   (void) save_registered_pragmas (pfile->pragmas, result);

1101   return result;

1102 }

 

1062 static int

1063 count_registered_pragmas (struct pragma_entry *pe)                                           in cpplib.c

1064 {

1065   int ct = 0;

1066   for (; pe != NULL; pe = pe->next)

1067   {

1068     if (pe->is_nspace)

1069       ct += count_registered_pragmas (pe->u.space);

1070     ct++;

1071   }

1072   return ct;

1073 }

 

1078 static char **

1079 save_registered_pragmas (struct pragma_entry *pe, char **sd)

1080 {

1081   for (; pe != NULL; pe = pe->next)

1082   {

1083     if (pe->is_nspace)

1084       sd = save_registered_pragmas (pe->u.space, sd);

1085       *sd++ = xmemdup (HT_STR (&pe->pragma->ident),

1086              HT_LEN (&pe->pragma->ident),

1087              HT_LEN (&pe->pragma->ident) + 1);

1088   }

1089   return sd;

1090 }

 

在上面的例子中,GCC即构成1069行的pe->u.space中的space部分,其下又有dependencypoisonsystem_header save_registered_paragmas,由count_registered_pragmas配合,将这个pragmas树存入由save_macro_data构成的链表中。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值