You have two different types:
int x[10];
This declares a type of int[10]. As you can see, the size of the array is part of the type.
int * y;
This is a pointer to an int. It will have size equivalent to the size of a pointer on your system. Your system appears to have 64 bit pointers (commonplace now).
Here's a little trick you can do in C++17:
template<class T>
std::enable_if_t<std::is_pointer_v<T>> foo(T ptr)
{
std::cout << "Called foo for a pointer type" << std::endl;
}
template<class T, int N>
void foo(T (&arr)[N])
{
std::cout << "Called foo for an array type of size " << N << std::endl;
}
We defined two functions named foo. The first one uses type traits to only enable itself for pointer types, and the second one is for array types (by reference).
We can call them from main:
int x[10];
int * y = nullptr;
foo(x);
foo(y);
And get output that looks like this:
Called foo for an array type of size 10
Called foo for a pointer type
Edit: Mark Ransom gave a good aside about the size of the array allocated for y, and I'll throw in a little piece, too. While it is indeed an implementation detail, what's common is that the implementation of the memory allocator (like malloc or new) under the hood will write this value to the start of the memory block that it allocates.
That way, when you call delete, you don't have to know how many bytes to delete; the deallocator (free or delete) can check the start of the memory block for how big it is. So an allocation for, say 1 byte, may actually allocate more than 1 byte. (Again, this is only one way it can be done)