First of all, the return type for main should be int, not void. void main() is only well-defined if your compiler documentation explicitly lists it as a legal signature. Otherwise you invoke undefined behavior. Use int main(void) instead.
Secondly, it's time for a quick crash course on strings, arrays, and pointers.
Unlike Java, C doesn't have a dedicated string datatype; rather, strings are represented as sequences of char values terminated by a 0. They are stored as arrays of char. The string literal "hello" is stored as a 6-element array of char (const char in C++). This array has static extent, meaning it is allocated at program startup and held until the program terminates. Attempting to modify the contents of a string literal invokes undefined behavior; it's best to act as though they're unwritable.
When an array expression appears in most contexts, the type of the expression is converted from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element of the array. That's one of the reasons the string = "hello"; statement doesn't work; in that context, the type of the expression "hello" is converted from "6-element array of char" to "pointer to char", which is incompatible with the target type (which, being char, isn't the correct type anyway). The only exceptions to this rule are when the array expression is an operand of either the sizeof or unary & operators, or if it is a string literal being used to initialize another array in a declaration.
For example, the declaration
char foo[] = "hello";
allocates foo as a 6-element array of char and copies the contents of the string literal to it, whereas
char *bar = "hello";
allocates bar as a pointer to char and copies the address of the string literal to it.
If you want to copy the contents of one array to another, you need to use a library function like strcpy or memcpy. For strings, you'd use strcpy like so:
char string[MAX_LENGTH];
strcpy(string, "hello");
You'll need to make sure that the target is large enough to store the contents of the source string, along with the terminating 0. Otherwise you'll get a buffer overflow. Arrays in C don't know how big they are, and running past the end of an array will not raise an exception like it does in Java.
If you want to guard against the possibility of a buffer overflow, you'd use strncpy, which takes a count as an additional parameter, so that no more than N characters are copied:
strncpy(string, "hello", MAX_LEN - 1);
The problem is that strncpy won't append the 0 terminator to the target if the source is longer than the destination; you'll have to do that yourself.
If you want to print the contents of a string, you'd use the %s conversion specifier and pass an expression that evaluates to the address of the first element of the string, like so:
char string[10] = "hello";
char *p = string;
printf("%s\n", "hello"); // "hello" is an array expression that decays to a pointer
printf("%s\n", string); // string is an array expression that decays to a pointer
printf("%s\n", p); // p is a pointer to the beginning of the string
Again, both "hello" and string have their types converted from "N-element array of char" to "pointer to char"; all printf sees is a pointer value.
Here's a handy table showing the types of various expressions involving arrays:
Declaration: T a[M];
Expression Type Decays to
---------- ---- ---------
a T [M] T *
&a T (*)[M]
*a T
a[i] T
&a[i] T *
Declaration: T a[M][N];
Expression Type Decays to
---------- ---- ---------
a T [M][N] T (*)[N]
&a T (*)[M][N]
*a T [N] T *
a[i] T [N] T *
&a[i] T (*)[N]
*a[i] T
a[i][j] T
&a[i][j] T *
Remember that the unary & operator will yield the address of its operand (provided the operand is an lvalue). That's why your char fnamn[] = &fname; declaration threw up the "invalid initializer" error; you're trying to initialize the contents of an array of char with a pointer value.
The unary * operator will yield the value of whatever its operand points to. If the operand isn't pointing anywhere meaningful (it's either NULL or doesn't correspond to a valid address), the behavior is undefined. If you're lucky, you'll get a segfault outright. If you're not lucky, you'll get weird runtime behavior.
Note that the expressions a and &a yield the same value (the address of the first element in the array), but their types are different. The first yields a simple pointer to T, where the second yields a pointer to an array of T. This matters when you're doing pointer arithmetic. For example, assume the following code:
int a[5] = {0,1,2,3,4};
int *p = a;
int (*pa)[5] = &a;
printf("p = %p, pa = %p\n", (void *) p, (void *) pa);
p++;
pa++;
printf("p = %p, pa = %p\n", (void *) p, (void *) pa);
For the first printf, the two pointer values are identical. Then we advance both pointers. p will be advanced by sizeof int bytes (i.e., it will point to the second element of the array). pa, OTOH, will be advanced by sizeof int [5] bytes, so that it will point to the first byte past the end of the array.