1

I am trying to write a C++ program for my Computer Machine Organization class in which I perform a memory dump in hex on some address stored in memory. I don't really understand what a memory dump is, and am pretty new to writing C++. My questions are:

  1. How can I create a method that takes two arguments in which they specify address in memory?
  2. How can I further modify those arguments to specify a word address that is exactly 4 bytes long?
  3. How can I then convert those addresses into hex values?

I know that this is a lot, but thank you for any suggestions.

For anyone who needs it, here is my code so far:

#include <stdio.h>

// Create something to do the methods on
char array[3] = {'a', 'b', 'c'};

void mdump(char start, char end){

    // Create pointers to get the address of the starting and ending characters
    char* pointer1 = (char *)& start;
    char* pointer2 = (char *)& end;

    // Check to see if starting pointer is in lower memory than ending pointer
    if(pointer1 < pointer2){
        printf("Passed");
    }
    else{
        printf("Failed");
    }

    // Modify both the arguments so that each of them are exactly 4 bytes
    // Create a header for the dump
    // Iterate through the addresses, from start pointer to end pointer, and produce lines of hex values
    // Declare a struct to format the values
    // Add code that creates printable ASCII characters for each memory location (print "cntrl-xx" for values 0-31, or map them into a blank)
    // Print the values in decimal and in ASCII form
}

