Q:

How many ways are there to pass char array to function in C?

foo(char *s)
foo(char *s[ ])
foo(char  s[ ])

What is the difference in all these ?
Is there any way in which I will be able to modify the elements of the array which is passed as argument, just as we pass int or float using & and value of actual arguments gets modified?

A:

It is not possible in C to pass an array by value. Each working solution that you listed (which unfortunately excludes #2) will not only let you, but force you to modify the original array.
Because of argument decay, foo(char* s) and foo(char s[]) are exactly equivalent to one another. In both cases, you pass the array with its name:
char array[4];
foo(array); // regardless of whether foo accepts a char* or a char[]

The array, in both cases, is converted into a pointer to its first element.
The pointer-to-array solution is less common. It needs to be prototyped this way (notice the parentheses around *s):
void foo(char (*s)[]);

Without the parentheses, you're asking for an array of char pointers.
In this case, to invoke the function, you need to pass the address of the array:
foo(&array);

You also need to dereference the pointer from foo each time you want to access an array element:
void foo(char (*s)[])
{
    char c = (*s)[3];
}

Just like that, it's not especially convenient. However, it is the only form that allows you to specify an array length, which you may find useful. It's one of my personal favourites.
void foo(char (*s)[4]);

The compiler will then warn you if the array you try to pass does not have exactly 4 characters. Additionally, sizeof still works as expected. (The obvious downside is that the array must have the exact number of elements.)

A:

Scenario: You're calling the function with an array as argument.
In that case,
 foo(char *s)

and
 foo(char s[])

are considered equivalent. They both expect to be called with a char array.
OTOH, foo(char *s[ ]), is different, as it takes the address of a char * array.

