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

2.      编译器的初始化

编译器的核心始于YOUR-GCC-SOURCE-DIR/gcc目录下,文件main.c中的main函数。该函数立即调用函数toplev_main

 

4684 int

4685 toplev_main (unsigned int argc, const char **argv)                                       in toplev.c

4686 {

4687   save_argv = argv;

4688

4689   /* Initialization of GCC's environment, and diagnostics.  */

4690   general_init (argv[0]);

4691

4692   /* Parse the options and do minimal processing; basically just

4693     enough to default flags appropriately.  */

4694   decode_options (argc, argv);

4695

4696   randomize ();

4697

4698   /* Exit early if we can (e.g. -help).  */

4699   if (!exit_after_options)

4700     do_compile ();

4701

4702   if (errorcount || sorrycount)

4703     return (FATAL_EXIT_CODE);

4704

4705   return (SUCCESS_EXIT_CODE);

4706 }

 

这个函数完成了整个的编译过程!它所调用的函数都非常复杂。现在我们就一个一个来学习它们。第一个被调用的函数是general_init。正如它的名字所表示的,它为编译器做一些基本的初始化工作。

 

4208 static void

4209 general_init (const char *argv0)                                                                 in toplev.c

4210 {

4211   const char *p;

4212

4213   p = argv0 + strlen (argv0);

4214   while (p != argv0 && !IS_DIR_SEPARATOR (p[-1]))

4215     --p;

4216   progname = p;

4217

4218   xmalloc_set_program_name (progname);

4219

4220   hex_init ();

4221

4222   gcc_init_libintl ();

4223

4224   /* Initialize the diagnostics reporting machinery, so option parsing

4225     can give warnings and errors. */

4226   diagnostic_initialize (global_dc);

4227   /* Set a default printer. Language specific initializations will

4228     override it later. */

4229   pp_format_decoder (global_dc->printer) = &default_tree_printer;

4230

4231   /* Trap fatal signals, e.g. SIGSEGV, and convert them to ICE messages. */

4232 #ifdef SIGSEGV

4233   signal (SIGSEGV, crash_signal);

4234 #endif

4235 #ifdef SIGILL

4236   signal (SIGILL, crash_signal);

4237 #endif

4238 #ifdef SIGBUS

4239   signal (SIGBUS, crash_signal);

4240 #endif

4241 #ifdef SIGABRT

4242   signal (SIGABRT, crash_signal);

4243 #endif

4244 #if defined SIGIOT && (!defined SIGABRT || SIGABRT != SIGIOT)

4245   signal (SIGIOT, crash_signal);

4246 #endif

4247 #ifdef SIGFPE

4248   signal (SIGFPE, crash_signal);

4249 #endif

4250

4251   /* Other host-specific signal setup. */

4252   (*host_hooks.extra_signals)();

4253

4254   /* Initialize the garbage-collector, string pools and tree type hash

4255     table. */

4256   init_ggc ();

4257   init_stringpool ();

4258   init_ttree ();

4259

4260   /* Initialize register usage now so switches may override. */

4261   init_reg_sets ();

4262

4263   /* Register the language-independent parameters. */

4264   add_params (lang_independent_params, LAST_PARAM);

4265

4266   /* This must be done after add_params but before argument processing. */

4267   init_ggc_heuristics();

4268 }

 

4218行,在支持sbrk 的平台上,xmalloc_set_program_namefirst_break中保存程序当前数据段的边界。在内存分配失败的时候,它和102行的name一起,构成debugging信息。

 

98    void

99    xmalloc_set_program_name (s)                                                                  in xmalloc.c

100              const char *s;

101  {

102   name = s;

103  #ifdef HAVE_SBRK

104    /* Win32 ports other than cygwin32 don't have brk() */

105    if (first_break == NULL)

106      first_break = (char *) sbrk (0);

107  #endif /* HAVE_SBRK */

108  }

 

而在4220行,函数hex_init只定义在使用EBCDIC编码的IBM及其兼容系统中。在这些系统里,hex_init初始化数组_hex_value,后者将EBCDIC的字符‘0’~’f’映射到对应的ASCII 字符。而在其他系统中,_hex_value是到这些ASCII字符的自身映射。

