0

I'm going through a programming book and I'm wondering what a line of code does. There are no comments in the book or explanations about what it's supposed to do.

Below is a function that takes a character array and prints it backwards.

void print_reverse (char *s)
{
    size_t len = strlen(s);
    char *t = s + len -1;  // I don't understand what this line is doing
    while (t >= s) {
        printf("%c", *t);
        t = t - 1;
    }
    puts("");
}
user2864740
  • 60,010
  • 15
  • 145
  • 220
jwhstman
  • 43
  • 4
  • 3
    It's pointer arithmetic. – user2864740 Aug 01 '14 at 00:06
  • 2
    takes the pointer `s`, increments it by `len-1` objects, and assigns that pointer to `t`? – Mooing Duck Aug 01 '14 at 00:07
  • http://stackoverflow.com/questions/394767/pointer-arithmetic – user2864740 Aug 01 '14 at 00:07
  • take the code, and make a program using it. Then step through it in a debugger. A must have skill and habit – pm100 Aug 01 '14 at 00:14
  • 1
    I don't think anyone understands that line so far because actually it produces undefined behaviour. Consider what happens when s is an empty string – Brandin Aug 01 '14 at 00:17
  • 1
    @Brandin, it actually works exactly the same with an empty string as it does with a non-empty one. UB when you compare `t` and `s` when `t < s`. That situation arises _quicker_ with an empty string but it arises _always,_ regardless of the string. – paxdiablo Aug 01 '14 at 00:20
  • @paxdiablo So what we've learned is that the book this came from belongs next to a fireplace – Brandin Aug 01 '14 at 00:24
  • To simply prevent UB as pointed out by @Brandin, `char *t = s + len; while (t > s) { t--; printf("%c", *t); }` – chux - Reinstate Monica Aug 01 '14 at 13:29

2 Answers2

8

Let's say s is the string "PaxDiablo", stored in memory at location 1 thus:

         s
         |
         V
       +---+---+---+---+---+---+---+---+---+----+
       | P | a | x | D | i | a | b | l | o | \0 |
       +---+---+---+---+---+---+---+---+---+----+
Address: 1   2   3   4   5   6   7   8   9   10 

The expression t = s + len - 1 (where len is 9 in this case) sets t to eight characters past s.

         s                               t
         |                               |
         V                               V
       +---+---+---+---+---+---+---+---+---+----+
       | P | a | x | D | i | a | b | l | o | \0 |
       +---+---+---+---+---+---+---+---+---+----+
Address: 1   2   3   4   5   6   7   8   9   10 

In other words, it gives you the address of the last character in the string.

The rest of the code then iterates over the string in a backwards direction, by decrementing t until it passes s.


Technically, this is undefined behaviour since you're only every supposed to compare pointers where they point to the same array on one character beyond (here we are comparing t where it's one character before the array), but you'd struggle to find a system on which this didn't work.

As Potatoswatter (I absolutely love some of the names people choose here on SO) points out in a comment, you can avoid that comparison by using the do {} while construct rather than while {}:

#include <stdio.h>
#include <string.h>

void printReverse (char *str) {
    size_t len = strlen (str);
    if (len != 0) {
        char *pStr = str + len;
        do {
            putchar (*(--pStr));
        } while (pStr > str);
    }
    putchar ('\n');
}
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

Let's say s points to the string "Hello". In memory, it looks like:

s --> [ H | e | l | l | o | \0 ]

Now, char *t declares a new pointer variable to a char value, that is initialized with s + len - 1, which is:

s ------                    ------- s + 5   // (len is 5 since strlen("Hello") is 5)
       |                    |
     [ H | e | l | l | o | \0 ]
                       |
                       ------------ finally, t = s + len - 1
Santa
  • 11,381
  • 8
  • 51
  • 64