Expressions are allowed in different places in the structure definition. For example, the size of the bit field, the number of items in array and pointer offset are all specified using expressions. Expressions are also used in calculating constant values and enumeration values.
Expression is a combination of immediate values, constants, enumeration values, function calls and field references. All these elements are connected with one or more operators.
An expression may be as simple as:
5 // evaluates to integer "5"
or as complex as:
5 + 7*(10 - 2) // calculate an expression
info.bmiHeader.sel.header.biSizeImage // take a value of a field several scopes deep
bfTypeAndSignature.bfType == 'BM' && bfReserved1 == 0 && bfReserved2 == 0 // verify a condition
RvaToVa(OptionalHeader.DataDirectory[i].VirtualAddress) // access a field in a nested structure and array and call an external function
Below is a table of supported operators. Operators are sorted by their precedence, from highest to lowest. Operators in the same row have the same precedence value and are evaluated from left to right.
Operator(s) | Name or Meaning |
---|---|
. [] () | Field access, array indexing, expression grouping |
() | Function call |
- ~ ! sizeof() & | Unary minus, bitwise NOT, logical NOT, sizeof, address-of |
* / % | Multiplication, division, modulo division |
+ - | Addition, subtraction |
<< >> >>> | Left shift, right shift, right unsigned shift |
< <= > >= | Less than, less than or equal, greater than, greater than or equal |
== != | Equality, inequality |
& | Bitwise AND |
^ | Bitwise XOR |
| | Bitwise OR |
&& | Logical AND |
|| | Logical OR |
?: | Conditional operator |
All expressions are evaluated at the time a structure file is compiled. If expression is successfully evaluated to constant value (that is, it does not contain any field references), calculated value is used instead of the expression. It is used each time a type is bound to the data, thus greatly minimizing bind time. You can get advantage of this optimization taken by the Device Monitoring Studio: instead of
const SecondsInHour = 3600; // 60 * 60
const SecondsInDay = 86400; // 60 * 60 * 24
use
const SecondsInHour = 60*60;
const SecondsInDay = SecondsInHour * 24;
As the result will be the same after the source file is compiled.
Device Monitoring Studio is also capable of optimizing sub-expressions:
struct A
{
int size;
byte data[size * (sizeof(int) - 1)];
};
Important note here is that constant sub-expression must be enclosed in parenthesis in order to be optimized. Otherwise, Device Monitoring Studio will not optimize it because it considers a variable as having arbitrary type:
var i = ...
var j = i + (5 + 3); // will be optimized to i + 8
var j = i + 5 + 3; // will not be optimized (consider the case when i is a string, for example, which results in i + "53").