In this case, it is not undefined behaviour.
For case 0 (when expr == 0, which is your case), i gets assigned a value before being used (value being read from).
OK, to elaborate a bit more, for the snippet
switch (expr)
    {
            int i;
            case 0:
                    i = 17;
            default:
                    printf("%d\n", i);
    }
just makes the variable i defined in the block scope. Even if, the code would have been written as
            int i = 0; //or any value
the value of i not initialized, it's just the identifier is visible in the scope. You must have another statement assigning value to i before you can make use of it.
In this regard, the C11 standard has a very enlightening example and description. let me quote it, from chapter §6.8.4.2/P7
EXAMPLE In the artificial program fragment
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17;
/* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is ``i exists with automatic storage duration (within the block) but is never initialized, and thus if
  the controlling expression has a nonzero value, the call to the printf
  function will access an indeterminate value. [....]