⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 begpoint.txt

📁 有关指针的使用问题
💻 TXT
📖 第 1 页 / 共 3 页
字号:

    my_array = ptr;


    The reason is that the while ptr is a variable, my_array is a

constant.  That is, the location at which the first element of

my_array will be stored cannot be changed once my_array[] has

been declared.


Modify the example program above by changing


    ptr = &my_array[0];     to     ptr = my_array;


and run it again to verify the results are identical.


    Now, let's delve a little further into the difference between

the names "ptr" and "my_array" as used above.  We said that

my_array is a constant pointer.  What do we mean by that?  Well,

to understand the term "constant" in this sense, let's go back to

our definition of the term "variable".  When we define a variable

we set aside a spot in memory to hold the value of the

appropriate type.  Once that is done the name of the variable can

be interpreted in one of two ways.  When used on the left side of

the assignment operator, the compiler interprets it as the memory

location to which to move that which lies on the right side of

the assignment operator.  But, when used on the right side of the

assignment operator, the name of a variable is interpreted to

mean the contents stored at that memory address set aside to hold

the value of that variable.


    With that in mind, let's now consider the simplest of

constants, as in:


    int i, k;

    i = 2;


    Here, while "i" is a variable and then occupies space in the

data portion of memory, "2" is a constant and, as such, instead

of setting aside memory in the data segment, it is imbedded

directly in the code segment of memory.  That is, while writing

something like k = i;  tells the compiler to create code which at

run time will look at memory location &i to determine the value

to be moved to k, code created by  i = 2;  simply puts the '2' in

the code and there is no referencing of the data segment.


    Similarly, in the above, since "my_array" is a constant, once

the compiler establishes where the array itself is to be stored,

it "knows" the address of my_array[0] and on seeing:


    ptr = my_array;


it simply uses this address as a constant in the code segment and

there is no referencing of the data segment beyond that.


    Well, that's a lot of technical stuff to digest and I don't

expect a beginner to understand all of it on first reading.  With

time and experimentation you will want to come back and re-read

the first 2 chapters.  But for now, let's move on to the

relationship between pointers, character arrays, and strings.


==================================================================

CHAPTER 3:  Pointers and Strings


    The study of strings is useful to further tie in the

relationship between pointers and arrays.  It also makes it easy

to illustrate how some of the standard C string functions can be

implemented. Finally it illustrates how and when pointers can and

should be passed to functions.


    In C, strings are arrays of characters.  This is not

necessarily true in other languages.  In Pascal or (most versions

of) Basic, strings are treated differently from arrays.  To start

off our discussion we will write some code which, while preferred

for illustrative purposes, you would probably never write in an

actual program.  Consider, for example:


    char my_string[40];


    my_string[0] = 'T';

    my_string[1] = 'e';

    my_string[2] = 'd':

    my_string[3] = '\0';


    While one would never build a string like this, the end

result is a string in that it is an array of characters

_terminated_with_a_nul_character_.  By definition, in C, a string

is an array of characters terminated with the nul character. Note

that "nul" is _not_ the same as "NULL".  The nul refers to a zero

as is defined by the escape sequence '\0'.  That is it occupies

one byte of memory.  The NULL, on the other hand, is the value of

an uninitialized pointer and pointers require more than one byte

of storage.  NULL is #defined in a header file in your C

compiler, nul may not be #defined at all.


    Since writing the above code would be very time consuming, C

permits two alternate ways of achieving the same thing.  First,

one might write:


    char my_string[40] = {'T', 'e', 'd', '\0',};


    But this also takes more typing than is convenient.  So, C

permits:


    char my_string[40] = "Ted";


    When the double quotes are used, instead of the single quotes

as was done in the previous examples, the nul character ( '\0' )

is automatically appended to the end of the string.


    In all of the above cases, the same thing happens.  The

compiler sets aside an contiguous block of memory 40 bytes long

to hold characters and initialized it such that the first 4

characters are Ted\0.


    Now, consider the following program:


------------------program 3.1-------------------------------------

