cprover
Loading...
Searching...
No Matches
value_set_dereference.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Symbolic Execution of ANSI-C
4
5Author: Daniel Kroening, kroening@kroening.com
6
7\*******************************************************************/
8
11
13
14#ifdef DEBUG
15#include <iostream>
16#endif
17
18#include <util/arith_tools.h>
19#include <util/byte_operators.h>
20#include <util/c_types.h>
21#include <util/config.h>
22#include <util/cprover_prefix.h>
23#include <util/expr_iterator.h>
24#include <util/expr_util.h>
25#include <util/format_expr.h>
26#include <util/fresh_symbol.h>
27#include <util/json.h>
28#include <util/message.h>
29#include <util/namespace.h>
30#include <util/pointer_expr.h>
33#include <util/range.h>
34#include <util/simplify_expr.h>
35#include <util/symbol.h>
36
38
39#include <deque>
40
51static bool should_use_local_definition_for(const exprt &expr)
52{
53 bool seen_symbol = false;
54 for(auto it = expr.depth_begin(), itend = expr.depth_end(); it != itend; ++it)
55 {
56 if(it->id() == ID_symbol)
57 {
58 if(seen_symbol)
59 return true;
60 else
61 seen_symbol = true;
62 }
63 }
64
65 return false;
66}
67
69 const exprt &pointer,
70 const std::vector<exprt> &points_to_set,
71 const std::vector<exprt> &retained_values,
72 const exprt &value)
73{
75 json_result["Pointer"] = json_stringt{format_to_string(pointer)};
76 json_result["PointsToSetSize"] =
77 json_numbert{std::to_string(points_to_set.size())};
78
80 for(const auto &object : points_to_set)
81 {
83 }
84
85 json_result["PointsToSet"] = points_to_set_json;
86
87 json_result["RetainedValuesSetSize"] =
88 json_numbert{std::to_string(points_to_set.size())};
89
92 {
95 }
96
97 json_result["RetainedValuesSet"] = retained_values_set_json;
98
99 json_result["Value"] = json_stringt{format_to_string(value)};
100
101 return json_result;
102}
103
107static optionalt<exprt>
109{
110 if(const auto *index_expr = expr_try_dynamic_cast<index_exprt>(expr))
111 {
112 return index_exprt{
113 index_expr->array(),
114 plus_exprt{index_expr->index(),
116 offset_elements, index_expr->index().type())}};
117 }
118 else if(const auto *if_expr = expr_try_dynamic_cast<if_exprt>(expr))
119 {
120 const auto true_case =
122 if(!true_case)
123 return {};
124 const auto false_case =
126 if(!false_case)
127 return {};
128 return if_exprt{if_expr->cond(), *true_case, *false_case};
129 }
130 else if(can_cast_expr<typecast_exprt>(expr))
131 {
132 // the case of a type cast is _not_ handled here, because that would require
133 // doing arithmetic on the offset, and may result in an offset into some
134 // sub-element
135 return {};
136 }
137 else
138 {
139 return {};
140 }
141}
142
144 const exprt &pointer,
146{
148 pointer.type().id() == ID_pointer,
149 "dereference expected pointer type, but got " + pointer.type().pretty());
150
151 // we may get ifs due to recursive calls
152 if(pointer.id()==ID_if)
153 {
154 const if_exprt &if_expr=to_if_expr(pointer);
155 exprt true_case = dereference(if_expr.true_case(), display_points_to_sets);
156 exprt false_case =
158 return if_exprt(if_expr.cond(), true_case, false_case);
159 }
160 else if(pointer.id() == ID_typecast)
161 {
162 const exprt *underlying = &pointer;
163 // Note this isn't quite the same as skip_typecast, which would also skip
164 // things such as int-to-ptr typecasts which we shouldn't ignore
165 while(underlying->id() == ID_typecast &&
166 underlying->type().id() == ID_pointer)
167 {
168 underlying = &to_typecast_expr(*underlying).op();
169 }
170
171 if(underlying->id() == ID_if && underlying->type().id() == ID_pointer)
172 {
173 const auto &if_expr = to_if_expr(*underlying);
174 return if_exprt(
175 if_expr.cond(),
177 typecast_exprt(if_expr.true_case(), pointer.type()),
180 typecast_exprt(if_expr.false_case(), pointer.type()),
182 }
183 }
184 else if(pointer.id() == ID_plus && pointer.operands().size() == 2)
185 {
186 // Try to improve results for *(p + i) where p points to known offsets but
187 // i is non-constant-- if `p` points to known positions in arrays or array-members
188 // of structs then we can add the non-constant expression `i` to the index
189 // instead of using a byte-extract expression.
190
191 exprt pointer_expr = to_plus_expr(pointer).op0();
192 exprt offset_expr = to_plus_expr(pointer).op1();
193
195 std::swap(pointer_expr, offset_expr);
196
197 if(
198 can_cast_type<pointer_typet>(pointer_expr.type()) &&
201 {
202 exprt derefd_pointer = dereference(pointer_expr);
203 if(
204 auto derefd_with_offset =
206 return *derefd_with_offset;
207
208 // If any of this fails, fall through to use the normal byte-extract path.
209 }
210 }
211
213}
214
216 const exprt &pointer,
218{ // type of the object
219 const typet &type = to_pointer_type(pointer.type()).base_type();
220
221 // collect objects the pointer may point to
222 const std::vector<exprt> points_to_set =
224
225 // get the values of these
226 const std::vector<exprt> retained_values =
227 make_range(points_to_set).filter([&](const exprt &value) {
229 });
230
232
233 if(retained_values.size() >= 2 && should_use_local_definition_for(pointer))
234 {
236 pointer.type(),
237 "derefd_pointer",
238 "derefd_pointer",
242
244 }
245
246 auto values =
248 .map([&](const exprt &value) {
250 })
251 .collect<std::deque<valuet>>();
252
253 const bool may_fail =
254 values.empty() ||
255 std::any_of(values.begin(), values.end(), [](const valuet &value) {
256 return value.value.is_nil();
257 });
258
259 if(may_fail)
260 {
261 values.push_front(get_failure_value(pointer, type));
262 }
263
264 // now build big case split, but we only do "good" objects
265
266 exprt result_value = nil_exprt{};
267
268 for(const auto &value : values)
269 {
270 if(value.value.is_not_nil())
271 {
272 if(result_value.is_nil()) // first?
273 result_value = value.value;
274 else
275 result_value = if_exprt(value.pointer_guard, value.value, result_value);
276 }
277 }
278
279 if(compare_against_pointer != pointer)
280 result_value =
281 let_exprt(to_symbol_expr(compare_against_pointer), pointer, result_value);
282
284 {
286 pointer, points_to_set, retained_values, result_value);
287 }
288
289 return result_value;
290}
291
293 const exprt &pointer,
294 const typet &type)
295{
296 // first see if we have a "failed object" for this pointer
298
299 if(
300 const symbolt *failed_symbol =
302 {
303 // yes!
304 failure_value = failed_symbol->symbol_expr();
306 }
307 else
308 {
309 // else: produce new symbol
311 type,
312 "symex",
313 "invalid_object",
314 pointer.source_location(),
317
318 // make it a lvalue, so we can assign to it
319 symbol.is_lvalue = true;
320
321 failure_value = symbol.symbol_expr();
323 }
324
325 valuet result{};
326 result.value = failure_value;
327 result.pointer_guard = true_exprt();
328 return result;
329}
330
340 const typet &object_type,
341 const typet &dereference_type,
342 const namespacet &ns)
343{
344 const typet *object_unwrapped = &object_type;
346 while(object_unwrapped->id() == ID_pointer &&
348 {
349 object_unwrapped = &to_pointer_type(*object_unwrapped).base_type();
351 &to_pointer_type(*dereference_unwrapped).base_type();
352 }
354 {
355 return true;
356 }
357 else if(dereference_unwrapped->id() == ID_pointer &&
359 {
360#ifdef DEBUG
361 std::cout << "value_set_dereference: the dereference type has "
362 "too many ID_pointer levels"
363 << std::endl;
364 std::cout << " object_type: " << object_type.pretty() << std::endl;
365 std::cout << " dereference_type: " << dereference_type.pretty()
366 << std::endl;
367#endif
368 }
369
370 if(object_type == dereference_type)
371 return true; // ok, they just match
372
373 // check for struct prefixes
374
375 const typet ot_base=ns.follow(object_type),
377
378 if(ot_base.id()==ID_struct &&
379 dt_base.id()==ID_struct)
380 {
381 if(to_struct_type(dt_base).is_prefix_of(
383 return true; // ok, dt is a prefix of ot
384 }
385
386 // we are generous about code pointers
387 if(dereference_type.id()==ID_code &&
388 object_type.id()==ID_code)
389 return true;
390
391 // bitvectors of same width are ok
392 if((dereference_type.id()==ID_signedbv ||
394 (object_type.id()==ID_signedbv ||
395 object_type.id()==ID_unsignedbv) &&
397 to_bitvector_type(object_type).get_width())
398 return true;
399
400 // really different
401
402 return false;
403}
404
419 const exprt &what,
420 bool exclude_null_derefs,
421 const irep_idt &language_mode)
422{
423 if(what.id() == ID_unknown || what.id() == ID_invalid)
424 {
425 return false;
426 }
427
429
430 const exprt &root_object = o.root_object();
431 if(root_object.id() == ID_null_object)
432 {
433 return exclude_null_derefs;
434 }
435 else if(root_object.id() == ID_integer_address)
436 {
437 return language_mode == ID_java;
438 }
439
440 return false;
441}
442
456 const exprt &what,
457 const exprt &pointer_expr,
458 const namespacet &ns)
459{
463
464 if(what.id()==ID_unknown ||
465 what.id()==ID_invalid)
466 {
467 return valuet();
468 }
469
471 what.id() == ID_object_descriptor,
472 "unknown points-to: " + what.id_string());
473
475
476 const exprt &root_object=o.root_object();
477 const exprt &object=o.object();
478
479 #if 0
480 std::cout << "O: " << format(root_object) << '\n';
481 #endif
482
483 if(root_object.id() == ID_null_object)
484 {
485 if(!o.offset().is_zero())
486 return {};
487
488 valuet result;
490 return result;
491 }
492 else if(root_object.id()==ID_dynamic_object)
493 {
494 valuet result;
495
496 // constraint that it actually is a dynamic object
497 // this is also our guard
498 result.pointer_guard = is_dynamic_object_exprt(pointer_expr);
499
500 // can't remove here, turn into *p
501 result.value = dereference_exprt{pointer_expr};
502 result.pointer = pointer_expr;
503
504 return result;
505 }
506 else if(root_object.id()==ID_integer_address)
507 {
508 // This is stuff like *((char *)5).
509 // This is turned into an access to __CPROVER_memory[...].
510
511 const symbolt &memory_symbol=ns.lookup(CPROVER_PREFIX "memory");
512 const symbol_exprt symbol_expr(memory_symbol.name, memory_symbol.type);
513
514 if(to_array_type(memory_symbol.type).element_type() == dereference_type)
515 {
516 // Types match already, what a coincidence!
517 // We can use an index expression.
518
520 symbol_expr,
522 pointer_offset(pointer_expr),
523 to_array_type(memory_symbol.type).index_type()),
524 to_array_type(memory_symbol.type).element_type());
525
526 valuet result;
527 result.value=index_expr;
529 return result;
530 }
532 to_array_type(memory_symbol.type).element_type(),
534 ns))
535 {
537 symbol_expr,
539 pointer_offset(pointer_expr),
540 to_array_type(memory_symbol.type).index_type()),
541 to_array_type(memory_symbol.type).element_type());
542
543 valuet result;
545 result.pointer =
547 return result;
548 }
549 else
550 {
551 // We need to use byte_extract.
552 // Won't do this without a commitment to an endianness.
553
555 return {};
556
557 valuet result;
558 result.value = make_byte_extract(
559 symbol_expr, pointer_offset(pointer_expr), dereference_type);
560 result.pointer = address_of_exprt{result.value};
561 return result;
562 }
563 }
564 else
565 {
566 valuet result;
567
568 // something generic -- really has to be a symbol
570
571 if(o.offset().is_zero())
572 {
573 result.pointer_guard = equal_exprt(
576 }
577 else
578 {
579 result.pointer_guard=same_object(pointer_expr, object_pointer);
580 }
581
582 const typet &object_type = object.type();
583 const typet &root_object_type = root_object.type();
584
585 if(
587 o.offset().is_zero())
588 {
589 // The simplest case: types match, and offset is zero!
590 // This is great, we are almost done.
591
593 result.pointer =
595
596 return result;
597 }
598
599 // this is relative to the root object
600 exprt offset;
601 if(o.offset().is_constant())
602 offset = o.offset();
603 else
604 offset = simplify_expr(pointer_offset(pointer_expr), ns);
605
606 if(
607 root_object_type.id() == ID_array &&
612 offset.is_constant())
613 {
614 // We have an array with a subtype that matches
615 // the dereferencing type.
616
617 // are we doing a byte?
618 auto element_size =
620 CHECK_RETURN(element_size.has_value());
622
623 const auto offset_int =
625
626 if(offset_int % *element_size == 0)
627 {
629 root_object,
633 result.value =
637
638 return result;
639 }
640 }
641
642 // try to build a member/index expression - do not use byte_extract
644 root_object, o.offset(), dereference_type, ns);
645 if(subexpr.has_value())
646 simplify(subexpr.value(), ns);
647 if(
648 subexpr.has_value() &&
649 subexpr.value().id() != ID_byte_extract_little_endian &&
650 subexpr.value().id() != ID_byte_extract_big_endian)
651 {
652 // Successfully found a member, array index, or combination thereof
653 // that matches the desired type and offset:
654 result.value = subexpr.value();
657 return result;
658 }
659
660 // we extract something from the root object
661 result.value = o.root_object();
664
665 if(memory_model(result.value, dereference_type, offset, ns))
666 {
667 // set pointer correctly
671 result.pointer,
673 offset),
675 }
676 else
677 {
678 return {}; // give up, no way that this is ok
679 }
680
681 return result;
682 }
683}
684
685static bool is_a_bv_type(const typet &type)
686{
687 return type.id()==ID_unsignedbv ||
688 type.id()==ID_signedbv ||
689 type.id()==ID_bv ||
690 type.id()==ID_fixedbv ||
691 type.id()==ID_floatbv ||
692 type.id()==ID_c_enum_tag;
693}
694
704 exprt &value,
705 const typet &to_type,
706 const exprt &offset,
707 const namespacet &ns)
708{
709 // we will allow more or less arbitrary pointer type cast
710
711 const typet from_type=value.type();
712
713 // first, check if it's really just a type coercion,
714 // i.e., both have exactly the same (constant) size,
715 // for bit-vector types or pointers
716
717 if(
718 (is_a_bv_type(from_type) && is_a_bv_type(to_type)) ||
719 (from_type.id() == ID_pointer && to_type.id() == ID_pointer))
720 {
722 {
723 // avoid semantic conversion in case of
724 // cast to float or fixed-point,
725 // or cast from float or fixed-point
726
727 if(
728 to_type.id() != ID_fixedbv && to_type.id() != ID_floatbv &&
729 from_type.id() != ID_fixedbv && from_type.id() != ID_floatbv)
730 {
731 value = typecast_exprt::conditional_cast(value, to_type);
732 return true;
733 }
734 }
735 }
736
737 // otherwise, we will stitch it together from bytes
738
739 return memory_model_bytes(value, to_type, offset, ns);
740}
741
751 exprt &value,
752 const typet &to_type,
753 const exprt &offset,
754 const namespacet &ns)
755{
756 const typet from_type=value.type();
757
758 // We simply refuse to convert to/from code.
759 if(from_type.id()==ID_code || to_type.id()==ID_code)
760 return false;
761
762 // We won't do this without a commitment to an endianness.
764 return false;
765
766 // But everything else we will try!
767 // We just rely on byte_extract to do the job!
768
769 exprt result;
770
771 // See if we have an array of bytes already,
772 // and we want something byte-sized.
774 from_type.id() == ID_array
777
778 auto to_type_size = pointer_offset_size(to_type, ns);
779
780 if(
781 from_type.id() == ID_array && from_type_element_type_size.has_value() &&
782 *from_type_element_type_size == 1 && to_type_size.has_value() &&
783 *to_type_size == 1 &&
784 is_a_bv_type(to_array_type(from_type).element_type()) &&
785 is_a_bv_type(to_type))
786 {
787 // yes, can use 'index', but possibly need to convert
790 value,
793 to_array_type(from_type).element_type()),
794 to_type);
795 }
796 else
797 {
798 // no, use 'byte_extract'
799 result = make_byte_extract(value, offset, to_type);
800 }
801
802 value=result;
803
804 return true;
805}
configt config
Definition config.cpp:25
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
const bitvector_typet & to_bitvector_type(const typet &type)
Cast a typet to a bitvector_typet.
byte_extract_exprt make_byte_extract(const exprt &_op, const exprt &_offset, const typet &_type)
Construct a byte_extract_exprt with endianness and byte width matching the current configuration.
Expression classes for byte-level operators.
bitvector_typet index_type()
Definition c_types.cpp:22
pointer_typet pointer_type(const typet &subtype)
Definition c_types.cpp:240
bitvector_typet char_type()
Definition c_types.cpp:111
Operator to return the address of an object.
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:563
std::size_t get_width() const
Definition std_types.h:876
struct configt::ansi_ct ansi_c
virtual const symbolt * get_or_create_failed_symbol(const exprt &expr)=0
virtual std::vector< exprt > get_value_set(const exprt &expr) const =0
Operator to dereference a pointer.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:39
Equality.
Definition std_expr.h:1306
Base class for all expressions.
Definition expr.h:56
depth_iteratort depth_end()
Definition expr.cpp:249
depth_iteratort depth_begin()
Definition expr.cpp:247
bool is_zero() const
Return whether the expression is a constant representing 0.
Definition expr.cpp:47
bool is_constant() const
Return whether the expression is a constant.
Definition expr.h:204
typet & type()
Return the type of the expression.
Definition expr.h:84
operandst & operands()
Definition expr.h:94
const source_locationt & source_location() const
Definition expr.h:223
The trinary if-then-else operator.
Definition std_expr.h:2323
Array index operator.
Definition std_expr.h:1410
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition irep.cpp:490
bool is_not_nil() const
Definition irep.h:380
const std::string & id_string() const
Definition irep.h:399
const irep_idt & id() const
Definition irep.h:396
bool is_nil() const
Definition irep.h:376
Evaluates to true if the operand is a pointer to a dynamic object.
A let expression.
Definition std_expr.h:3149
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
The NIL expression.
Definition std_expr.h:3026
The null pointer constant.
Split an expression into a base object and a (byte) offset.
static const exprt & root_object(const exprt &expr)
The plus expression Associativity is not specified.
Definition std_expr.h:947
The pointer type These are both 'bitvector_typet' (they have a width) and 'type_with_subtypet' (they ...
const typet & base_type() const
The type of the data what we point to.
Expression to hold a symbol (variable)
Definition std_expr.h:113
Symbol table entry.
Definition symbol.h:28
class symbol_exprt symbol_expr() const
Produces a symbol_exprt for a symbol.
Definition symbol.cpp:121
bool is_lvalue
Definition symbol.h:72
The Boolean constant true.
Definition std_expr.h:3008
Semantic type conversion.
Definition std_expr.h:2017
static exprt conditional_cast(const exprt &expr, const typet &type)
Definition std_expr.h:2025
The type of an expression, extends irept.
Definition type.h:29
Return value for build_reference_to; see that method for documentation.
static bool memory_model_bytes(exprt &value, const typet &type, const exprt &offset, const namespacet &ns)
Replace value by an expression of type to_type corresponding to the value at memory address value + o...
valuet get_failure_value(const exprt &pointer, const typet &type)
exprt handle_dereference_base_case(const exprt &pointer, bool display_points_to_sets)
const bool exclude_null_derefs
Flag indicating whether value_set_dereferencet::dereference should disregard an apparent attempt to d...
symbol_table_baset & new_symbol_table
static bool dereference_type_compare(const typet &object_type, const typet &dereference_type, const namespacet &ns)
Check if the two types have matching number of ID_pointer levels, with the dereference type eventuall...
dereference_callbackt & dereference_callback
exprt dereference(const exprt &pointer, bool display_points_to_sets=false)
Dereference the given pointer-expression.
const irep_idt language_mode
language_mode: ID_java, ID_C or another language identifier if we know the source language in use,...
static valuet build_reference_to(const exprt &what, const exprt &pointer, const namespacet &ns)
static bool should_ignore_value(const exprt &what, bool exclude_null_derefs, const irep_idt &language_mode)
Determine whether possible alias what should be ignored when replacing a pointer by its referees.
static bool memory_model(exprt &value, const typet &type, const exprt &offset, const namespacet &ns)
Replace value by an expression of type to_type corresponding to the value at memory address value + o...
#define CPROVER_PREFIX
Pointer Dereferencing.
Forward depth-first search iterators These iterators' copy operations are expensive,...
const exprt & skip_typecast(const exprt &expr)
find the expression nested inside typecasts, if any
Deprecated expression utility functions.
static format_containert< T > format(const T &o)
Definition format.h:37
std::string format_to_string(const T &o)
Definition format.h:43
symbolt & get_fresh_aux_symbol(const typet &type, const std::string &name_prefix, const std::string &basename_prefix, const source_locationt &source_location, const irep_idt &symbol_mode, const namespacet &ns, symbol_table_baset &symbol_table)
Installs a fresh-named symbol with respect to the given namespace ns with the requested name pattern ...
Fresh auxiliary symbol creation.
std::string from_type(const namespacet &ns, const irep_idt &identifier, const typet &type)
API to expression classes for Pointers.
const object_descriptor_exprt & to_object_descriptor_expr(const exprt &expr)
Cast an exprt to an object_descriptor_exprt.
const pointer_typet & to_pointer_type(const typet &type)
Cast a typet to a pointer_typet.
optionalt< mp_integer > pointer_offset_size(const typet &type, const namespacet &ns)
Compute the size of a type in bytes, rounding up to full bytes.
optionalt< exprt > get_subexpression_at_offset(const exprt &expr, const mp_integer &offset_bytes, const typet &target_type_raw, const namespacet &ns)
optionalt< mp_integer > pointer_offset_bits(const typet &type, const namespacet &ns)
Pointer Logic.
exprt pointer_offset(const exprt &pointer)
exprt same_object(const exprt &p1, const exprt &p2)
Various predicates over pointers in programs.
Ranges: pair of begin and end iterators, which can be initialized from containers,...
ranget< iteratort > make_range(iteratort begin, iteratort end)
Definition range.h:524
bool simplify(exprt &expr, const namespacet &ns)
exprt simplify_expr(exprt src, const namespacet &ns)
#define PRECONDITION_WITH_DIAGNOSTICS(CONDITION,...)
Definition invariant.h:464
#define CHECK_RETURN(CONDITION)
Definition invariant.h:495
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition std_expr.h:2051
const plus_exprt & to_plus_expr(const exprt &expr)
Cast an exprt to a plus_exprt.
Definition std_expr.h:986
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition std_expr.h:2403
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition std_expr.h:2992
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:222
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition std_types.h:308
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition std_types.h:844
Symbol table entry.
static optionalt< exprt > try_add_offset_to_indices(const exprt &expr, const exprt &offset_elements)
If expr is of the form (c1 ? e1[o1] : c2 ? e2[o2] : c3 ? ...) then return c1 ? e1[o1 + offset] : e2[o...
static bool is_a_bv_type(const typet &type)
static json_objectt value_set_dereference_stats_to_json(const exprt &pointer, const std::vector< exprt > &points_to_set, const std::vector< exprt > &retained_values, const exprt &value)
static bool should_use_local_definition_for(const exprt &expr)
Returns true if expr is complicated enough that a local definition (using a let expression) is prefer...
Pointer Dereferencing.