C语言指针与地址 Pointers & Addresses

C Pointers

Pointers are much used in C, partly because they are sometimes the only way to express a computation, and partly because they usually lead to more compact and efficient code than can be obtained in other ways.

Understand-c-pointers

This post aims at a clear illustration of pointers and how to use it.


As you are familiar with variable that represents a specific value set aside in a memory location, and memory location has is own address in machine’s memory, in which an array of consecutively numbered or addressed memory cells that may be manipulated individually or in contiguous groups.

MemoryAddressContent


1. Address is numeric

The address is a numerical number (often expressed in hexadecimal). let’s print it out:

int a = 10;
prinf("Address of a is %X\n", &a);   // EEBD089C

We use unary operator & to get the address of an object, here the program print out the address of a: EEBD089C.
We can of course save the address EEBD089C to a int type variable, say addrA:

int addrA = &a;   // may give a warnning from compiler if you do this!

Save a address in such way is trivial since you can do nothing on the object set aside in that address. So we need a pointer points to that address to not only save address but also manipulate the object, directly.


2. Pointers and Addresses

int *pa;		// declare `pa` as an int pointer
pa = &a;   		// pa now points to the address of a
*pa = 100;		// change the value of a to 100

The unary operator * is the indirection or dereferencing operator; when applied to a pointer, it accesses the object the pointer points to.

More example:

int x = 1, y = 2, z[10];
int *ip;  		// ip is a pointer to int
ip = &x;		// ip now points to x
y = *ip; 		// y is now 1
*ip = 0; 		// x is now 0
ip = &z[0]; 	// ip now points to z[0]

You should also note the implication that a pointer is constrained to point to a particular kind of object: every pointer points to a specific data type. (There is one exception: a ‘‘pointer to void’’ is used to hold any type of pointer but cannot be dereferenced itself.)

Apply self increment or decrement operator ++/-- on both sides are difference, as unary operators like * or ++/-- associate right to left:

++*ip;		// increment whatever ip points to, as you expected
(*ip)++;	// the parenthese is necessary!

3. Pointers and Function Arguments

Since C passes arguments to functions by value, there is no direct way for the called function to alter a variable in the calling function. For instance, you may want to swap two variables by writing a swap() function like this:

void swap(int a, int b)  /* WRONG */
{
	int tmp = a;
	a = b;
	b = tmp;
}

Because of call by value, swap has no way to access argument a and b in the routine that called it. The function above just swap copies of a and b.

The way to obtain the desired effect is for the calling program to pass pointers to the values to be changed:

void swap(int *a, int *b) 
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int a = 10, b = 100;
swap(&a, &b);
printf("a = %d, b = %d\n",a,b);  // a = 100, b = 10

Now by pointers arguments, the swap() works as you want. This is the scheme used by scanf() as well.


4. Pointers and Arrays

Pointers and arrays have strong relationships, strong enough that pointers and arrays should be discussed simulaneously:

int arr[10];
int *pa = &arr[0];
printf("Address of the 1st element: %p\n", pa);     // 0x7ffeeb185870
printf("Let's print `arr` directly: %p\n", arr);	// 0x7ffeeb185870

As you can see that arr itself is also a pointer points to the first element. Thus we can also write pa with arr interchangeably:

pa = &arr[0]
pa = arr
// they are the same thing!

Now pa points to the first element, by definition, pa + 1 points to the next element, pa + i points to the i elements after pa, and pa - i points to i elements before. Thus *(pa+1) refers to the points to arr[1].

These remarks are true regardless of the type or size of the variables in the array arr. The meaning of ‘‘adding 1 to a pointer,’’ and by extension, all pointer arithmetic, is that pa + 1 points to the next object, and pa + i points to the i-th object beyond pa.

Pointers and Arrays
Rather more surprising, at first sight, is the fact that arr[i] can also be written as pa[i]. In evaluating arr[i] or pa[i], C converts it to *(pa+i) immediately.

NOTE: There is one difference between an array name and a pointer that must be kept in mind.
A pointer is a variable, so pa = arr and pa++ are legal. But an array name is not a variable; constructions like arr = pa and arr++ are illegal.

When an array name is passed to a function, what is passed is the location of the initial element. Within the called function, this argument is a local variable, and so an array name parameter is a pointer, that is, a variable containing an address:

/* strlen: return length of string s */
 int strlen(char *s)
 {
	 int n;
	 for (n = 0; *s != ’\0, s++)
	 n++;
	 return n;
 }
 
 // All works
 strlen("hello, world"); 		// string constant 
 strlen(array); 				// char array[100];
 strlen(ptr);					// char *ptr;
 strlen(&array[50]);  			// or below
 strlen(array+50);				// are the same thing

As formal parameters in a function definition, char s[] and char *s are equivalent; we prefer the latter because it says more explicitly that the variable is a pointer.


5. Address Arithmetic

C is consistent and regular in its approach to address arithmetic; its integration of pointers, arrays, and address arithmetic is one of the strenghts of the language.

To illustrate it clear, I will use the simple example from The C Programming Language (written by K&RC)

Writing a simple storage allocator of characters. Two routines here:

  • alloc(n) returns a pointer to the start of n consecutive character positions
  • afree(p) releases the storage thus it can be re-used later

The standard library provides analogous functions called malloc and free