接下来,在4222行,gcc_init_libintl初始化了翻译库(这个取决于终端(terminal)对字符集的支持)。

4251行,host_hooks提供了一个机制,使得平台有机会运行自己特定的初始化操作。对于Linux平台,extra_signals是一个空函数。

4256行,init_ggc初始化了编译器的垃圾收集器。GCC的垃圾收集也是一个比较大的题目,我们以后再看它。

2.1. 标识符哈希表的初始化

C++中,每个标识符(identifier)都是通过其字面的名字来区分(前端总是使用字面名字在当前绑定域中进行名字查找。修饰名是给汇编器和链接器用的)。在GCC里,为了使名字查找更加高效,标识符都是保存在哈希表中,这个哈希表由4257行的init_stringpool 来初始化。

 

57    void

58    init_stringpool (void)                                                                           in stringpool.c

59    {

60      /* Create with 16K (2^14) entries.  */

61      ident_hash = ht_create (14);

62      ident_hash->alloc_node = alloc_node;

63      gcc_obstack_init (&string_stack);

64    }

 

上面61行的ident_hash是一个ht类型的全局对象。而在下面46行,stack是一个栈对象。所有标识符的名字都是从中分配的。

 

49    struct ht *ident_hash;                                                                           in stringpool.c

 

43    struct ht                                                                                              in hashtable.h

44    {

45      /* Identifiers are allocated from here.  */

46      struct obstack stack;

47   

48      hashnode *entries;

49      /* Call back.  */

50      hashnode (*alloc_node) (hash_table *);

51   

52      unsigned int nslots;           /* Total slots in the entries array.  */

53      unsigned int nelements;     /* Number of live elements.  */

54   

55      /* Link to reader, if any. For the benefit of cpplib.  */

56      struct cpp_reader *pfile;

57   

58      /* Table usage statistics.  */

59      unsigned int searches;

60      unsigned int collisions;

61    };

 

上面48行的hashnode实际上是指向在下面27行的ht_identifier的指针。

 

37    typedef struct ht hash_table;                                                                 in hashtable.h

38    typedef struct ht_identifier *hashnode;

 

26    typedef struct ht_identifier ht_identifier;                                               in hashtable.h

27    struct ht_identifier GTY(())

28    {

29      const unsigned char *str;

30      unsigned int len;

31      unsigned int hash_value;

32    };

 

同时在27行,GTY(())把这个对象托管给垃圾回收器的标记。ht_create 一开始给哈希表分配一张有16K行的表。

 

53    hash_table *

54    ht_create (unsigned int order)                                                                      in hashtable.c

55    {

56      unsigned int nslots = 1 << order;

57      hash_table *table;

58   

59      table = xcalloc (1, sizeof (hash_table));

60   

61      /* Strings need no alignment.  */

62      _obstack_begin (&table->stack, 0, 0,

63                    (void *(*) (long)) xmalloc,

64                    (void (*) (void *)) free);

65   

66      obstack_alignment_mask (&table->stack) = 0;

67   

68      table->entries = xcalloc (nslots, sizeof (hashnode));

69      table->nslots = nslots;

70      return table;

71    }

 

66行的obstack_alignment_mask用于指定在该栈上创建对象的对齐掩模(注意这是栈的要求,类型本身还会有自己的对齐要求,最终的对齐量将是2者中的较大者)。

2.1.1. Obstack的管理

2.1.1.1.    初始化管理块

哈希表中的stack对象具有obstack类型,它有如下定义。它是栈的管理块。

 

168  struct obstack         /* control current object in current chunk */                      in obstack.h

