A couple of possibilities... none of these criteria is firmly written in stone:

The computational model employed; all general purpose programming languages are based on TuringMachines (or on other mathematical formulations which have equivalent computational power, such as the LambdaCalculus). (Some special-purpose languages may use less powerful models, such as FiniteStateMachine or PushdownAutomata?). Hardware engineers generally deal with state machines and combinatorial logic, with occasional use of stacks and the like.

Software engineers are generally freed from having to worry about such things as signal timing, clock skew, bus capacitance and loading (fan-out), analog signal processing (and analog signal characteristics), temperature, power consumption, electromagnetic interference, and a whole host of other nasties which plague hardware engineers.

Software engineers, however, do have to worry about similar constraints such as response times, memory utilization, code re-entry and other threading issues, and external data sources with various data characteristics. The specifics differ but the concept remains. As a former hardware engineer, though, I find that hardware engineers usually have far better tools available for addressing their issues.

Hardware engineers can employ high degrees of parallelism when performing computations; software engineers are limited by the VonNeumannBottleneck.

Software engineers are generally not capable of SmokingTheBoard? - destroying via software the hardware that the software is running on.

Software engineers can produce designs that are malleable in ways that hardware engineers cannot approach. This has many functional advantages, but many feel it leads to much greater sloppiness in our craft.

Software engineers, in many cases, are wholly unfamiliar with sound engineering practice.

Tools for software engineers are (in many ways) much simpler, faster, (and cheaper!) than CAD tools for hardware engineers. While generating optimal code in a compiler and routing an FPGA are both NpHard problems, quick-and-dirty heuristics will generally suffice for software engineers; you will rarely, if ever, see a C compiler spending hours trying to squeeze every last cycle or instruction out of a function. CAD tools for FPGA or ASIC design, on the other hand, must engage in rather expensive global optimizations to be worthwhile. Consequently, such tools may take hours to run, even on ultra-fast workstations.

Only one thing more dangerous than a hardware engineer with a compiler: a software engineer with a soldering iron. HaHaOnlySerious

You are presuming he knows which end gets hot, of course. Oh, is it the one without the cord sticking out?

I just hope he never tries to Pair Solder!

A distinguishing thing between these is that the former has to deal with physics -- that's a major constraint. The latter only has to deal with logic.