To expand on Michael Madsen's answer a little, an example might be the behaviour of the ++ operator. Informally, we describe the operator using plain English. For instance:
If x
is a variable of type int
, ++x
causes x to be incremented by one.
(I'm assuming no integer overflows, and that ++x
doesn't return anything)
In a formal semantics (and I'm going to use operational semantics), we'd have a bit of work to do. Firstly, we need to define a notion of types. In this case, I'm going to assume that all variables are of type int
. In this simple language, the current state of the program can be described by a store, which is a mapping from variables to values. For instance, at some point in the program, x
might be equal to 42, while y
is equal to -5351. The store can be used as a function -- so, for instance, if the store s
has the variable x
with the value 42, then s(x) = 42
.
Also included in the current state of the program is the remaining statements of the program we have to execute. We can bundle this up as <C, s>
, where C
is the remaining program, and s
is the store.
So, if we have the state <++x, {x -> 42, y -> -5351}>
, this is informally a state where the only remaining command to execute is ++x
, the variable x
has value 42, and the variable y
has value -5351
.
We can then define transitions from one state of the program to another -- we describe what happens when we take the next step in the program. So, for ++
, we could define the following semantics:
<++x, s> --> <skip, s{x -> (s(x) + 1)>
Somewhat informally, by executing ++x
, the next command is skip
, which has no effect, and the variables in the store are unchanged, except for x
, which now has the value that it originally had plus one. There's still some work to be done, such as defining the notation I used for updating the store (which I've not done to stop this answer getting even longer!). So, a specific instance of the general rule might be:
<++x, {x -> 42, y -> -5351}> --> <skip, {x -> 43, y -> -5351}>
Hopefully that gives you the idea. Note that this is just one example of formal semantics -- along with operational semantics, there's axiomatic semantics (which often uses Hoare logic) and denotational semantics, and probably plenty more that I'm not familiar with.
As I mentioned in a comment to another answer, an advantage of formal semantics is that you can use them to prove certain properties of your program, for instance that it terminates. As well as showing your program doesn't exhibit bad behaviour (such as non-termination), you can also prove that your program behaves as required by proving your program matches a given specification. Having said that, I've never found the idea of specifying and verifying a program all that convincing, since I've found the specification usually just being the program rewritten in logic, and so the specification is just as likely to be buggy.