can someone explain this short segment of C++ code

2020-04-27 11:12发布

问题:

#include <opcodes.h>
const char *getOpcodeName(
    uint8_t op
)
{
    #define OPCODE(x, y) if((0x##y)==op) return "OP_" #x;
        OPCODES
    #undef OPCODE
    return "OP_UNKNOWN";
}

Link to the code here: https://github.com/znort987/blockparser/blob/master/opcodes.cpp

Here is a link to the included opcodes.h

I understand this is just a strangely formatted function, however, I am wondering what exactly the * at the beginning of the function name means. I assume it has something to do with pointers?

Also, how are the #undef and #define statements valid? There's no semicolon after either one, and one of them seems to be defined as a one-line function. What does (0x##y) mean? What does return "OP_" #x mean? I haven't ever come across syntax like this before.

I want to get into C++ more seriously, but it's very hard when looking at code to tell what exactly is going on. How can I learn the syntax and rules most effectively?

回答1:

Run your code thru a C++ preprocessor, e.g. using g++ -Wall -C -E opcodes.cpp > opcodes.i then look inside the generated opcodes.i

#define is not a statement but a preprocessor directive.

The macro OPCODES gets expanded to some big chunk, notably containing OPCODE( NOP, 61) which would get expanded to something like

if ((0x61)==op) return "OP_" "NOP";

The two string literals are concatenated into one, "OP_NOP" here.

GCC has a good documentation on its cpp preprocessor. Read about stringification (with the single # like the ending #x; of the OPCODE macro) and about concatenation (with a double ## like (0x##y) of the OPCODE macro).



回答2:

Short answer: This function converts opcodes to string.

The * IS RELATED to do with pointers! in fact, this function returns a const char * type. This is a pointer for a char buffer (in this case) used to point to a C-String. Every C-String is actually a buffer with "readable characteres" (alphanumerical, some accents, basic symbols + some stuff) followed by a byte of value 0 (or '\0') to indicate the end of the string!

This function converts opcodes (assembly instructions) to a readable string. So The intention of the programmer was to transform:

  • 0x01 -> OP_CODE1
  • 0x02 -> OP_CODE2
  • 0x03 -> OP_CODE3

The expanded version of the code is something like this:

const char *getOpcodeName( uint8_t op )
{
    if((0x01)==op) return "OP_X";
    if((0x02)==op) return "OP_Y";
    if((0x03)==op) return "OP_Z";
    ...
    if((0x??)==op) return "OP_?";
    return "OP_UNKNOWN";
}

Insted of write houndreds of IF's ... the programmer decided to create the macro

#define OPCODE(x, y) if((0x##y)==op) return "OP_" #x;

So it's possible to write the OPCODES table easily like this:

#define OPCODES \
OPCODE( 01, "X" ) \
OPCODE( 02, "Y" ) \
OPCODE( 03, "Z" ) \
...
OPCODE( ??, "?" )

Opcodes are codes that indentify the processor instruction (assembly instructions). Example of real instructions (for some Intel processor) are:

  inc eax      ; opcode = 0x40
  pusha        ; opcode = 0x60
  nop          ; opcode = 0x90

So your table could be:

#define OPCODES \
OPCODE( 40, "INCEAX" ) \
OPCODE( 60, "PUSHA" ) \
OPCODE( 90, "NOP" )