¿Cuál es la diferencia entre los siguientes dos fragmentos de código?

En C hay tres tipos básicos de punteros.

Veamos a través de esto con los siguientes ejemplos.
Ej 1.

int a;

En el ejemplo anterior, a es variable y & a es puntero

Ej: 2

int * a;

En los ejemplos anteriores * a es variable y & a es puntero pero a es puntero (para * a ) y variable (para & a ). Entonces a se llama variable puntero

Ej: 3

int a [10];

En el ejemplo anterior, a es puntero y a [0], a [1] , … son variables.

En su caso, arr1 y arr2 son punteros cuyos valores son diferentes.

Si declara int arr1 [10], int arr2 [10], el compilador asigna diferentes ubicaciones para arr1 y arr2. Entonces cuando ejecutas la declaración
if (arr1 == arr2), está comparando los punteros que de todos modos son diferentes. Por lo tanto, la declaración es False. En este caso, no está comparando el contenido de las matrices. Pero cuando ejecuta la instrucción memcmp (arr1, arr2, n * sizeof (int)), en realidad está comparando el contenido de la matriz. Está pasando los punteros de ambas matrices a la función memcmp.

Stack Overflow es probablemente un mejor lugar para esta pregunta.

Aparte de eso, cuando haces:

if (arr1 == arr2)

arr1 y arr2 se descomponen en int * antes ==. Esto se debe a que no hay un operador == para la matriz, ¡y no puede escribir uno!

Entonces, si (arr1 == arr2) verifica si las dos matrices comienzan en el mismo lugar en la memoria.

Por otro lado, cuando haces:

if (! memcmp (arr1, arr2, sizeof (arr1)))

(tenga en cuenta que utilicé sizeof (arr1), que es el número de bytes en arr1 suponiendo que arr1 no se ha reducido a int *)

Está haciendo una comparación byte por byte del contenido en arr1 y arr2.

int arr1 [] = {1,2,3};
int arr2 [] = {1,2,3};
int * arr3 = arr1;

(arr1 == arr2) es falso
(! memcmp (arr1, arr2, sizeof (arr1))) es verdadero
(arr1 == arr3) es verdadero
(! memcmp (arr1, arr3, sizeof (arr1))) también es cierto