#include <stdio.h>


char strA[80] = "A string to be used for demonstration purposes";

char strB[80];


int main(void)

{

   char *pA;     /* a pointer to type character */

   char *pB;     /* another pointer to type character */

   puts(strA);   /* show string A */

   pA = strA;    /* point pA at string A */

   puts(pA);     /* show what pA is pointing to */

   pB = strB;    /* point pB at string B */

   putchar('\n');       /* move down one line on the screen */

   while(*pA != '\0')   /* line A (see text) */

   {

     *pB++ = *pA++;     /* line B (see text) */

   }

   *pB = '\0';          /* line C (see text) */

   puts(strB);          /* show strB on screen */

   return 0;

}

--------- end program 3.1 -------------------------------------


    In the above we start out by defining two character arrays of

80 characters each.  Since these are globally defined, they are

initialized to all '\0's first.  Then, strA has the first 42

characters initialized to the string in quotes.


    Now, moving into the code, we define two character pointers

and show the string on the screen.  We then "point" the ponter pA

at strA.  That is, by means of the assignment statement we copy

the address of strA[0] into our variable pA.  We now use puts()

to show that which is pointed to by pA on the screen.  Consider

here that the function prototype for puts() is:


    int puts(const char *s);


    For the moment, ignore the "const".  The parameter passed to

puts is a pointer, that is the _value_ of a pointer (since all

parameters in C are passed by value), and the value of a pointer

is the address to which it points, or, simply, an address.  Thus

when we write:


    puts(strA);        as we have seen, we are passing the


address of strA[0].  Similarly, when we write:


    puts(pA);          we are passing the same address, since


we have set pA = strA;


    Given that, follow the code down to the while() statement on

line A.  Line A states:


    While the character pointed to by pA (i.e. *pA) is not a nul

character (i.e. the terminating '\0'), do the following:


    line B states:  copy the character pointed to by pA to the

space pointed to by pB, then increment pA so it points to the

next character and pB so it points to the next space.


    Note that when we have copied the last character, pA now

points to the terminating nul character and the loop ends.

However, we have not copied the nul character.  And, by

definition a string in C _must_ be nul terminated.  So, we add

the nul character with line C.


    It is very educational to run this program with your debugger

while watching strA, strB, pA and pB and single stepping through

the program.  It is even more educational if instead of simply

defining strB[] as has been done above, initialize it also with

something like:


 strB[80] = "12345678901234567890123456789012345678901234567890"


where the number of digits used is greater than the length of

strA and then repeat the single stepping procedure while watching

the above variables.  Give these things a try!


    Of course, what the above program illustrates is a simple way

of copying a string.  After playing with the above until you have

a good understanding of what is happening, we can proceed to

creating our own replacement for the standard strcpy() that comes

with C.  It might look like:


   char *my_strcpy(char *destination, char *source)

   {

        char *p = destination

        while (*source != '\0')

        {

           *p++ = *source++;

        }

        *p = '\0';

        return destination.

   }


    In this case, I have followed the practice used in the

standard routine of returning a pointer to the destination.


    Again, the function is designed to accept the values of two

character pointers, i.e. addresses, and thus in the previous

program we could write:


int main(void)

{

  my_strcpy(strB, strA);

  puts(strB);

}


    I have deviated slightly from the form used in standard C

which would have the prototype:


    char *my_strcpy(char *destination, const char *source);


    Here the "const" modifier is used to assure the user that the

function will not modify the contents pointed to by the source

pointer.  You can prove this by modifying the function above, and

its prototype, to include the "const" modifier as shown.  Then,

within the function you can add a statement which attempts to

change the contents of that which is pointed to by source, such

as:


    *source = 'X';


which would normally change the first character of the string to

an X.  The const modifier should cause your compiler to catch

this as an error.  Try it and see.


    Now, let's consider some of the things the above examples

have shown us.  First off, consider the fact that *ptr++ is to be

interpreted as returning the value pointed to by ptr and then

incrementing the pointer value.  On the other hand, note that

