But from a performance perspective, you are right!
Anton Ertl of Gforth fame published a microbenchmark performance comparison of toy interpreters, written in C, run on various CPUs. [1] As you say, the approach using function pointers (call threading) scores poorly.
Interestingly, direct threading and indirect threading are extremely close, with the winner seeming to depend on the specific CPU. [1] Branch-prediction differences seem to be the main reason. There was a 2001 paper on this. [2]
[0] https://www.complang.tuwien.ac.at/forth/threaded-code.html
[1] https://www.complang.tuwien.ac.at/forth/threading/
[2] https://github.com/ForthPapersMirror/Papers/blob/master/Conv...
https://eli.thegreenplace.net/2012/07/12/computed-goto-for-e... - article about them being used in CPython
In practice, having a switch is not worse or can even be better for performance because any non-naive optimising compiler will emit a nicely ordered indexed jump table, whereas following function pointers involves more indirection through the addresses of the functions, and has the additional overhead of passing args and preparing/restoring the stack.