Idea:

  • An large character array allocbuf is privately shared by alloc and afree, and thus defined static in a separate source file containing alloc and afree.
  • We also need to track that how much of allocbuf has been used. Therefore we need a pointer allocp, that points to the next free element. When alloc is asked for n characters, it checks to see if there is enough room left in allocbuf:
    • If it has enough memory, alloc returns the current value of allocp and increment it by n to point to the next free area.
    • If it doesn’t, return 0
  • afree(p) is simple, merely sets allocp to p if p is inside allocbuf
  • This example manage storage like a stack, which means afree must called in the opposite order to the calls of alloc

Characters Allocator

/*
 * Storage Allocator: ch_alloc.c
 */
#define ALLOCSIZE 10000     /* size of available space */
static char allocbuf[ALLOCSIZE];  /* storage for alloc */
static char *allocp = allocbuf;  /* next free position */

char *alloc(int n)      /* return pointer to n characters */
{
	if (allocbuf + ALLOCSIZE - allocp >= n) {  /* it fits */
		allocp += n;
		return allocp - n; /* old p */
	} else 		 /* not enough room */
		return 0;
}

void afree(char *p)  /* free storage pointed to by p */
{
	if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
		allocp = p;
}

Let’s read off some information about pointers in the code above:

  1. pointer is a variable, so we can declare it static as normal variable does:

    static char *allop = allocbuf
    

    initializeallop to the first element of allocbuf, which could also have writen

    static char *allop = &allocbuf[0]
    

    since the array name is the address of the zero-th element.

  2. allocbuf + ALLOCSIZE - allocp >= n checks if there’s enough space of n consecutive characters. If not, return 0 as a signal of no space left.

    NOTE: C guarantees that 0 is never a valid address for data. The const 0 can be assigned to or compared with pointers. The symbolic constant NULL is often used in palce of 0, as it’s more clear this is a special pointer.

  3. The above arithmetic expression also shows the fact that if two pointers p and q point to members of the same array, then relations like ==, !=, <, >=, etc., work properly, like q < q.

  4. We have already observed that a pointer p can be added or subtracted to an integer n, which means the result pointer will point to n elements beyond or before the one that p currently points to:

    p + n  	// n elements beyond p
    

    This is true regardless of the type of object p points to, as n will be scaled according to the size of the object p points to. If an int is 4 bytes, it’ll be scaled by 4.

Valid Pointer Operations:

  • Assigning pointer of the same type
  • Adding / subtracting a pointer with an integer
  • Comparing two pointers of the same array
  • Assigning / comparing to 0 or NULL

6. Character Pointers

Constructions like "Hello, world!" is a string constant in C and we can easily store the string in a char [] array:

char [] hi = "Hello";

But one thing needs special attention that the array is terminated with a null character '\0', so that programs can find the end of the string, which means the above code is the same as:

char [] hi = {'H', 'e', 'l', 'l', 'o', '\0'};

We know that an array name itself is also an address of the first element, such that we can also write the pointer’s form interchangeablly as the array form above as:

char *phi = "Hello";

This form is usually used as function arguments, e.g. printf.

NOTE: Since phi is a pointer points to the character array, it is not a string copy, and then could be changed to point to another address somewhere.

To make it more clear, see the example below. Two versions of function strcpy, adapted from standard library <string.h>:

/* strcpy: copy t to s; array subscript version */
void strcpy(char *s, char *t)
{
	int i = 0;
	while ((s[i] = t[i]) != '\0')
		i++;
}

For contrast, here is a version of strcpy with pointers:

/* strcpy: copy t to s; pointer version */
void strcpy(char *s, char *t)
{
	while ((*s++ = *t++) != '\0'// a comparison against '\0' is redundant, could also written as
		;						   //     while (*s++ = *t++) ;
}

**NOTE: *t++ is the character that t pointed to before t was incremented; the postfix ++ doesn’t change t until after this character has been fetched.

Because we take the advantage of the feature that function arguments passed by value, strcpy uses s and t in any way it pleases. Some excercies in this section are worth doing…


Exercise 5-3. Write a pointer version of the function strcat that we showed in strcat(s,t) copies the string t to the end of s.

/* strcat(s, t): concatenate t to end of s; s must be big enough */
void strcat(char *s, char *t)
{
	while (*s)				// find the end of s
		++s;
	while (*s++ = *t++)		// concatenate t to s
		;
}


Exercise 5-4. Write the function strend(s,t), which returns 1 if the string t occurs at the end of the string s, and 0 otherwise.

/* strend(s, t): returns 1 if s ends with t, otherwise returns 0 */
int strend(char *s, char *t) 
{
	// find the end of s
	while (*s) 
		++s;   	
	
	// find the end of t while count its length
	int len;
	for (len = 0; *t != '\0'; ++t, ++len)  
		;  		
 	
 	// compare from end towards to begin
 	while (len > 0 && *--t == *--s) 
 		--len;	
 	
 	// return the result
	if (len == 0)   
		return 1;
	else
		return 0;
}

NOTE: Since ++ and -- are either postfix or prefix operators, combination like *--t makes sense, although less frequently occur.
Standard idiom for pushing and poping a stack:

*p++ = val;	 	// push val onto stack
val = *--p; 	// pop top of stack into val

7. Pointer Arrays

to be continued…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值