169  {

170    long    chunk_size;            /* preferred size to allocate chunks in */

171    struct _obstack_chunk *chunk;  /* address of current struct obstack_chunk */

172    char     *object_base;         /* address of object we are building */

173    char     *next_free;            /* where to add next char to current object */

174    char     *chunk_limit;        /* address of char after current chunk */

175    PTR_INT_TYPE temp;            /* Temporary for some macros.  */

176    int   alignment_mask;             /* Mask of alignment for each object. */

177  #if defined __STDC__ && __STDC__

178   /* These prototypes vary based on `use_extra_arg', and we use

179      casts to the prototypeless function type in all assignments,

180      but having prototypes here quiets -Wstrict-prototypes.  */

181    struct _obstack_chunk *(*chunkfun) (void *, long);

182    void (*freefun) (void *, struct _obstack_chunk *);

183    void *extra_arg;        /* first arg for chunk alloc/dealloc funcs */

184  #else

185    struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk.  */

186    void (*freefun) ();            /* User's function to free a chunk.  */

187    char *extra_arg;         /* first arg for chunk alloc/dealloc funcs */

188  #endif

189    unsigned use_extra_arg:1;  /* chunk alloc/dealloc funcs take extra arg */

190    unsigned maybe_empty_object:1;/* There is a possibility that the current

191                                 chunk contains a zero-length object. This

192                                 prevents freeing the chunk if we allocate

193                                 a bigger chunk to replace it. */

194    unsigned alloc_failed:1;     /* No longer used, as we now call the failed

195                           handler on error, but retained for binary

196                           compatibility.  */

197  };

 

要使用obstack来管理内存,需要按对齐要求初始化这个对象,并指定用于分配和释放的函数。在这里,传入的size0(一开始是空的),alignment0(没有特殊要求),函数xmalloc作为chunkfun,函数free作为freefun

 

150  int

151  _obstack_begin (h, size, alignment, chunkfun, freefun)                                  in obstack.c

152       struct obstack *h;

153       int size;

154       int alignment;

155  #if defined (__STDC__) && __STDC__

156       POINTER (*chunkfun) (long);

157       void (*freefun) (void *);

158  #else

159       POINTER (*chunkfun) ();

160       void (*freefun) ();

161  #endif

162  {

163    register struct _obstack_chunk *chunk; /* points to new chunk */

164 

165    if (alignment == 0)

166      alignment = (int) DEFAULT_ALIGNMENT;

167    if (size == 0)

168      /* Default size is what GNU malloc can fit in a 4096-byte block.  */

169    {

170      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.

171        Use the values for range checking, because if range checking is off,

172        the extra bytes won't be missed terribly, but if range checking is on

173        and we used a larger request, a whole extra 4096 bytes would be

174        allocated.

175 

176       These number are irrelevant to the new GNU malloc. I suspect it is

177       less sensitive to the size of the request.  */

178      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))

179                  + 4 + DEFAULT_ROUNDING - 1)

180                    & ~(DEFAULT_ROUNDING - 1));

181      size = 4096 - extra;

182    }

183 

184  #if defined (__STDC__) && __STDC__

185    h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;

186    h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;

187  #else

188    h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;

189    h->freefun = freefun;

190  #endif

191    h->chunk_size = size;

192    h->alignment_mask = alignment - 1;

193    h->use_extra_arg = 0;

194 

195    chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);

196    if (!chunk)

197      (*obstack_alloc_failed_handler) ();

198    h->next_free = h->object_base = chunk->contents;

199    h->chunk_limit = chunk->limit

200      = (char *) chunk + h->chunk_size;

201    chunk->prev = 0;

202    /* The initial chunk now contains no empty object.  */

203    h->maybe_empty_object = 0;

204    h->alloc_failed = 0;

205    return 1;

206  }

 

在下面通过fooalignDEFAULT_ALIGNMENT实际上是double的对齐量,在Linux x86上是4。而DEFAULT_ROUNDING使用fooround来决定对第一个块的舍入值,在Linux x86上是8

 

62    struct fooalign {char x; double d;};                                                             in obstack.h

63    #define DEFAULT_ALIGNMENT  /

64      ((PTR_INT_TYPE) ((char *) &((struct fooalign *) 0)->d - (char *) 0))

65    /* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.

66      But in fact it might be less smart and round addresses to as much as

67      DEFAULT_ROUNDING. So we prepare for it to do that.  */

68    union fooround {long x; double d;};

69    #define DEFAULT_ROUNDING (sizeof (union fooround))

 

