When you do *ptr = b1, what you are doing is as follows:
1) Dereferencing the object that is already at the memory location pointed to be ptr
2) Copying over the members of b1 to the object deferenced in 1
Which is why, your code will give you a segmentation fault if you utilised a member variable. Consider the following modification for your class:
#include <iostream>
#include <string>
class Base
{
public:
    Base():name("undefined")
    {
    }
    Base& operator=(const Base& other){
        std::cout << "Copying from " << other.name << " to " << name << std::endl;
        name = other.name;
        return *this;
    }
    void fc(){
        cout<<"Class function is called"<<endl;
    }
    std::string name;
};
Now, with the above modifications, the following code will segfault:
int main()
{   Base *ptr; ptr=0;
    Base b1,b2;
    *ptr=b1; // will segfault here
    ptr->fc();
    ptr=0;
    ptr=&b2;
    ptr->fc();
    return 0;
}
In the second case, i.e. when you call
ptr=&b2;
what you are doing is merely setting the address of the memory location that ptr points to. So, the following code WILL run fine:
int main()
{   Base *ptr; ptr=0;
    Base b1,b2;
    ptr=&b2;
    ptr->fc();
    return 0;
}
A more illustrative example will be the following:
int main()
{   Base *ptr;
    Base objA;
    objA.name = "objA";
    Base objB;
    objB.name = "objB";
    ptr = new Base();
    ptr->name = "ptrObj";
    *ptr = objA;
    std::cout << "Ptr is now " << ptr->name << std::endl;
    ptr = &objB;
    std::cout <<  "Ptr is now " << ptr->name << std::endl;    
    return 0;
}
Which gives the following output:
Copying from objA to ptrObj
Ptr is now objA 
Ptr is now objB