this has to do with the precedence of the operators.  Were we to

write (*ptr)++ we would increment, not the pointer, but that

which the pointer points to!  i.e. if used on the first character

of the above example string the 'T' would be incremented to a

'U'.  You can write some simple example code to illustrate this.


    Recall again that a string is nothing more than an array

of characters.  What we have done above is deal with copying

an array.  It happens to be an array of characters but the

technique could be applied to an array of integers, doubles,

etc.  In those cases, however, we would not be dealing with

strings and hence the end of the array would not be

_automatically_ marked with a special value like the nul

character.  We could implement a version that relied on a

special value to identify the end. For example, we could

copy an array of postive integers by marking the end with a

negative integer.  On the other hand, it is more usual that

when we write a function to copy an array of items other

than strings we pass the function the number of items to be

copied as well as the address of the array, e.g. something

like the following prototype might indicate:


    void int_copy(int *ptrA, int *ptrB, int nbr);


where nbr is the number of integers to be copied.  You might want

to play with this idea and create an array of integers and see if

you can write the function int_copy() and make it work.


    Note that this permits using functions to manipulate very

large arrays.  For example, if we have an array of 5000 integers

that we want to manipulate with a function, we need only pass to

that function the address of the array (and any auxiliary

information such as nbr above, depending on what we are doing).

The array itself does _not_ get passed, i.e. the whole array is

not copied and put on the stack before calling the function, only

its address is sent.


    Note that this is different from passing, say an integer, to

a function.  When we pass an integer we make a copy of the

integer, i.e. get its value and put it on the stack.  Within the

function any manipulation of the value passed can in no way

effect the original integer.  But, with arrays and pointers we

can pass the address of the variable and hence manipulate the

values of of the original variables.


==================================================================

CHAPTER 4: More on Strings


    Well, we have progressed quite aways in a short time!  Let's

back up a little and look at what was done in Chapter 3 on

copying of strings but in a different light.  Consider the

following function:


   char *my_strcpy(char dest[], char source[])

   {

        int i = 0;


        while (source[i] != '\0')

        {

           dest[i] = source[i];

           i++;

        }

        dest[i] = '\0';

        return dest;

   }


    Recall that strings are arrays of characters.  Here we have

chosen to use array notation instead of pointer notation to do

the actual copying.  The results are the same, i.e. the string

gets copied using this notation just as accurately as it did

before.  This raises some interesting points which we will

discuss.


    Since parameters are passed by value, in both the passing of

a character pointer or the name of the array as above, what

actually gets passed is the address of the first element of each

array.  Thus, the numerical value of the parameter passed is the

same whether we use a character pointer or an array name as a

parameter.  This would tend to imply that somehow:


        source[i]  is the same as  *(p+i);


In fact, this is true, i.e wherever one writes   a[i]  it can be

replaced with  *(a + i) without any problems.  In fact, the

compiler will create the same code in either case.   Now, looking

at this last expression, part of it..  (a + i)  is a simple

addition using the + operator and the rules of c state that such

an expression is commutative.  That is   (a + i) is identical to

(i + a).  Thus we could write *(i + a) just as easily as

*(a + i).


    But *(i + a) could have come from i[a] !  From all of this

comes the curious truth that if:


    char a[20];

    int i;


    writing    a[3] = 'x';   is the same as writing


               3[a] = 'x';


    Try it!  Set up an array of characters, integers or longs,

etc. and assigned the 3rd or 4th element a value using the

conventional approach and then print out that value to be sure

you have that working.  Then reverse the array notation as I have

done above.  A good compiler will not balk and the results will

be identical.   A curiosity... nothing more!


    Now, looking at our function above, when we write:


        dest[i] = source[i];


    this gets interpreted by C to read:


        *(dest + i) = *(source + i);


    But, this takes 2 additions for each value taken on by i.

Additions, generally speaking, take more time than

incrementations (such as those done using the ++ operator as in

i++).  This may not be true in modern optimizing compilers, but

one can never be sure.  Thus, the pointer version may be a bit

faster than the array version.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -