Regarding your first (commented) example...
As mentioned in comments, C passes arguments by value.
In C, if the value of a variable is to be changed, then the address of that variable must be passed as opposed to the variable itself.
So in your original function:
void insert_start(struct node *head, int data)
{
    struct node *ptr = (struct node *)malloc(sizeof(struct node));
    ptr->next_ptr = head;
    ptr->data = data;
    head=ptr;
}
*head contains the value of the address of an instance of struct node. As such, head will not be changed upon return.
Should you want to use a form of the void function that will modify the argument to allow it to change the address contained in *head, then you must pass it's address:  **head.  Then in the body of the function, make the following changes.  (note reason cast has been removed.)
void insert_start(struct node **head, int data)
 {
     struct node *ptr = malloc(sizeof(*ptr));
     if(ptr)//verify return of malloc before using
     {
        ptr->data = data;
        ptr->next_ptr = (*head);
        (*head) = ptr;//setting head to address of new node
     }
 }
Calling example: (called in a function such as main())
struct node *new;
insert_start(&new, 10);//passing address of *new
if(!new)
{ 
    //handle error 
}
....