Código
Tenemos dos funciones que si ves siguen el mismo tipo de ordenamiento (un bubblesort muy simple
void ordenarAscendente(int *numeros, int size){ for(size_t i = 1; i < size; ++i) for(size_t j = 0; j < size-i; ++j) if(numeros[j] > numeros[j+1]){ numeros[j] += numeros[j+1]; numeros[j+1] = numeros[j] - numeros[j+1]; numeros[j] -= numeros[j+1]; } } void ordenarDescendente(int *numeros, int size){ for(size_t i = 1; i < size; ++i) for(size_t j = 0; j < size-i; ++j) if(numeros[j] < numeros[j+1]){ numeros[j] += numeros[j+1]; numeros[j+1] = numeros[j] - numeros[j+1]; numeros[j] -= numeros[j+1]; } }

Código
Y esto mismo lo podríamos hacer con otros tipos de ordenamiento. Es decir, hacemos una simple función que determine el tipo de orden a seguir y reutilizamos la función que tiene el algoritmo de ordenamiento.
bool mayor(int a, int b){ return a > b; } bool menor(int a, int b){ return a < b; } void ordenar(int *numeros, int size, bool (*orden)(int,int)){ for(size_t i = 1; i < size; ++i) for(size_t j = 0; j < size-i; ++j) if(!orden(numeros[j], numeros[j+1])){ numeros[j] += numeros[j+1]; numeros[j+1] = numeros[j] - numeros[j+1]; numeros[j] -= numeros[j+1]; } }
Luego ya si nos venimos arriba le ponemos unos <template> a ese programa y ya podemos ordenar cualquier tipo de dato (primitivos/objetos) que tengan claro está los operadores que usemos sobrecargados (en este caso < y >). Tampoco quiero adelantarte contenidos, ya lo verás más adelante pero para que veas que conocer los recursos disponibles ayuda a ahorrar muchas líneas de código al programador.
PD: Ahí te he dejado también como curiosidad cómo intercambiar el valor de dos variables sin usar ninguna variable auxiliar.