2
/*
 * Change the value pointed to by ptr byte-by-byte so that when returned as an
 * integer the value is 351351.
 *
 * Hints: Recall that an int is 4 bytes and how little-endian works for
 * multibyte memory storage. We suggest starting by converting 351351 into
 * binary/hexadecimal.
 *
 * ALLOWED:
 *   Pointer operators: *, &
 *   Binary integer operators: -, +, *
 *   Shorthand operators based on the above: +=, *=, ++, --, etc.
 *   Unary integer operators: !
 *
 * DISALLOWED:
 *   Pointer operators: [] (Array Indexing Operator)
 *   Binary integer operators: &, &&, |, ||, <, >, <<, >>, ==, !=, ^, /, %
 *   Unary integer operators: ~, -
 */

I need to write a function following the requirements above. I can't use loops, or array indexing, and other stuff. I understand the endianness concept. So I have integer 351351. In hex it is 0x 00 05 5C 77. So with the little endian machine, it would be stored as 77 5C 05 00, right?

int endianExperiment(int * ptr) {
  char * bytePtr;
  // Your code here
  // 0x 00 05 5C 77
  int num = 0;
  bytePtr = (char * ) ptr;

  return num;
}

This is my function so far. I got the address of the pointer parameter in char (so only 2 bytes) then stored it in bytePtr. But I am stuck after. I tried several options, and nothing works. Please help.

Also, so with little-endian, the number is stored with a least significant digit at the lowest index in array. But to access it (address of the digit), is it still the lowest index? Or is it the index that contains the first byte? Where do I need to have pointer point to?

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • `So with the little endian machine, it would be stored as 77 5C 05 00, right?` - Right. What I don't understand is what it is you start with and what you want to return. If you start with `351351` and need that returned -- return it. Or are you saying that `351351` is the value and you need to change endianness using only ALLOWED operators? – David C. Rankin Oct 14 '20 at 02:52
  • @DavidC.Rankin Yes, I need to change the endianness. – Roman Kudryashov Oct 14 '20 at 03:05
  • 1
    For starters, you want to use `unsigned` and not `int` and recall doubling a value is the same a shifting left by one and halving the value is the same as shifting right by one. – David C. Rankin Oct 14 '20 at 04:51
  • When you say `"I can't use loops"` -- is that because you can't use `'<'` and `'>'` or is there another constraint not shown in DISALLOWED that says no loops? – David C. Rankin Oct 14 '20 at 07:49

1 Answers1

1

The "disallowed" list makes a lot of restrictions. For instance, a usual approach is to bitmask (&) and bitshift (<<, >>) the bytes into opposite endian position, but these operations are disallowed. Remainder (%) is also disallowed, which we otherwise could use to emulate bitmasking. So what's left? It sounds like an allowed approach is the following (and hinted at by asking that the value be changed "byte-by-byte"):

  1. Start by punning the input to a char* pointer as you have done: char* bytePtr = (char*)ptr. Assuming little endian native byte order, *bytePtr is the least significant byte of integer *ptr.

  2. Make a pointer to the most significant byte of num: char* dest = ((char*)&num) + 3.

  3. Assign *bytePtr to *dest. That's one byte done.

  4. Increment bytePtr and decrement dest.

  5. Repeat steps 3 and 4 three more times for the remaining bytes. But don't use a loop to do that, since < is disallowed, just write out the repetitions unrolled.

At the end, num contains the integer with reversed endianness.


Edit: I may have misunderstood the intention. Thanks to busybee for pointing this out. Re-reading, the problem statement is "Change the value pointed to by ptr byte-by-byte so that when returned as an integer the value is 351351."

It sounds like we should write the specific int value 351351 to *ptr. And, we should do so byte-by-byte---it would be too easy if we could just do *ptr = 351351.

The hexadecimal form of 351351 is 0x00055c77 like Roman said.

  • Supposing the machine is little endian, write the bytes 0x77, 0x5c, 0x05, 0x00, starting in the lowest address with 0x77: *bytePtr = 0x77, *(bytePtr + 1) = 0x5c, and so on.

  • However, if the machine is big endian, the bytes need to be in the reverse order so that *ptr == 351351 when interpreted as an int value: *bytePtr = 0x00, *(bytePtr + 1) = 0x05, and so on.

It's a good idea to write code portably to work on both little endian and big endian machines. There are multiple methods to detect the endianness of the machine, including some that work in C with the allowed operations. So we could detect little vs. big endianness, then proceed to write the bytes as described above in the appropriate order. (Endianness other than little and big are also possible, and a comprehensive solution could handle them as well, but nowadays they are rare.)

Pascal Getreuer
  • 2,906
  • 1
  • 5
  • 14
  • How do you know that the endianness is reversed? The system can be a little-endian system. – the busybee Oct 14 '20 at 07:42
  • If the native order is little endian (as it is on x86 and most ARM), then bytePtr in step 1 points to the least significant byte of the integer *ptr. If the native order is big endian, then it points to the most significant byte. In either case, we write the bytes to *dest in reversed order, so we get the opposite endianness, whichever it is. – Pascal Getreuer Oct 15 '20 at 04:40
  • 1
    Oh, sorry, I overlooked step 4. -- Anyway, the information about endianness reversal is hidden in a comment. The question is about storing a specific 4-byte integer as little endian, without saying what endianness the system has. This is a completely different thing. – the busybee Oct 15 '20 at 05:54
  • Thank you for pointing this out. I agree the problem statement appears to be about storing the specific int value 351351. I elaborated my answer. – Pascal Getreuer Oct 15 '20 at 07:30