int main(){
    mdump(array[0], array[2]);
    return 0;
}
mFuchs
  • 35
  • 6
  • 1. Same way you do any typed function arguments, and you will use pointer types (note: you only need _one_ value to specify an address); 2. A pointer _already_ specifies an address; 3. You dereference the pointer. – paddy Mar 04 '22 at 05:21
  • @paddy So what I need to do is create a procedure that takes two arguments, the first one specifies where the memory dump starts, and the second one specifies where it should end. I edited my question to have some more clarity on question 2. Thanks for the reply. – mFuchs Mar 04 '22 at 05:47
  • Why do you pass in `char` into `mdump()` and take the address of the arguments? (Read this literally and loud: "the address of the arguments". What you want instead is the address of something outside `mdump()`.) Wouldn't it make more sense to pass in `char*` (or better `const char*`)? – Scheff's Cat Mar 04 '22 at 05:59
  • 1
    Btw. `#include ` is a bad choice in C++. Better would be `#include ` or even better `#include ` (but then you have to switch from C `printf()` to C++ stream output). – Scheff's Cat Mar 04 '22 at 06:02
  • @Scheff'sCat Yes that would make more sense I suppose. I don't exactly know how I would then do the method on my 'char array[3] = {'a', 'b', 'c'}'. – mFuchs Mar 04 '22 at 06:03
  • Why not the same way like you did? E.g. `mdump(&a[0], &a[2]);` – Scheff's Cat Mar 04 '22 at 06:04
  • 1
    FYI: [How would I create a hex dump utility in C++?](https://stackoverflow.com/a/16804835/7478597) – Scheff's Cat Mar 04 '22 at 06:05
  • @Scheff'sCat Okay, thank you. And, I already saw that post but the code is so confusing to me. I only started writing C++ a few weeks ago. – mFuchs Mar 04 '22 at 06:06
  • Well.... There is the ability to have printf show hexadecimal numbers. I forget the flag, but they've discussed it here: https://stackoverflow.com/questions/12344814/how-to-print-unsigned-char-as-2-digit-hex-value-in-c – enhzflep Mar 04 '22 at 06:22

1 Answers1

5

How to write a Hex dump tool while learning C++:

  1. Start with something simple:
#include <iostream>

int main()
{
  char test[32] = "My sample data";
  // output character
  std::cout << test[0] << '\n';
}

Output:

M

Live demo on coliru


  1. Print the hex-value instead of the character:
#include <iostream>

int main()
{
  char test[32] = "My sample data";
  // output a character as hex-code
  std::cout << std::hex << test[0] << '\n'; // Uh oh -> still a character
  std::cout << std::hex << (unsigned)(unsigned char)test[0] << '\n';
}

Output:

M
4d

Live demo on coliru

Note:

The stream output operator for char is intended to print a character (of course). There is another stream output operator for unsigned which fits better. To achieve that it's used, the char has to be converted to unsigned.

But be prepared: The C++ standard doesn't mandate whether char is signed or unsigned—this decision is left to the compiler vendor. To be on the safe side, the 'char' is first converted to 'unsigned char' then converted to unsigned.


  1. Print the address of the variable with the character:
#include <iostream>

int main()
{
  char test[32] = "My sample data";
  // output an address
  std::cout << &test[0] << '\n'; // Uh oh -> wrong output stream operator
  std::cout << (const void*)&test[0] << '\n';
}

Output:

My sample data
0x7ffd3baf9b70

Live demo on coliru

Note:

There is one stream output operator for const char* which is intended to print a (zero-terminated) string. This is not what is intended. Hence, the (ugly) trick with the cast to const void* is necessary which triggers another stream output operator which fits better.


  1. What if the data is not a 2 digit hex?
#include <iomanip>
#include <iostream>

int main()
{
  // output character as 2 digit hex-code
  std::cout << (unsigned)(unsigned char)'\x6' << '\n'; // Uh oh -> output not with two digits
  std::cout << std::hex << std::setw(2) << std::setfill('0')
    << (unsigned)(unsigned char)'\x6' << '\n';
}

Output:

6
06

Live demo on coliru

Note:

There are I/O manipulators which can be used to modify the formatting of (some) stream output operators.


  1. Now, put it all together (in loops) et voilà: a hex-dump.
#include <iomanip>
#include <iostream>

int main()
{
  char test[32] = "My sample data";
  // output an address
  std::cout << (const void*)&test[0] << ':';
  // output the contents
  for (char c : test) {
    std::cout << ' '
      << std::hex << std::setw(2) << std::setfill('0')
      << (unsigned)(unsigned char)c;
  }
  std::cout << '\n';
}

Output:

0x7ffd345d9820: 4d 79 20 73 61 6d 70 6c 65 20 64 61 74 61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Live demo on coliru


  1. Make it nice:
#include <algorithm>
#include <iomanip>
#include <iostream>

int main()
{
  char test[32] = "My sample data";
  // hex dump
  const size_t len = sizeof test;
  for (size_t i = 0; i < len; i += 16) {
    // output an address
    std::cout << (const void*)&test[i] << ':';
    // output the contents
    for (size_t j = 0, n = std::min<size_t>(len - i, 16); j < n; ++j) {
      std::cout << ' '
        << std::hex << std::setw(2) << std::setfill('0')
        << (unsigned)(unsigned char)test[i + j];
    }
    std::cout << '\n';
  }
}

Output:

0x7fffd341f2b0: 4d 79 20 73 61 6d 70 6c 65 20 64 61 74 61 00 00
0x7fffd341f2c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Live demo on coliru


  1. Make it a function:
#include <algorithm>
#include <iomanip>
#include <iostream>

void hexdump(const char* data, size_t len)
{
  // hex dump
  for (size_t i = 0; i < len; i += 16) {
    // output an address
    std::cout << (const void*)&data[i] << ':';
    // output the contents
    for (size_t j = 0, n = std::min<size_t>(len - i, 16); j < n; ++j) {
      std::cout << ' '
        << std::hex << std::setw(2) << std::setfill('0')
        << (unsigned)(unsigned char)data[i + j];
    }
    std::cout << '\n';
  }
}

int main()
{
  char test[32] = "My sample data";
  std::cout << "dump test:\n";
  hexdump(test, sizeof test);
  std::cout << "dump 4 bytes of test:\n";
  hexdump(test, 4);
  std::cout << "dump an int:\n";
  int n = 123;
  hexdump((const char*)&n, sizeof n);
}

Output:

dump test:
0x7ffe900f4ea0: 4d 79 20 73 61 6d 70 6c 65 20 64 61 74 61 00 00
0x7ffe900f4eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
dump 4 bytes of test:
0x7ffe900f4ea0: 4d 79 20 73
dump an int:
0x7ffe900f4e9c: 7b 00 00 00

Live demo on coliru

Note:

(const char*)&n may look a bit adventurous. In fact, conversion of pointers is always something which should be at best not necessary. However, for the dump tool this is the easiest way to access the bytes of arbitrary data. (This is one of the rare cases which is explicitly allowed by the standard.)


An even nicer hexdump can be found in
SO: How would I create a hex dump utility in C++?
(which I recommended OP beforehand).

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • I noticed that in the output, `0x7ffe900f4eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00`, outputs all 0's, why is this? – mFuchs Mar 04 '22 at 07:21
  • `char test[32]` is an array of 32 characters. It's initialized with a C string literal `"My sample data"` which consists of 14 characters and the 0-terminator (auto-appended by the compiler). The rest of the 32 - 15 = 17 characters are ... ehm ... uninitialized or filled with 0s. (I must admit I'm uncertain as `test` is a local variable. [cppreference](https://en.cppreference.com/w/c/language/array_initialization) mentions the initialization of arrays with string literals but leaves this detail out. :-( Or, it's mentioned somewhere else and I overlooked it...) – Scheff's Cat Mar 04 '22 at 07:38
  • Please note that the demo on coliru runs in something like a sandbox. I'm 99.999 % sure that the corresponding memory is zeroed out before my session is started. Otherwise, my little hexdump could show uninitialized memory (actually U.B. but often possible in practice). Who knows what was stored in this memory before.... (With a bit luck the root password.) ;-) – Scheff's Cat Mar 04 '22 at 07:50
  • 1
    @mFuchs and Scheff'sCat: Just like with `char test[32] = {'h', 'i'};`, providing an initializer for *any* element initializes *all* elements in C or C++. Any elements not explicitly initialized are initialized with zero. Fun fact: `char str[2] = "hi";` is legal in C (no terminating 0 byte because there's no room for it), but not in C++ where the array has to be at least large enough to hold the 0-terminated string. Annoying for stuff like `char hex_lut[16]="0123456789abcdef"`. `char str[1] = "hi"` is illegal in both languages, though. – Peter Cordes Mar 04 '22 at 11:39