Popular Posts

Thursday, July 16, 2009

Pointer

Pointers



A pointer is a variable that contains the address of another variable (either numeric or string, or a function) instead of a directly accessible value.

When declaring a pointer, you must include the type of the variable whose address this pointer will contain, so that the compiler knows how many bytes to read/write when it will access the value that this variable contains. To differentiate pointers from other variable types, a pointer is declared using an asterisk (*).

As an example, int *ptr declares a pointer to an integer. That is, ptr will hold the address of a variable of type integer. Since the compiler was told that the variable whose address ptr holds is an integer, it knows that it should read/write 4 bytes (the size of an integer is platform dependent, but it's usually 4 bytes.)

Knowing the length of the actual value this variable uses also allows you to move back and forth through a multi-byte value such as a string using the ++ and -- quantifiers:

char *mystr="My string";

printf("First character: %c\n",*mystr); //Alternatively, you can use *mystr++ to combine two lines in one
mystr++;
printf("Second character: %c\n",*mystr);

To initialize a pointer, you need to extract the address of a variable, using the & sign:

int *ptr=NULL; //It's a good habit to use the NULL macro to initialize a pointer right away
int someint;
ptr = &someint; //Now, ptr contains the address where someint lives in RAM, not its actual value

To access the value that the variable points to, you must dereference the pointer, that is, tell the compiler that you wish to go to the address that the pointer holds, and read/write the value that this memory cell holds. Here's an example:

someint = 16;
printf("Reading the value of someint directly: %d\n",someint);
printf("Reading the value of someint by dereferencing ptr: %d\n",*ptr);

Now, let's change the content of the variable by dereferencing ptr:

*ptr = 32; //Equivalent to someint=32;
printf("Reading the value of someint directly: %d\n",someint);
printf("Reading the value of someint by dereferenceing ptr: %d\n",*ptr);

The name of an array is actually the address of the first element:

int myarray[]={1,2,3};
int *arrptr;
arrptr = myarray; //You can also use &myarray[0];

printf("First element: %d\n",*arrptr);
printf("First element: %d\n",arrptr[0]);

A string is actually an array of characters that ends with a NUL character, ie. ASCII 0, is a pointer to the first character, and when a string is passed to a function, the function will work on the actual string since it receives the address of the array instead of a copy made when calling the function:

void myfunc(char arr[])
{
arr[0]='X';
}

int main(void)
{
char myarr[]="This is a string"; //could also use char *myarr="This is a string";
printf("Original string:%s\n",myarr);

myfunc(myarr);

printf("After myfunc: %s\n",myarr);
return 0;
}

Important: when using a char pointer to create a string, the string is read-only, ie. if you need to change the contents of the string, you should use char [] instead:

int main(void)
{
//BAD---------
char *myarr="This is a string";
//GPF because the string is immutable
*myarr='X';

//Good----------
char myarr[]="This is a string";
myarr[0]='X';
return 0;
}

Since a string created with the "char *" format is immutable, you could add the "const" prefix to get a compile-time error in case you changed the contents of this string:

//Without "const", the compiler may not say anything
const char *mystr="Before";

//no run-time crash, since compiler chocked on this
*mystr='X';

Note that it's OK to reuse the pointer to set it to a new string, though, since it's the memory space used to store the string itself that is immutable, not the pointer:

const char *mystr="Before";

mystr="After";
puts(mystr);

I read that GCC accepts the "-fwritable-strings" switch to allow a pointed string to be writable, but this is likely to be non-portable across compilers. A better alternative is to use malloc() and free() to allocate memory dynamically:

#define STR "This is a string"

void myfunc(char *arr)
{
//alternative: arr[0]='X';
*arr='X';
}

int main(void)
{
//Add 1 for terminating \0; calloc() sets string to \0, while malloc() doesn't
char *str = (char*) calloc(strlen(STR) + 1,sizeof(char));

strncpy(str,STR,strlen(STR));

printf("Original string:%s\n",str);
myfunc(str);
printf("After myfunc: %s\n",str);
free(str);
str = NULL; //Good practice

return 0;
}

No comments: