GCC-3.4.6源代码学习笔记(152)
来源:互联网 发布:sql商业智能培训 编辑:程序博客网 时间:2024/06/07 23:33
5.12.5.2.2.2.2. 缺省实参
如果耗尽了arg,但parm还没有,parm必然包含了缺省实参,该形参列表应该以特殊节点void_list_node来结尾。那么在对应的节点中,TREE_VALUE保存了该类型,而TREE_PURPOSE是缺省实参的表达式。
4258 tree
4259 convert_default_arg (tree type, tree arg, tree fn, int parmnum) in call.c
4260 {
4261 /*If the ARG is an unparsed default argument expression, the
4262 conversion cannot be performed. */
4263 if (TREE_CODE (arg) ==DEFAULT_ARG)
4264 {
4265 error ("the defaultargument for parameter %d of `%D' has "
4266 "not yet beenparsed",
4267 parmnum, fn);
4268 returnerror_mark_node;
4269 }
4270
4271 if (fn &&DECL_TEMPLATE_INFO (fn))
4272 arg =tsubst_default_argument (fn, type, arg);
4273
4274 arg = break_out_target_exprs (arg);
4275
4276 if (TREE_CODE (arg) ==CONSTRUCTOR)
4277 {
4278 arg = digest_init(type, arg, 0);
4279 arg =convert_for_initialization (0, type, arg, LOOKUP_NORMAL,
4280 "defaultargument", fn, parmnum);
4281 }
4282 else
4283 {
4284 /*This could get clobbered by the following call. */
4285 if (TREE_HAS_CONSTRUCTOR(arg))
4286 arg = copy_node (arg);
4287
4288 arg =convert_for_initialization (0, type, arg, LOOKUP_NORMAL,
4289 "defaultargument", fn, parmnum);
4290 arg = convert_for_arg_passing (type, arg);
4291 }
4292
4293 returnarg;
4294 }
节点DEFAULT_ARG是为未解析的缺省实参所构建的。记得在解析类定义的过程中,缺省实参被DEFAULT_ARG所缓存,它将在该解析结束后才解析。因此在这里不应该出现DEFAULT_ARG(如果是这样,这可能是在类定义中缺少了“};”),而且在这一点上,前端不知道如何处理这种节点。
那么在4274传递给下面函数的arg,是由该函数所有调用所共享的缺省实参。不过,正如【3】所定义的,“每次调用该函数都要评估缺省实参”,因此在真正评估这个缺省实参之前,需要准备合适的arg的拷贝(我们不能直接改变arg),并如下所示地更新这个局部临时对象。
1259 tree
1260 break_out_target_exprs (tree t) incp/tree.c
1261 {
1262 staticint target_remap_count;
1263 staticsplay_tree target_remap;
1264
1265 if (!target_remap_count++)
1266 target_remap =splay_tree_new (splay_tree_compare_pointers,
1267 /*splay_tree_delete_key_fn=*/NULL,
1268 /*splay_tree_delete_value_fn=*/NULL);
1269 walk_tree(&t, bot_manip, target_remap, NULL);
1270 walk_tree(&t, bot_replace, target_remap, NULL);
1271
1272 if (!--target_remap_count)
1273 {
1274 splay_tree_delete(target_remap);
1275 target_remap = NULL;
1276 }
1277
1278 returnt;
1279 }
上面,walk_tree遍历以t为根的子树,并在以t的编码所选出的节点上执行给定的函数。在第一次遍历中,使用下面的函数。注意该函数总是返回NULL(下面的copy_tree_r返回NULL)来强制walk_tree执行深度优先的完整遍历(即,tp可能是一个tree_list,其中的节点可以包含操作数,它们依次亦可能是tree_list,等等,访问将从底部开始向上);不过是否进入子树(即操作数)由局部变量walk_subtrees来控制(它在下面的函数中作为实参walk_subtrees传入)。在使用指定的函数处理树节点前,walk_subtrees被设置为1;是指定的函数来决定该节点是否是感兴趣的,并且需要进入。
1182 static tree
1183 bot_manip (tree* tp, int* walk_subtrees, void* data) in cp/tree.c
1184 {
1185 splay_tree target_remap =((splay_tree) data);
1186 tree t = *tp;
1187
1188 if (TREE_CONSTANT (t))
1189 {
1190 /*There can't be any TARGET_EXPRs or their slot variables below
1191 thispoint. We used to check !TREE_SIDE_EFFECTS, but then we
1192 failed to copy an ADDR_EXPR of the slotVAR_DECL. */
1193 *walk_subtrees = 0;
1194 returnNULL_TREE;
1195 }
1196 if (TREE_CODE (t) ==TARGET_EXPR)
1197 {
1198 tree u;
1199
1200 if (TREE_CODE(TREE_OPERAND (t, 1)) == AGGR_INIT_EXPR)
1201 {
1202 mark_used(TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 1), 0), 0));
1203 u = build_cplus_new
1204 (TREE_TYPE (t),break_out_target_exprs (TREE_OPERAND (t, 1)));
1205 }
1206 else
1207 {
1208 u = build_target_expr_with_type
1209 (break_out_target_exprs (TREE_OPERAND (t,1)), TREE_TYPE (t));
1210 }
1211
1212 /* Map the old variable to the new one. */
1213 splay_tree_insert(target_remap,
1214 (splay_tree_key)TREE_OPERAND (t, 0),
1215 (splay_tree_value)TREE_OPERAND (u, 0));
1216
1217 /*Replace the old expression with the new version. */
1218 *tp = u;
1219 /*We don't have to go below this point; the recursive call to
1220 break_out_target_exprs will have handledanything below this
1221 point. */
1222 *walk_subtrees = 0;
1223 returnNULL_TREE;
1224 }
1225 else if (TREE_CODE (t) ==CALL_EXPR)
1226 mark_used(TREE_OPERAND (TREE_OPERAND (t, 0), 0));
1227
1228 /*Make a copy of this node. */
1229 returncopy_tree_r (tp, walk_subtrees, NULL);
1230 }
对于常量或TRAGET_EXPR以外的节点,copy_tree_r拷贝这个节点,如果它是*_CST (看到它其实被上面的TREE_CONSTANT滤掉了),或表达式,或TREE_LIST,或TREE_VEC,或OVERLOAD(由下面的C++的钩子tree_chain_matters_p来辨别,并注意到在这里walk_subtrees没有改变,walk_tree将进入该节点的子节点,并继续拷贝其结构)。
1966 tree
1967 copy_tree_r (tree *tp, int*walk_subtrees, void *data ATTRIBUTE_UNUSED) in tree-inline.c
1968 {
1969 enumtree_code code = TREE_CODE (*tp);
1970
1971 /* We make copies of most nodes. */
1972 if (IS_EXPR_CODE_CLASS(TREE_CODE_CLASS (code))
1973 || TREE_CODE_CLASS(code) == 'c'
1974 || code == TREE_LIST
1975 || code == TREE_VEC
1976 || (*lang_hooks.tree_inlining.tree_chain_matters_p)(*tp))
1977 {
1978 /* Because the chain gets clobbered when wemake a copy, we save it
1979 here. */
1980 tree chain = TREE_CHAIN(*tp);
1981
1982 /*Copy the node. */
1983 *tp = copy_node (*tp);
1984
1985 /*Now, restore the chain, if appropriate. That will cause
1986 walk_tree to walk into the chain aswell. */
1987 if (code == PARM_DECL ||code == TREE_LIST
1988 #ifndef INLINER_FOR_JAVA
1989 || (*lang_hooks.tree_inlining.tree_chain_matters_p)(*tp)
1990 || STATEMENT_CODE_P (code))
1991 TREE_CHAIN (*tp) = chain;
1992
1993 /* For now, we don't update BLOCKs when wemake copies. So, we
1994 haveto nullify all scope-statements. */
1995 if (TREE_CODE (*tp) ==SCOPE_STMT)
1996 SCOPE_STMT_BLOCK (*tp) =NULL_TREE;
1997 #else/* INLINER_FOR_JAVA */
1998 || (*lang_hooks.tree_inlining.tree_chain_matters_p)(*tp))
1999 TREE_CHAIN (*tp) =chain;
2000 #endif /*INLINER_FOR_JAVA */
2001 }
2002 else if (TREE_CODE_CLASS(code) == 't')
2003 *walk_subtrees = 0;
2004
2005 returnNULL_TREE;
2006 }
一个TARGET_EXPR代表一个临时对象。其第一个操作数是表示这个临时对象的一个VAR_DECL。其第二个操作数是该临时对象的初始值。该初始值被评估,然后拷贝入这个临时对象。
一个AGGR_INIT_EXPR代表,作为一个函数调用返回值,或一个构造函数结果的初始化。一个AGGR_INIT_EXPR将仅出现作为一个TARGET_EXPR的第二个操作数。该AGGR_INIT_EXPR的第一个操作数是所调用函数的地址,就像在一个CALL_EXPR那样。第二个操作数是以一个TREE_LIST形式传递给该函数的实参,同样类似于在一个CALL_EXPR里那样。这个表达式的值由这个函数返回。
那么如果AGGR_INIT_EXPR被用于TRAGET_EXPR里,递归break_out_target_exprs来拷贝这个节点。对于这个拷贝过来的表达式,build_cplus_new为其初始化产生了代码。
2007 tree
2008 build_cplus_new (tree type, tree init) incp/tree.c
2009 {
2010 tree fn;
2011 tree slot;
2012 tree rval;
2013 int is_ctor;
2014
2015 /*Make sure that we're not trying to create an instance of an
2016 abstract class. */
2017 abstract_virtuals_error (NULL_TREE, type);
2018
2019 if (TREE_CODE (init) != CALL_EXPR &&TREE_CODE (init) != AGGR_INIT_EXPR)
2020 returnconvert (type, init);
2021
2022 fn = TREE_OPERAND (init, 0);
2023 is_ctor = (TREE_CODE (fn) ==ADDR_EXPR
2024 &&TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
2025 && DECL_CONSTRUCTOR_P(TREE_OPERAND (fn, 0)));
2026
2027 slot = build_local_temp (type);
2028
2029 /*We split the CALL_EXPR into its function and its arguments here.
2030 Then,in expand_expr, we put them back together. The reason for
2031 thisis that this expression might be a default argument
2032 expression. In that case, we need a newtemporary every time the
2033 expression is used. That's whatbreak_out_target_exprs does; it
2034 replaces every AGGR_INIT_EXPR with a copythat uses a fresh
2035 temporary slot. Then, expand_expr builds upa call-expression
2036 usingthe new slot. */
2037
2038 /*If we don't need to use a constructor to create an object of this
2039 type,don't mess with AGGR_INIT_EXPR. */
2040 if (is_ctor ||TREE_ADDRESSABLE (type))
2041 {
2042 rval = build(AGGR_INIT_EXPR, type, fn, TREE_OPERAND (init, 1), slot);
2043 TREE_SIDE_EFFECTS (rval) =1;
2044 AGGR_INIT_VIA_CTOR_P(rval) = is_ctor;
2045 }
2046 else
2047 rval = init;
2048
2049 rval = build_target_expr (slot, rval);
2050
2051 returnrval;
2052 }
现在需要更新在TARGET_EXPR中的临时对象,因为我们不是在产生原始的TARGET_EXPR的上下文中。看到这个临时对象成为了局部的,其DECL_CONTEXT被强制设置为current_function_decl。
253 static tree
254 build_local_temp (tree type) incp/tree.c
255 {
256 tree slot = build_decl(VAR_DECL, NULL_TREE, type);
257 DECL_ARTIFICIAL (slot) = 1;
258 DECL_CONTEXT (slot) = current_function_decl;
259 layout_decl (slot,0);
260 return slot;
261 }
注意如果对于这个AGGR_INIT_EXPR,AGGR_INIT_VIA_CTOR_P成立,表示这个初始化是通过一个构造函数调用实现。在2042行所构建的AGGR_INIT_EXPR,其第三个操作数是这个临时对象,它总是一个VAR_DECL。而init是原来TARGET_EXPR中的AGGR_INIT_EXPR(这个AGGR_INIT_EXPR的第三个操作数的地址被提取,并替换实参列表中的值。这个情况下,该表达式的值是提供给这个AGGR_INIT_EXPR的VAR_DECL;构造函数不返回值)。
最后,这个新构建的TARGET_EXPR被build_cplus_new返回。
而对于TARGET_EXPR的第二个操作数不是AGGR_INIT_EXPR的情况,该操作数由build_target_expr_with_type来处理。这里如果init是TARGET_EXPR,它必定是由在bot_manip中1209行的break_out_target_exprs所构建的,这个节点正是我们期望的。
320 tree
321 build_target_expr_with_type (tree init,tree type) incp/tree.c
322 {
323 tree slot;
324
325 if (TREE_CODE (init) == TARGET_EXPR)
326 returninit;
327 else if (CLASS_TYPE_P (type) &&!TYPE_HAS_TRIVIAL_INIT_REF (type)
328 && TREE_CODE (init) !=COND_EXPR
329 && TREE_CODE (init) !=CONSTRUCTOR
330 && TREE_CODE (init) !=VA_ARG_EXPR)
331 /* We need tobuild up a copy constructor call. COND_EXPR is a special
332 case because wealready have copies on the arms and we don't want
333 another onehere. A CONSTRUCTOR is aggregate initialization, which
334 is handledseparately. A VA_ARG_EXPR is magic creation of an
335 aggregate; there's no additional work to bedone. */
336 return force_rvalue (init);
337
338 slot = build_local_temp(type);
339 return build_target_expr (slot, init);
340 }
上面,如果TYPE_HAS_TRIVIAL_INIT_REF不是0,表示该拷贝初始化可以使用按位拷贝。对于这样的情形,可以简单地构建TARGET_EXPR;否则,需要执行一个左值到右值的转换,包括如下所示的拷贝构造函数调用。
590 tree
591 force_rvalue (tree expr) incvt.c
592 {
593 if (IS_AGGR_TYPE (TREE_TYPE(expr)) && TREE_CODE (expr) != TARGET_EXPR)
594 expr = ocp_convert (TREE_TYPE (expr), expr,
595 CONV_IMPLICIT|CONV_FORCE_TEMP, LOOKUP_NORMAL);
596 else
597 expr = decay_conversion(expr);
598
599 return expr;
600 }
不久之后我们将看到ocp_convert的细节。在这里总而言之,该函数将产生调用合适的拷贝构造函数代码,然后调用build_cplus_new来产生构建这个临时对象及其初始化。在离开bot_manip之前,看一下build_target_expr。
234 static tree
235 build_target_expr (tree decl, treevalue) incp/tree.c
236 {
237 tree t;
238
239 t = build (TARGET_EXPR,TREE_TYPE (decl), decl, value,
240 cxx_maybe_build_cleanup(decl), NULL_TREE);
241 /* We always setTREE_SIDE_EFFECTS so that expand_expr does not
242 ignore theTARGET_EXPR. If there really turn out to be no
243 side-effects,then the optimizer should be able to get rid of
244 whatever code isgenerated anyhow. */
245 TREE_SIDE_EFFECTS (t) = 1;
246
247 return t;
248 }
对于具有非平凡析构函数的临时对象,编译器需要产生代码,在其越出其作用域时,通过调用这个析构函数摧毁这个临时对象。因此在240行,cxx_maybe_build_cleanup产生这些代码,如果需要的话。
现在在bot_manip的1213行,u是对应于t的TARGET_EXPR。它把新旧版本的临时对象映射起来。然后,我们立即用新版本取代了旧版本。不过,在由break_out_target_exprs处理的节点的某些子节点中,可能仍然保留了这个旧版本的引用,它们需要如下的更新。
1236 static tree
1237 bot_replace (tree* t, incp/tree.c
1238 int* walk_subtreesATTRIBUTE_UNUSED ,
1239 void* data)
1240 {
1241 splay_tree target_remap =((splay_tree) data);
1242
1243 if (TREE_CODE (*t) ==VAR_DECL)
1244 {
1245 splay_tree_node n =splay_tree_lookup (target_remap,
1246 (splay_tree_key)*t);
1247 if (n)
1248 *t = (tree) n->value;
1249 }
1250
1251 returnNULL_TREE;
1252 }
回到convert_default_arg,在4274行从break_out_target_exprs得到这个更新的arg,然后接下来的函数用于产生初始化代码。
5.12.5.2.2.2.3. 省略实参
最后一个的可能就是省略实参,注意到省略实参与缺省实参不可共存。在前端中,为了识别包含了省略实参的函数声明,形参列表由NULL,而不是void_list_node来结尾。
4161 tree
4162 convert_arg_to_ellipsis (tree arg) incall.c
4163 {
4164 /*[expr.call]
4165
4166 Thelvalue-to-rvalue, array-to-pointer, and function-to-pointer
4167 standard conversions are performed. */
4168 arg = decay_conversion (arg);
4169 /* [expr.call]
4170
4171 If theargument has integral or enumeration type that is subject
4172 to theintegral promotions (_conv.prom_), or a floating point
4173 typethat is subject to the floating point promotion
4174 (_conv.fpprom_), the value of the argumentis converted to the
4175 promoted type before the call. */
4176 if (TREE_CODE (TREE_TYPE(arg)) == REAL_TYPE
4177 &&(TYPE_PRECISION (TREE_TYPE (arg))
4178 < TYPE_PRECISION(double_type_node)))
4179 arg = convert_to_real(double_type_node, arg);
4180 else if (INTEGRAL_OR_ENUMERATION_TYPE_P(TREE_TYPE (arg)))
4181 arg =perform_integral_promotions (arg);
4182
4183 arg = require_complete_type(arg);
4184
4185 if (arg != error_mark_node
4186 && !pod_type_p(TREE_TYPE (arg)))
4187 {
4188 /*Undefined behavior [expr.call] 5.2.2/7. We usedto just warn
4189 hereand do a bitwise copy, but now cp_expr_size will abort if we
4190 tryto do that.
4191 Ifthe call appears in the context of a sizeof expression,
4192 there is no need to emit a warning, sincethe expression won't be
4193 evaluated. We keep the builtin_trap justas a safety check. */
4194 if (!skip_evaluation)
4195 warning ("cannotpass objects of non-POD type `%#T' through `...'; "
4196 "call willabort at runtime", TREE_TYPE (arg));
4197 arg = call_builtin_trap ();
4198 arg = build(COMPOUND_EXPR, integer_type_node, arg,
4199 integer_zero_node);
4200 }
4201
4202 returnarg;
4203 }
【3】的条文5.2.2“函数调用”,条款7如下定义了编译器在省略实参上的行为。
7. 当一个给定的实参没有对应的形参,该实参被以这样的方式传入——接受函数可以通过调用va_arg(18.7)来得到该实参值。左值到右值(4.1),数组到指针(4.2),及函数到指针(4.3)标准转换在该实参表达式上执行。在这些转换之后,如果该实参不是数字,枚举值,指针,成员的指针,或类类型,该程序非法。如果该实参具有一个non-POD类类型(条文9),其行为是未定义的。如果该实参具有适用于整型提升(4.5)的整型或枚举类型,或适用于浮点提升(4.6)的浮点类型,在该调用之前,该实参的值被转换到提升后的类型。这些提升被称为缺省实参提升。
函数decay_conversion执行在exp中的转换,它应用在当一个左值出现在一个右值上下文中时候。包括左值到右值,数组到指针,及函数到指针的转换。
1335 tree
1336 decay_conversion (tree exp) ntypeck.c
1337 {
1338 tree type;
1339 enumtree_code code;
1340
1341 type = TREE_TYPE (exp);
1342 code = TREE_CODE (type);
1343
1344 if (code == REFERENCE_TYPE)
1345 {
1346 exp = convert_from_reference (exp);
1347 type = TREE_TYPE (exp);
1348 code = TREE_CODE (type);
1349 }
1350
1351 if (type == error_mark_node)
1352 returnerror_mark_node;
1353
1354 if (type_unknown_p (exp))
1355 {
1356 cxx_incomplete_type_error(exp, TREE_TYPE (exp));
1357 returnerror_mark_node;
1358 }
1359
1360 /*Constants can be used directly unless they're not loadable. */
1361 if (TREE_CODE (exp) ==CONST_DECL)
1362 exp = DECL_INITIAL (exp);
1363 /*Replace a nonvolatile const static variable with its value. We
1364 don'tdo this for arrays, though; we want the address of the
1365 firstelement of the array, not the address of the first element
1366 of itsinitializing constant. */
1367 else if (code != ARRAY_TYPE)
1368 {
1369 exp = decl_constant_value(exp);
1370 type = TREE_TYPE (exp);
1371 }
1372
1373 /* build_c_cast puts on a NOP_EXPR to make theresult not an lvalue.
1374 Leavesuch NOP_EXPRs, since RHS is being used in non-lvalue context. */
1375
1376 if (code == VOID_TYPE)
1377 {
1378 error ("void valuenot ignored as it ought to be");
1379 returnerror_mark_node;
1380 }
1381 if(invalid_nonstatic_memfn_p (exp))
1382 returnerror_mark_node;
1383 if (code == FUNCTION_TYPE ||is_overloaded_fn (exp))
1384 returnbuild_unary_op (ADDR_EXPR, exp, 0);
1385 if (code == ARRAY_TYPE)
1386 {
1387 tree adr;
1388 tree ptrtype;
1389
1390 if (TREE_CODE (exp) ==INDIRECT_REF)
1391 returnbuild_nop (build_pointer_type (TREE_TYPE(type)),
1392 TREE_OPERAND (exp, 0));
1393
1394 if (TREE_CODE (exp) ==COMPOUND_EXPR)
1395 {
1396 tree op1 = decay_conversion (TREE_OPERAND (exp, 1));
1397 returnbuild (COMPOUND_EXPR, TREE_TYPE (op1),
1398 TREE_OPERAND(exp, 0), op1);
1399 }
1400
1401 if (!lvalue_p (exp)
1402 && ! (TREE_CODE(exp) == CONSTRUCTOR && TREE_STATIC (exp)))
1403 {
1404 error ("invalid useof non-lvalue array");
1405 returnerror_mark_node;
1406 }
1407
1408 ptrtype = build_pointer_type (TREE_TYPE (type));
1409
1410 if (TREE_CODE (exp) ==VAR_DECL)
1411 {
1412 if(!cxx_mark_addressable (exp))
1413 return error_mark_node;
1414 adr = build_nop(ptrtype, build_address (exp));
1415 TREE_SIDE_EFFECTS (adr)= 0; /*Default would be, same as EXP. */
1416 returnadr;
1417 }
1418 /*This way is better for a COMPONENT_REF since it can
1419 simplify the offset for a component. */
1420 adr = build_unary_op(ADDR_EXPR, exp, 1);
1421 returncp_convert (ptrtype, adr);
1422 }
1423
1424 /*[basic.lval]: Class rvalues can have cv-qualified types; non-class
1425 rvalues always have cv-unqualifiedtypes. */
1426 if (! CLASS_TYPE_P (type))
1427 exp = cp_convert(TYPE_MAIN_VARIANT (type), exp);
1428
1429 returnexp;
1430 }
首先对于REFERENCE_TYPE,它是一个左值,要把它转换为右值,就应该使用被引用的值,而不再保存引用。在前端中,INDIRECT_REF为这个目标而构建(这是因为引用总是传入其地址就像指针那样;而在编译器中,引用的模式(mode)与指针的相同,都是ptr_mode,参见build_reference_type)。
566 tree
567 convert_from_reference (tree val) in cvt.c
568 {
569 if (TREE_CODE (TREE_TYPE (val)) ==REFERENCE_TYPE)
570 return build_indirect_ref (val, NULL);
571 return val;
572 }
记得指针是右值。当处理ARRAY_TYPE时,如果我们正在使用形如:int *a[i](a是维度大于1的数组,例如,intA[2][2])的表达式,满足1390行的条件, 因此构建了形如:int**的类型,并且看到转换指针“int*a[i]”到“int**”不需要产生任何代码,因而为这个转换构建了NOP_EXPR。
而如果exp只是一个数组的声明,例如:int a[8],这个声明的右值是“int*”。可以直接构建这个右值。不过,对于其他的情形,例如:“tempA.a”(这是一棵以SCOPE_REF为根节点的树),就没有可以直接使用的简单规则,因此调用build_unary_op及cp_convert来执行合适的转换。
正如在convert_arg_to_ellipsis的4188行的注释所提到的,对于对应于non-POD类类型的未定义行为,GCC以前使用按位拷贝,不过在当前版本中,这个行为在后面对cp_expr_size的调用将导致异常终止。这个终止由下面“__builtin_trap”的调用触发,在4198行它成为了COMPOUND_EXPR形式的arg的一部分。
4148 c tree
4149 call_builtin_trap (void) incall.c
4150 {
4151 tree fn = IDENTIFIER_GLOBAL_VALUE(get_identifier ("__builtin_trap"));
4152
4153 my_friendly_assert (fn !=NULL, 20030927);
4154 fn = build_call (fn,NULL_TREE);
4155 returnfn;
4156 }
内建陷阱的行为是:如果目标机器定义了陷阱指令,就使用它;否则编译器将调用abort() (参考expand_builtin_trap)。
下面是对non-POD类类型省略实参的一个有趣的测试:
#include <stdarg.h>
class A {
public: virtual void func() {}
};
int func (inta, ...) {
va_list ap;
va_start(ap, a);
va_arg(ap, A);
va_end(ap);
return 1;
}
int main() {
A a;
func (1, a); // sizeof (func (1, a))
}
编译器将给出以下警告:
test2.cpp: In function ‘int func(int, ...)’:
test2.cpp:11: warning: cannot receive objects ofnon-POD type ‘class A’ through ‘...’; call will abort at runtime
test2.cpp: In function ‘int main()’:
test2.cpp:19: warning: cannot pass objects of non-PODtype ‘class A’ through ’...’; call will abort at runtime
当执行这个程序时,得到错误:Illegal instruction.
不过,如果我们使用注释中的语句,编译器将给出警告:
test2.cpp: In function ‘int func(int, ...)’:
test2.cpp:11: warning: cannot receive objects ofnon-POD type ‘class A’ through ‘...’; call will abort at runtime
而在执行时,没有产生错误,因为func(1, a)没有被评估,除了其返回值。
- GCC-3.4.6源代码学习笔记(152)
- GCC-3.4.6源代码学习笔记 (100)
- GCC-3.4.6源代码学习笔记 (101)
- GCC-3.4.6源代码学习笔记 (102)
- GCC-3.4.6源代码学习笔记 (103)
- GCC-3.4.6源代码学习笔记 (104)
- GCC-3.4.6源代码学习笔记 (105)
- GCC-3.4.6源代码学习笔记 (106)
- GCC-3.4.6源代码学习笔记(166)
- GCC-3.4.6源代码学习笔记
- GCC-3.4.6源代码学习笔记(6)
- GCC-3.4.6源代码学习笔记(1)
- GCC-3.4.6源代码学习笔记(2)
- GCC-3.4.6源代码学习笔记(3)
- GCC-3.4.6源代码学习笔记(4)
- GCC-3.4.6源代码学习笔记(5)
- GCC-3.4.6源代码学习笔记(7)
- GCC-3.4.6源代码学习笔记(8)
- Studying note of GCC-3.4.6 source (150)
- GCC-3.4.6源代码学习笔记(151)
- Studying note of GCC-3.4.6 source (151)
- 我为什么写博客
- java高手释疑:package 与 import 【转载:java联机版】
- GCC-3.4.6源代码学习笔记(152)
- asp.net获取html内容中的指定标签下的某个属性值
- Studying note of GCC-3.4.6 source (152)
- GCC-3.4.6源代码学习笔记(153)
- Studying note of GCC-3.4.6 source (153)
- 有趣的智力题
- 简单的操作通信
- 现在就开始
- VHD文件的压缩和转化