我们期望分配的块能刚好占据一个页面(通常是4k)。对于这个块大小,我们要扣除被malloc用于簿记数据的大小。在上面178行的注释提到,malloc里结构体mhead 用了12字节,另有4个额外字节。这两部分都要在DEFAULT_ROUNDING大小的边界上对齐,因此最终结果为24个字节。

被分配的对象具有类型_obstack_chunk,它有如下定义。

 

161  struct _obstack_chunk           /* Lives at front of each chunk. */                      in obstack.h

162  {

163    char  *limit;                    /* 1 past end of this chunk */

164    struct _obstack_chunk *prev;    /* address of prior chunk or NULL */

165    char     contents[4];           /* objects begin here */

166  };

 

正如它的名字所提示的,obstack只能被用作栈。栈的生长将从contents开始直到到达limit块的底部。填满的块通过prev域依次连接。

2.1.1.2.    内存分配

可以通过以下的宏从参数hobstack对象)中分配由length指定大小的内存。还有一种是通过obstack_grow系列宏在obstack中分配类如字符串这样的没有对齐要求的对象。

 

566  #define obstack_alloc(h,length)                                 /                                  in obstack.h

567  (obstack_blank ((h), (length)), obstack_finish ((h)))

 

560  #define obstack_blank(h,length)                                /                                  in obstack.h

561  ( (h)->temp = (length),                                                 /

562    (((h)->chunk_limit - (h)->next_free < (h)->temp)                 /

563     ? (_obstack_newchunk ((h), (h)->temp), 0) : 0),                  /

564    obstack_blank_fast (h, (h)->temp))

 

当块中剩余的内存足够,将执行以下步骤。

 

348  #define obstack_blank_fast(h,n) ((h)->next_free += (n))                                in obstack.h

 

上面这个宏使得空对象能被检查出来。下面576行,如果obstack_blank_fast执行后next_free等于object_base,我们在请求空对象。当所要求的内存能被满足,就要调用下面的obstack_finish使内存被真正分配出来。

 

575  #define obstack_finish(h)                                        /                                  in obstack.h

576  ( ((h)->next_free == (h)->object_base                                   /

577     ? (((h)->maybe_empty_object = 1), 0)                               /

578     : 0),                                                    /

579    (h)->temp = __PTR_TO_INT ((h)->object_base),                        /

580    (h)->next_free                                              /

581      = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) /

582                    & ~ ((h)->alignment_mask)),                            /

583    (((h)->next_free - (char *) (h)->chunk                          /

584      > (h)->chunk_limit - (char *) (h)->chunk)                        /

585       ? ((h)->next_free = (h)->chunk_limit) : 0),                            /

586         (h)->object_base = (h)->next_free,                             /

587    __INT_TO_PTR ((h)->temp))

 

因为next_free 需要在alignment_mask的边界上对齐,next_free的结果可能超出块的范围。在这种情况下,会有部分用于对齐要求的字节分配不出来,但不影响分配出的对象的使用。因此调整next_free至块的底部,不考虑这部分字节。注意到587行是obstack_finish的值,它同时也是obstack_alloc的值,指向新分配对象的头。

当当前块剩余空间不能满足分配要求(上面562行),需要分配足够大的新块。注意到下面282行,如果通过obstack_alloc来分配对象,next_freeobject_base 一直是相等的(通过obstack_grow的话,则表示已请求,但尚未真正分配的内存。在调用obstack_finish前,可以多次调用obstack_grow)。在287行,我们不能只分配所要求的内存,应该稍微多分一点,尤其对于obstack_grow这样的应用。另外新块的容量不能小于chunk_size4k – 24)。

 

274  void

275  _obstack_newchunk (h, length)                                                                   in obstack.c

276       struct obstack *h;

277       int length;

278  {

279    register struct _obstack_chunk *old_chunk = h->chunk;

280    register struct _obstack_chunk *new_chunk;

281    register long       new_size;

282    register long obj_size = h->next_free - h->object_base;

283    register long i;

284    long already;

285 

286   /* Compute size for new chunk.  */

287    new_size = (obj_size + length) + (obj_size >> 3) + 100;

288    if (new_size < h->chunk_size)

289      new_size = h->chunk_size;

290 

291    /* Allocate and initialize the new chunk.  */

292    new_chunk = CALL_CHUNKFUN (h, new_size);

293    if (!new_chunk)

294      (*obstack_alloc_failed_handler) ();

295    h->chunk = new_chunk;

296    new_chunk->prev = old_chunk;

297    new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;

298 

299    /* Move the existing object to the new chunk.

300      Word at a time is fast and is safe if the object

301      is sufficiently aligned.  */

302    if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)

303    {

304      for (i = obj_size / sizeof (COPYING_UNIT) - 1;

305          i >= 0; i--)

306        ((COPYING_UNIT *)new_chunk->contents)[i]

307            = ((COPYING_UNIT *)h->object_base)[i];

308      /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,

309        but that can cross a page boundary on a machine

310        which does not do strict alignment for COPYING_UNITS.  */

311      already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);

312    }

313    else

314      already = 0;

315    /* Copy remaining bytes one by one.  */

316    for (i = already; i < obj_size; i++)

317      new_chunk->contents[i] = h->object_base[i];

318 

319    /* If the object just copied was the only data in OLD_CHUNK,

320      free that chunk and remove it from the chain.

321      But not if that chunk might contain an empty object.  */

322    if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)

323    {

324      new_chunk->prev = old_chunk->prev;

325     CALL_FREEFUN (h, old_chunk);

326    }

327 

328    h->object_base = new_chunk->contents;

329    h->next_free = h->object_base + obj_size;

330    /* The new chunk certainly contains no empty object yet.  */

331    h->maybe_empty_object = 0;

332  }

 

对于没有被真正分配的内存请求,接下来需要将它们从旧块拷贝到新块。因为contents标记了块的头,object_base标记了未兑现内存请求的开头。在旧块中,如果2者相等,而且块不包含空对象,那么该块只包含了这个已被移去新块的内存请求,它可以被释放。

2.1.1.3.    释放对象

作为栈,当要释放指定对象时,所有在其上的对象亦被释放。

 

597  #define obstack_free(h,obj)                                      /                                  in obstack.h

598  ( (h)->temp = (char *) (obj) - (char *) (h)->chunk,                 /

599    (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)/

600     ? (int) ((h)->next_free = (h)->object_base                          /

601             = (h)->temp + (char *) (h)->chunk)                          /

602     : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0)))

 

如果对象不在当前块,它必定在其他块中。我们已经见到块通过prev域连在一起。越接近当前块,块就越新鲜。根据后进先出的栈原则,当我们找到目标块时,那些通过prev链访问到的块也需要释放。

 

372  void

373  _obstack_free (h, obj)                                                                               in obstack.c

374       struct obstack *h;

375       POINTER obj;

376  {

377    register struct _obstack_chunk *lp;   /* below addr of any objects in this chunk */

378    register struct _obstack_chunk *plp;  /* point to previous chunk if any */

379 

380    lp = h->chunk;

381    /* We use >= because there cannot be an object at the beginning of a chunk.

382      But there can be an empty object at that address

383      at the end of another chunk.  */

384    while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))

385    {

386      plp = lp->prev;

387      CALL_FREEFUN (h, lp);

388      lp = plp;

389      /* If we switch chunks, we can't tell whether the new current

390        chunk contains an empty object, so assume that it may.  */

391      h->maybe_empty_object = 1;

392    }

393    if (lp)

394    {

395      h->object_base = h->next_free = (char *) (obj);

396      h->chunk_limit = lp->limit;

397      h->chunk = lp;

398    }

399    else if (obj != 0)

400      /* obj is not in any of the chunks! */

401      abort ();

402  }

 

如果有块被释放,目标块会变更为当前块。这时我们不可能知道其上是否有空对象,因此假定其有。在_obstack_newchunk中,这个块就不会因为没有对象而被释放。那么万一有空对象,它也不会因为块已被释放而走入401行。

2.1.1. 元素的分配函数

init_stringpool62行所示,标识符哈希表元素的分配函数是alloc_node

 

67    static hashnode

68    alloc_node (hash_table *table ATTRIBUTE_UNUSED)                          in stringpool.c

