CSE 333 Exercise 2

Out:   Friday, January 9
Due:   Monday, January 12 by 11 AM
Rating:   1 (note)
CSE 333 Exercise Rating Scale

Each exercise this quarter is rated on a integer scale of 1 – 5, inclusive, with 1 being the "least time-consuming" and 5 being the "most time-consuming".

This difficulty scale is meant as a rough guide for you in predicting the amount of time to set aside for each exercise as you balance the work required for 333 with your other obligations. However, it is necessarily imperfect as everyone's set of circumstances and experiences with the exercises differ. If your experience with an exercise does not align with its rating, that is not a reflection of you or your abilities.

Goals

  • Write code that uses pointers and data representations.
  • Examine data structure representation in memory.

Background

Recall the topics of endianness and C struct layout:

  • Endianness determines the ordering of the bytes when multibyte data is stored in memory – the least significant byte is stored at the littlest or biggest address.
    • This does not affect the address of the data, which is always that of the first/lowest byte.
  • In C, the layout of a struct instance is determined by the compiler based on the following rules:
    • The fields appear in the order in which they are declared.
    • Each field is placed at an offset that is a multiple of its individual alignment requirement (K).
    • The size of the struct instance (and its alignment requirement) is based on Kmax.

Problem Description

Write a C program (ex2.c) that does the following:

  • Contains a function called PrintBytes with prototype void PrintBytes(void* mem_addr, int num_bytes);. This function should print out the length, the address passed in, and the bytes of memory as exactly two digits each in lowercase hexadecimal, e.g.,
    The 4 bytes starting at 0x7fff1081856c are: ff 01 30 4e
  • Contains the main function found below that prints the bytes of a struct instance and its fields using the function that you wrote. Your main should match the provided code exactly; copy-and-paste this code from the spec instead of re-typing it. If you notice any style issues with it, feel free to ignore them.
    int main(int argc, char** argv) {
      float    float_val = 1.0f;
      char     char_val = '1';
      int32_t  int_val = 1;
      double   double_val  = 1.0;
    
      typedef struct {
        float    float_val;
        char     char_val;
        int32_t  int_val;
        double   double_val;
      } Ex2Struct;
    
      Ex2Struct struct_val = { float_val, char_val, int_val, double_val };
    
      PrintBytes(&float_val, sizeof(float));
      PrintBytes(&char_val, sizeof(char));
      PrintBytes(&int_val, sizeof(int32_t));
      PrintBytes(&double_val, sizeof(double));
      PrintBytes(&struct_val, sizeof(struct_val));
    
      return EXIT_SUCCESS;
    }
    
  • When your program compiles and runs, it should produce output that is identical to the following, except for values that might change from one execution to the next because of randomness outside of your control, including the values of uninitialized/mystery data and security measures such as stack randomization.
    $ gcc -Wall -g -std=c17 -o ex2 ex2.c
    $ ls
    ex2 ex2.c
    $ ./ex2
    The 4 bytes starting at 0x7ffce824a700 are: 00 00 80 3f
    The 1 bytes starting at 0x7ffce824a6ff are: 31
    The 4 bytes starting at 0x7ffce824a704 are: 01 00 00 00
    The 8 bytes starting at 0x7ffce824a708 are: 00 00 00 00 00 00 f0 3f
    The 24 bytes starting at 0x7ffce824a710 are: 00 00 80 3f 31 00 00 00 01 00 00 00 0d 62 00 00 00 00 00 00 00 00 f0 3f
    $
    • The last output line is a single line, but may wrap in your browser.
    • The number of bytes shown on the last line is NOT the sum of the number of bytes shown on the previous lines!

Implementation Notes

  • You will want to match the formatting shown in the example given above exactly, including spacing and capitalization.
    • For the case of num_bytes = 0, it's ok to have a space at the end of the output.
    • For the case of num_bytes > 0, there should not be a space at the end of the output.
  • You will need to convince the compiler to let you access bytes in memory starting from a void*, then loop to print each byte one-by-one using a pointer.
  • You will need to use format specifiers in printf to print out a pointer value as well as a uint8_t in lowercase hexadecimal. As a hint, take inspiration from the following code:
    #include <inttypes.h>
    uint8_t a_byte = 0xD1;
    printf("The byte is: %02" PRIx8 " -- enjoy!\n", a_byte);
    

Style Focus

No new style things to point out, but we strongly recommend that you revisit the Exercise 1 Style Focus section.

Submission

Submit the following file(s) by creating an ex2-submit tag in your exercise repo before the assignment deadline. The file(s) should be located in the exact directory listed below, including capitalization:

  • ex2/ex2.c

Your code must:

  • Compile without errors or warnings on CSE Linux machines (lab workstations, attu, or CSE home VM).
  • Have no runtime errors, memory leaks, or memory errors (gcc and valgrind).
  • Be contained in the file listed above that compiles with the command:
    $ gcc -Wall -g -std=c17 -o ex2 ex2.c
  • Have a comment at the top of your .c file with your name(s) and CSE or UW email address(es).
  • Be pretty: the formatting, modularization, variable and function names, commenting, and so on should be consistent with class style guidelines. Additionally, the linter shouldn't have any complaints about your code (cpplint.py --clint).
  • Be robust: your code should deal with hard-to-handle/edge cases and bogus user input (if there are any) gracefully.