I'm trying to make a function that changes a char array from the main function, that's what I'm trying to do:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void change(char *a);
int main()
{
char a[] = "hello";
printf("\na = %s", a);
change(a);
printf("%\na = %s", a);
getch();
}
void change(char *a)
{
a = "goodbye";
}
Several problems with this code, but first we need to take a step back and talk about how arrays are handled in C.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
In the declaration
char a[] = "hello";
"hello" is a string literal, which has type "6-element array of char" (5 characters plus the 0 terminator). Since it is being used to initialize the array a in a declaration, the rule above doesn't apply; instead, the size of a is set to be the same as the size of the literal (6), and the contents of the string literal are copied to the array.
When you call change from main as
change(a);
the expression a has type "6-element array of char". Since it is neither a string literal nor the operand of the sizeof or unary & operators, that expression will be converted to type "pointer to char", and the value of the expression will be the address of the first element of the aray. Hence why the change function is declared as
void change(char *a);
In this context, a is simply a pointer. When you write
a = "goodbye";
the string literal "goodbye" is not being used in an initializer, and it's not the operand of the sizeof or unary & operators, so the expression is converted to type "pointer to char", and the value of the expression is the address of the first character. So what happens here is that you're copying the address of the string literal "goodbye" to a. This overwrites the value in a, but this a is not the same object in memory as the array a in main, so any changes to it are not reflected in main.
If you want to update the contents of an array, you will need to use the library functions strcpy/strncpy (for 0-terminated strings) or memcpy (for everything else), or update each element explicitly (a[0]='g'; a[1]='o'; a[2]='o';, etc).
To update the contents of a, you'd use
strcpy( a, "goodbye" );
Except...
a is only large enough to hold 5 characters plus a 0 terminator; "goodbye" is 7 characters plus the 0 terminator; it's two characters larger than what a is capable of storing. C will happliy let you perform the operation and trash the bytes immediately following a, which may lead to any number of problems (buffer overruns such as this are a classic malware exploit). You have a couple of choices at this juncture:
First, you could declare a to be large enough to handle either string:
#define MAX_LEN 10
...
char a[MAX_LEN] = "hello";
Second, you could limit the size of the string copied to a:
void change( char *a, size_t size )
{
strncpy( a, "goodbye", size - 1 );
a[size - 1] = 0;
}
Note that you will need to pass the number of elements a can store as a separate parameter when you call change; there's no way to tell from a pointer how big the array it points to is:
change( a, sizeof a / sizeof *a ); // although in this case, sizeof a would be
// sufficient.
The main problem is that you are sending a copy of the char pointer a by doing this:
void change(char *a)
{
a = "goodbye";
}
if you want to change a value in another function you should do this:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void change(char **a);
int main()
{
char *a = "hello";
printf("\na = %s", a);
change(&a);
printf("%\na = %s", a);
getch();
}
void change(char **a)
{
*a = "goodbye";
}
I changed the function, now it works, this way:
void change(char *a)
{
strcpy(a, "goodbye");
}
Related
I'm new to the C language.
While studying char arrays, I have a question.
I know a pointer to an array is the address of first element of the array. Below code is perfect:
char* c1 = "test"
printf("%s\n",c1); =>[output] "test"
But I thought that c1 is the address of the letter "t," such as "00x1928." So, *c1 is a backreference to c1:
char* c1 = "test"
printf("%s\n",*c1) => error!
Why is this code an error?
*c1 dereferences the pointer c1, which is declared to point to char. Therefore, *c1 is a single char, which needs to be printed using %c instead of %s. To print multiple characters, you need %s, for which you need a pointer such as c1, but not an individual char such as *c1.
String literals like "test" are constant; you can't overwrite them. To prevent accidental overwriting, always declare pointers to string literals as const:
const char* c1 = "test"
printf("%s\n",c1);
If you enable compiler warnings (always a good idea, especially when learning), you should get a warning for your original code.
I know a pointer to an array is the address of first element of the
array. Below code is perfect:
Yes a pointer to an array contains the address of first element of the array. However you are determining its type incorrectly.
Let's consider the following declaration of an array
char s[] = "test";
then a pointer to the array is defined the following way
char ( *p )[5] = &s;
Now indeed there is declared a pointer to an array of the type char[5] because the array s has five elements (including the terminating zero of the string literal with which the array was initialized).
So dereferencing the pointer you will get lvalue of the array.
Consider the following demonstrative program
#include <stdio.h>
int main(void)
{
char s[] = "test";
char ( *p )[5] = &s;
printf( "The size of the pointed array is %zu\n", sizeof( *p ) );
return 0;
}
The program output is
The size of the pointed array is 5
What you mean is that arrays with rare exceptions are converted to pointers to their first elements.
For example
#include <stdio.h>
int main(void)
{
char s[] = "test";
printf( "The size of the pointed first alement of the array is %zu\n"
"and the pointed element is '%c'\n", sizeof( *s ), *s );
return 0;
}
The program output is
The size of the pointed first alement of the array is 1
and the pointed element is 't'
That is in this expression *s the array designator is implicitly converted to pointer to its first element. Dereferencing the pointer you get the first element of the array.
To create a pointer to the first element of an array you can write for example
char s[] = "test";
char *p = s;
Again the array s used as an initializer of the pointer is implicitly converted to pointer to its first element. That is the expression s used as an initializer has the type char *.
Please read the comments in the program below :
#include<stdio.h>
void test(char c[])
{
c=c+2; //why does this work ?
c--;
printf("%c",*c);
}
int main()
{
char ch[5]={'p','o','u','r'};
//ch = ch+2; //this is definitely not allowed on array names as they are not pointers
test(ch);
return 0;
}
OUTPUT
o
You should keep in mind that the name of the array "decays" to a pointer to its first element. This means that test(ch); is equivalent to test(&ch[0]);.
Also, void test(char c[]) is nothing but void test(char* c), a pointer to a character. Pointers can be incremented or decremented which is why c = c + 2 and c-- compiles just fine.
Array designators are immutable lvalues. That is you may not change an array designator such a way that ir would define another array.
Consider array designators as named memory extents.
As for your example then this function declaration
void test(char c[]);
is adjusted by the compiler the following way
void test(char *c);
that is a parameter having an array type is adjusted by the compiler to pointer. Thus for example these function declarations
void test(char c[100]);
void test(char c[10]);
void test(char c[1]);
void test(char c[]);
are equivalent and declare this one function
void test(char *c);
You nay include all these declarations in your program though they will be redundant.
For example
#include <stdio.h>
void test(char c[100]);
void test(char c[10]);
void test(char c[1]);
void test(char c[]);
void test( char *c)
{
c=c+2;
c--;
printf("%c",*c);
}
int main( void )
{
char ch[5]={'p','o','u','r'};
test(ch);
}
To make it evident consider the following program
#include <stdio.h>
void test( char c[] )
{
printf( "sizeof( c ) = %zu\n", sizeof( c ) );
}
int main( void )
{
char ch[5]={'p','o','u','r'};
test( ch );
printf( "sizeof( ch ) = %zu\n", sizeof( ch ) );
}
Its output is
sizeof( c ) = 8
sizeof( ch ) = 5
That is within the function sizeof( c ) is equal to the size of a pointer (in used system it is equal to 8). While in main sizeof( ch ) is the size of the array.
When you pass an array to such a function then the array designator is implicitly converted to pointer to its first element. So these calls
test( ch );
test( &ch[0] );
are equivalent.
This means that within the function you deal with a pointer and you can change the value of the pointer using the pointer arithmetic.
When an array is passed as function argument (among other cases), it decays to a pointer to the first element, and the function parameter which receives it is local to the function scope.
Quoting C11, chapter §6.3.2.1
Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. [...]
So, in your case, inside void test(char c[]) function call, c is just another pointer, which points to the first element of the array. Normal pointer arithmetic can be performed on that pointer.
In other words,
void test(char c[]) { //....
is same as
void test(char *c) { //....
So, you case is something similar to
int main(void) //correcting the definition
{
char ch[5]={'p','o','u','r'};
//ch = ch+2; //this is definitely not allowed on array names as they are not pointers
char *c = &ch[0]; // this is what happens when you pass the array as function argument.
c = c + 2; // see, this is possible.
test(ch);
return 0;
}
In declarations of function parameters, an array declaration is automatically adjusted to be a pointer declaration, per C 2018 6.7.6.3 7:
A declaration of a parameter as “array of type” shall be adjusted to “qualified pointer to type”,…
Thus void test(char c[]) is effectively void test(char *c).
In main, ch is an array, because it was declared with char ch[5]…, which is a normal declaration that is not adjusted. In test, c is a pointer.
When main calls test with test(ch), the argument ch is an expression. In an expression, an array is automatically converted to a pointer in most cases because C 2018 6.3.2 3 says:
Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type “array of type” is converted to an expression with type “pointer to type” that points to the initial element of the array object…
Thus, when an array is passed to a function with a parameter declared as an array, the array is converted to a pointer and is passed for a parameter that was adjusted to be a pointer.
Note that only an outer array is adjusted. If the function parameter declaration is int x[3][4], it is adjusted to be int (*x)[4], a pointer to an array of 4 int. Only the array that is the parameter (the array of 3 arrays of 4 int above) is adjusted; other types within its composition are not adjusted.
Aside
The C standard is not entirely clear about the effects of the adjustment. Using Apple LLVM 10.0.1 with clang-1001.0.46.4, the following program prints “Hello, world.”:
#include <stdio.h>
static void foo(int a[printf("Hello, world.\n")]) {}
int main(void) { foo(0); }
This shows the array declaration was not completely adjusted to be a pointer declaration, as the expression specifying the array size was retained but would not be present in a pointer declaration.
So I'm cruising through Learn C the Hard Way (just enrichment) - but one bonus problem I've come across is the following -
#include <stdio.h>
char * mystrcpy(char *, char *);
int main(void) {
char dest[100];
char src[] = "Test string\n";
char *p;
if (p = mystrcpy(dest,src)) {
printf("%s\n",p);
return(0);
}
else {
printf("null pointer received\n");
return(1);
}
}
/* mystrcpy: Copy a string from s to a buffer pointer to by d.
d = destination buffer
s = source string
return value = a pointer to the beginning of the string in the
destination buffer.
*/
char * mystrcpy(char *d, char *s) {
}
I know that *d and *s are both pointer variables, but I'm not exactly sure what that means, nor how to use them in the context of this problem. Any help or "pointers" (no pun intended) would be greatly appreciated.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be adjusted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
In the call to mystrcpy, the expressions src and dest "decay" from type "array of char" to "pointer to char", so what the function actually receives are the pointer values corresponding to the addresses of the first element of each array.
You can access the contents of each array by using the [] subscript operator on the pointer expressions1:
d[i] = s[i];
or, you can dereference each pointer directly:
*d = *s;
and adjust the value of each pointer
d++;
s++;
Note that these operations may be combined:
*d++ = *s++;
From there it's just a matter of determining where the source string ends.
The expression a[i] is defined as *(a + i); given the pointer value a, offset i elements (not bytes!) from that address and dereference the result. This is why the array conversion rule mentioned above exists in the first place.
My XCode (default compiler should be clang?) shows me on this code a warning:
Incompatible pointer types passing 'char *(*)[2]' to parameter of type 'char ***' (when calling func)
void func (char ***arrayref, register size_t size) {
/// ...
}
int main()
{
char *array[2] = {"string", "value"};
func(&array, 2);
}
while this code is no problem (=no warning):
void func (char **arrayref, register size_t size) {
/// ...
}
int main()
{
char *array[2] = {"string", "value"};
func(array, 2);
}
While this removes the warning
func((char***)&array, 2);
I still don't know why the first emits a warning, while the latter doesn't.
Also, when the first is a problem, I'd also expect that the first emits a warning like:
Incompatible pointer types passing 'char *[2]' to parameter of type 'char **'
Time for a brief refresher on array semantics.
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.
The expression array in your code has type "2-element array of char *", or char *[2]. When you pass it as an argument to func, as in
func( array, 2 );
the expression is converted to an expression of type "pointer to char *", or char **, which is the type your function is expecting, and the value of the expression is the address of the first element: array == &array[0]. This is why you don't get the warning for the second code snippet.
In the first snippet, the array is an operand of the unary & operator, so the conversion to a pointer type doesn't happen; instead, the type of the expression is "pointer to 2-element array of char *", or char *(*)[2], which is not compatible with char **. The address value is the same (the address of the first element of the array is the same as the address of the array itself), but the types don't match, hence the warning.
So why does this matter? A pointer is just an address, and all address are the same, right? Well, no. A pointer is an abstraction of an address, with associated type semantics. Pointers to different types don't have to have the same size or representation, and pointer arithmetic depends on the type of the pointed-to type.
For example, if I declare a pointer as char **p;, then the expression p++ will advance the pointer to point to the next object of type char *, or sizeof (char *) bytes from the current address. If p is declared as char *(*p)[2], however, the expression p++ will advance p to point to the next two-element array of char *, which is 2 * sizeof (char *) bytes from the current address.
char *array[2] = {"string", "value"};
is an array with 2 elements of char *.
Using array as an address results to a pointer to the first element, i. e. of type char **.
Using &array results to a pointer to the same place, but of type char *(*)[2] (not sure if the spelling is right).
This is not the same as a char *** - the representation in memory is completely different.
To be more verbose,
+++++++++++++++++++++++
+ array[0] + array[1] +
+++++++++++++++++++++++
this is the array.
char ** p1 = array; // is a pointer to the first element, which in turn is a pointer.
char *(*p2)[2] = &array; // is a pointer to the whole array. Same address, but different type, i. e. sizeof(*p1) != sizeof(*p2) and other differences.
char ***p3 = &p1; // Now, p1 is a different pointer variable which has an address itself which has type `char ***`.
Here's an example of how to do what you want and change what array points to:
char *array2[] = {"string", "NewValue"};
void func0 (char **arrayref, register size_t size) {
puts(arrayref[1]);
}
void func1 (char ***arrayref, register size_t size) {
puts(arrayref[0][1]);
*arrayref= (char **) array2;
}
int main()
{
char *array[] = {"string", "value"};
char **foo = array;
func0(foo, 2);
func1(&foo,2);
func0(foo, 2);
}
You have an array of type char *[2] i.e. array of 2 pointers to char. It is an array of fixed size with automatic storage duration. The only thing that your function can do with this kind of array is to either use its elements or to change them (it can not resize it or deallocate it... therefore it makes no sense to try to make it possible to change the array itself ~> in other words: you don't really need a pointer to this kind of array).
Here's a simple example:
void func (char *arrayref[2]) {
printf("%s", arrayref[1]);
arrayref[1] = "new value";
}
int main() { {
char *array[2] = {"string", "value"};
func(array);
printf(" -> %s", array[1]);
return 0;
}
or alternatively changing func to take an array of unspecified size making it usable with char *[X] for any X, not just 2 (in that case it makes sense already to pass the array's size):
void func (char *arrayref[], size_t size) {
if (size > 1) {
printf("%s", arrayref[1]);
arrayref[1] = "new value";
}
}
with one way or other, this program would output value -> new value.
If you need your function to be able to resize this array or affect the array itself in some other way, you should consider using dynamically-allocated array and passing in form of char**.
I'm trying to learn C now, I'm coming from Java and there is some stuff that is new to me.
I want to print a string and send an int and a string(char array) to another method. But I keep getting some errors that I don't know how to fix.
Would really appreciate if someone could take their time and explain to me what's wrong in my code. I'm quite disoriented at the moment with these pointers. When to use %s and %c when printing etc...
Code:
#include <stdio.h>
void main()
{
int k = 10;
char string;
char *sptr;
string = "hello!";
int *ptr;
sptr = &string;
ptr = &k;
printf("%s \n", &sptr);
printf("Sending pointer.\n");
sendptr(ptr, sptr);
}
And the errors.
test.c: In function ‘main’:
test.c:8:9: warning: assignment makes integer from pointer without a cast
test.c:15:2: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char **’
tezt.c: In function ‘sendptr’:
tezt.c:8:8: error: incompatible types when assigning to type ‘char[6]’ from type ‘char’
Thanks for your time! :)
First functions solved.
Second function i get this..
tezt.c: In function ‘sendptr’:
tezt.c:5:2: error: invalid initializer
#include <stdio.h>
void sendptr(int *test, char *fname)
{
char fnamn[] = &fname;
int pt;
pt = *test;
printf("%p \n", test);
printf("%d \n", pt);
printf("%s \n", fnamn);
}
char string;
string = "hello!";
First problem: you're declaring string as a single char, not as an array. Also, you can only initialize the array to a string literal in a single statement.
char string[] = "hello!";
Second problem: sptr is a pointer-to-char, so it has to point to the first element of your string. Either of these will do:
char *sptr = string;
char *sptr = &string[0];
Then, when printing the string, just pass sptr directly.
printf("%s \n", sptr);
EDIT for your next question.
char fnamn[] = &fname;
You're trying to assign a char** (pointer to pointer to char) to an array. That just won't work. If you want to copy the string pointed to by fname into fnamn then you need to use a function such as strncpy.
char fnamn[MAX_STRING_SIZE];
strncpy(fnamn, fname, MAX_STRING_SIZE);
Having said that, if you just want to print the string, then print fname directly without copying it into your array first.
Here's a corrected version of the program with some annotation:
#include <stdio.h>
int main(void) // int and (void) for standard mains.
{
int k = 10;
char *string; // a C string is a char array, you need a pointer to point to it
char *sptr;
int *ptr;
string = "hello!";
sptr = string;
ptr = &k;
printf("%s \n", sptr); // no &. The %s format expects a char*.
printf("Sending pointer.\n");
// sendptr(ptr, sptr); // don't know what this function is, ignoring
return 0;
}
In C language, the & operator means you want to use the address of the variable (ie & = "the address of the variable").
int an_integer=2; // an_integer is a memory part where you want to store 2 ;)
printf("%d", &an_integer); // here you will print the address of the memory part where an_integer is stored (not 2, more something like 2510849).
The * operator in a declaration of variable means that you want to have a pointer to a memory part, when using it in the code, it means the "the value contained at the address of"
int an_integer=2;
int *ptr_integer; // you declare a pointer to an integer
ptr_integer = &an_integer; // here you set the pointer ptr_integer to the address of an_integer
printf("%d", *ptr_integer); // here you print the value contained at the memory address stored in the ptr_integer
The [] operator means you want to store an array of something. In C, an array can be seen as a pointer to a memory space.
int an_integer[2]; // you declare an array of 2 integers
int *ptr_integer; // you declare a pointer to an integer
ptr_integer = (int *)an_integer; // here you set the value of the pointer to the address of the array, you have to cast it into an (int *) to avoid compilation warnings.
For a start, I would suggest changing:
char string;
to:
char *string;
It's pretty clear that you want the string variable to be a string rather than a single character.
In addition, you probably want to change the two lines:
sptr = &string;
printf("%s \n", &sptr);
to:
sptr = string;
printf("%s \n", sptr);
but you could equally well just pass string itself to printf.
As for the sendptr(ptr, sptr);, we can't help that much without knowing more details about it.
To fix your second function (from your edit), change:
char fnamn[] = &fname;
to:
char *fnamn = fname;
or just use fname directly. You don't have to make a copy of the pointer and the former is for things like:
char fnamn[] = "I am a string literal";
I thought it might be helpful to adding something about the difference between a char array and a pointer to a string.
In function1 below, the local variable stringPtr is a pointer to memory which contains the string "hello!". The memory containing this string will be located in a read-only section of the program. The compiler decides where to place the string "hello!" and ensures that your local variable is initialised with this memory address.
You can modify the pointer stringPtr and change it to point somewhere else. But you cannot modify the memory it points at.
Also, it is perfectly valid to use the array access notation stringPtr[2] even though it is a pointer.
In function2 the compiler will set aside 9 bytes of space on the stack for the local variable stringArray and it will ensure that this array is initialised with the string "Goodbye!". As this memory is on the stack you can modify the contents of the array.
#include <stdio.h>
void function1(void)
{
char *stringPtr = "hello!";
printf("The first char is %c\n", stringPtr[0]);
printf("The next char is %c\n", *(stringPtr+1));
// This would cause a segmentation fault, stringPtr points to read-only memory
// stringPtr[0] = 'H';
}
void function2(void)
{
char stringArray[] = "Goodbye!";
printf("The first char is %c\n", stringArray[0]);
}
int main(void)
{
function1();
function2();
return 0;
}
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.
#include <stdio.h>
void main()
{
int k = 10;
char string;
char *sptr;
sptr = "hello!";
int *ptr;
ptr = &k;
printf("%s \n", sptr);
printf("Sending pointer.\n");
sendptr(ptr, sptr);
}