69    {

70      return GCC_IDENT_TO_HT_IDENT (make_node (IDENTIFIER_NODE));

71    }

 

因为make_node使用IDENTIFIER_NODE作为参数,它会使用lang_hooks.identifier_size 来确定节点的大小。对于C++,它是定义在langhooks-def.h中的默认值 - sizeof (struct lang_identifier)lang_identifier也是与语言相关的。对于C++,它有如下定义。

 

220  struct lang_identifier GTY(())                                                                             in cp-tree.h

221  {

222    struct c_common_identifier c_common;

223    cxx_binding *namespace_bindings;

224    cxx_binding *bindings;

225    tree class_value;

226    tree class_template_info;

227    tree label_value;

228   tree implicit_decl;

229    tree error_locus;

230  };

 

c_common_identifier的定义中,它的第一个成员是tree_common

 

180  struct c_common_identifier GTY(())                                                     in c-common.h

181  {

182    struct tree_common common;

183    struct cpp_hashnode node;

184  };

 

另外,在lang_identifier 定义中,域namespace_bindings指向定义了该标识符的名字空间,而域bindings指向定义了该标识符的非名字空间的域。后面我们可以看到这个安排对名字查找是必要的。

2.1.1.1.    标识符同时存在树和哈希表的方式

上面make_node返回新建的lang_identifier对象,GCC_IDENT_TO_HT_IDENT将其转换为tree_identifier,进而返回ht_identifier部分。

 

753  #define HT_IDENT_TO_GCC_IDENT(NODE) /                                                        in tree.h

754    ((tree) ((char *) (NODE) - sizeof (struct tree_common)))

755  #define GCC_IDENT_TO_HT_IDENT(NODE) (&((struct tree_identifier *) (NODE))->id)

 

tree_identifier的定义中,它的第一个成员也是tree_common。但和cpp_hashnode比较,它第二个成员是什么呢?

 

757  struct tree_identifier GTY(())                                                                                    in tree.h

758  {

759    struct tree_common common;

760    struct ht_identifier id;

761  };

 

顺理成章,cpp_hashnode的第一个成员是ht_identifier。正如我们已看到的,标识符除了呆在树里,还需要通过哈希表来组织。因此,lang_identifier的第一个成员使得对象能生存在树中,而第二个成员使得对象在哈希表中也过得下去。

 

478  struct cpp_hashnode GTY(())                                                                       in cpplib.h

479  {

480    struct ht_identifier ident;

481    unsigned int is_directive : 1;

482    unsigned int directive_index : 7;       /* If is_directive,

483                                  then index into directive table.

484                                  Otherwise, a NODE_OPERATOR.  */

485    unsigned char rid_code;            /* Rid code - for front ends.  */

486    ENUM_BITFIELD(node_type) type : 8;  /* CPP node type.  */

487    unsigned char flags;                 /* CPP flags.  */

488 

489    union _cpp_hashnode_value

490    {

491      /* If a macro.  */

492      cpp_macro * GTY((skip (""))) macro;

493      /* Answers to an assertion.  */

494      struct answer * GTY ((skip (""))) answers;

495      /* Code for a builtin macro.  */

496      enum builtin_type GTY ((tag ("1"))) builtin;

497      /* Macro argument index.  */

498      unsigned short GTY ((tag ("0"))) arg_index;

499    } GTY ((desc ("0"))) value;

500  };

 

cpp_hashnode首先由词法分析器创建,彼时它是标记(tokencpp_token的一部分。它主要为预处理器使用。

2.1.2. 哈希表的查找

这个哈希表的查找函数是cpp_lookup

 

91    cpp_hashnode *

92    cpp_lookup (cpp_reader *pfile, const unsigned char *str, unsigned int len)      in cpphash.c

93    {

94      /* ht_lookup cannot return NULL.  */

95      return CPP_HASHNODE (ht_lookup (pfile->hash_table, str, len, HT_ALLOC));

96    }

 

95行,ht_loopup返回hashnode的地址,CPP_HASHNODE将它转换为cpp_hashnode*

 

470  #define CPP_HASHNODE(HNODE)   ((cpp_hashnode *) (HNODE))            in cpplib.h

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值