В первом случае c - указатель на выделенную область памяти в куче размером 4 байта. Переменная располагается в оперативной памяти (RAM) и для обращения к ней, процессор вынужден выполнять чтение и запись [относительно] медленной оперативной памяти.
Во втором случае c - локальная переменная, компилятор скорее всего превратит все действия по работе с ней в арифметические операции с регистрами процессора, обращение к которым на порядки быстрее, чем работа с ОЗУ.
примерный ассемблерный псевдокод в первом случае (смешал несколько архитектур :) не воспринимайте буквально)
mov r3, #4 //размер выделяемой области 4 байта
call malloc() //выделим память
mov r28, r3 //сохраним возвращенное значение адреса памяти
str r29, r28[0] //сохраним a (r29) в выделенную память (r28[0])
mov r29, r30 //присвоим a (r29) значение b (r30)
ldr r30, r28[0] //присвоим b значение *c
mov r3, r28 //адрес для очистки памяти
call free() //удаление выделенной области
следует заметить, что чтение и запись из ОЗУ - относительно медленные операции
во втором случае скомпилированный код будет гораздо компактнее и выполнится практически моментально
mov r28, r29 //c = a
mov r29, r30 //a = b
mov r30, r28 //b = c