Pointer in C/C++
2012-03-23 13:50
344 查看
Pointers
The source code to all code listings is available as a tarball and as a zip file. |
Using Variables
Essentially, the computer's memory is made up of bytes. Each byte hasa number,an address, associated with it.The picture below represents several bytes of a computer's memory. In the picture, addresses 924 thru 940 are shown.
![](http://hi.csdn.net/attachment/201203/23/0_1332480950sfUL.gif)
Try:
C Code Listing 1
#include <stdio.h> int main() { float fl=3.14; printf("%.2f\n", fl); return 0; }
[/code]
C++ Code Listing 1
#include <iostream> int main() { float fl=3.14; std::cout << fl << std::endl; return 0; }
[/code]
At line (4) in the program above, the computer reservesmemory for
fl. In our examples, we'll assumethat a
floatrequires 4 bytes. Depending on thecomputer's architecture, a
floatmay require 2,4, 8 or some other number
of bytes.
![](http://hi.csdn.net/attachment/201203/23/0_1332481001qyy3.gif)
When
flis used in line (5), two distinct steps occur:
The program finds and
grabs the address reserved for
fl--in this example 924.
The contents stored at that address are
retrieved
To generalize, whenever any variable is accessed,the above two distinct steps occur to retrieve the contentsof the variable.
The illustration that shows 3.14 in the computer's memory can be misleading. Looking at the diagram, it appears that "3" is stored in memory location924, "." is stored in memory location 925, "1" in 926, and "4" in 927. Keep in mind that the computer actually uses an algorithm to convert the floating point number 3.14 into a set of ones and zeros. Each byte holds 8 ones or zeros. So, our 4 byte floatis stored as 32 ones and zeros (8 per byte times 4 bytes). Regardless of whether the number is 3.14, or -273.15, the number is always stored in 4 bytes as a series of 32 ones and zeros. |
Separating the Steps
Two operators are provided that, when used, cause these two steps tooccur separately.operator | meaning | example |
---|---|---|
& | do only step 1 on a variable | &fl |
* | do step 2 on a number(address) | *some_num |
C Code Listing 2
#include <stdio.h> int main() { float fl=3.14; printf("fl's address=%u\n", (unsigned int) &fl); return 0; }
[/code]
C++ Code Listing 2
#include <iostream> int main() { float fl=3.14; std::cout << "fl's address=" << (unsigned int) &fl << std::endl; return 0; }
[/code]
On line (5) of the example, The
&operator is being usedon
fl. On line (5), only step 1 is being performed ona variable:
1. The program finds and grabs the address reserved for fl...
It is
fl's address that is printed to the screen.If the
&operator had not been placed in frontof
fl, then step 2 would have occurred as well,and 3.14 would have been printed to the screen.
The (unsigned int)phrase will be discussed later. It is there so that &addrwill print out as a non-negative number. It has been shown in gray to indicate that you must include it for the program to compile properly but that it is not relevant to this current discussion. |
C Code Listing 3
#include <stdio.h>
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
printf("fl's address=%u\n", addr);
return 0;
}
[/code]
C++ Code Listing 3
#include <iostream>
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
std::cout << "fl's address=" << addr << std::endl;
return 0;
}
[/code]
![](http://hi.csdn.net/attachment/201203/23/0_1332481104awX0.gif)
The above code shows that there is nothing magical about addresses.They are just simple numbers that can be stored in integer variables.
The unsignedkeyword at the start of line (5) simply means that the integer will not hold negative numbers. As before, the (unsigned int)phrase has been shown in gray. It must be included for the code to compile, but is not relevant to this discussion. It will be discussed later. |
*operator that retrieves the contents stored at an address:
C Code Listing 4
#include <stdio.h>
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
printf("fl's address=%u\n", addr);
printf("addr's contents=%.2f\n", * (float*) addr);
return 0;
}
[/code]
C++ Code Listing 4
#include <iostream>
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
std::cout << "fl's address=" << addr << std::endl;
std::cout << "addr's contents=" << * (float*) addr << std::endl;
return 0;
}
[/code]
In line (7), step 2 has been performed on a number:
2. The contents stored at that address [
addr] are retrieved.
In order to make line (7) work, a little "syntax sugar"had to be added for the program to compile. Like before,(float*)is shown ingray because it is not relevant to the current discussion.For the sake of this discussion, just read " *(float*)addr" as " *addr"(that is, ignore the stuff in gray). The code shown in graywill be discussed later. |
OK, But why do we need & and *
We have shown that 2 distinct steps occur when accessing a variable, and that we can make those steps occur separately. But why is this useful?To see why, let's first look at how functions work in C/C++. Try this code:
C Code Listing 5
#include <stdio.h>
void somefunc(float fvar)
{
fvar=99.9;
}
int main()
{float fl=3.14;
somefunc(fl);
printf("%.2f\n", fl);
return 0;
}
C++ Code Listing 5
#include <iostream>
void somefunc(float fvar)
{
fvar=99.9;
}
int main()
{float fl=3.14;
somefunc(fl);
std::cout << fl << std::endl;
return 0;
}
What prints out? 3.14? 99.9? It turns out that 3.14 prints out. The general term used to describe this behavior ispass by value. When
somefunc(fl)is called at line 9:
Execution jumps to line (2) to run the function
fvaris created as its own variable and
fl's value is copied into
fvar
![](http://hi.csdn.net/attachment/201203/23/0_1332481146B6mm.gif)
On line (4), 99.9 is assigned to fvar
![](http://hi.csdn.net/attachment/201203/23/0_1332481181CGAT.gif)
Now that the function is finished, execution resumes in
mainwhere it left off (line 10). The
flvariable is unchanged, 3.14 prints out.
We can circumvent this pass by value behavior and change valuespassed into functions by using the
&and
*operators.
C Code
#include <stdio.h>
void somefunc(unsigned int fptr)
{
*(float*)fptr=99.9;
}
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
somefunc(addr);
printf("%.2f\n", fl);
return 0;
}
[/code]
C++ Code
#include <iostream>
void somefunc(unsigned int fptr)
{
*(float*)fptr=99.9;
}
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
somefunc(addr);
std::cout << fl << std::endl;
return 0;
}
[/code]
Quite simply, the two steps that normally occur when accessing a variableare being separated to allow us to change the variable's value in adifferent function.
The floating point variable
flis created at line (9) and giventhe value 3.14
![](http://hi.csdn.net/attachment/201203/23/0_133248122766WO.gif)
The
&operator is used on
flat line (10) (do onlystep 1, get the address). The address is stored in the integer variable
addr.
![](http://hi.csdn.net/attachment/201203/23/0_13324812635iF6.gif)
The function
somefuncis called at line (11)and
fl's address is passed as an argument.
The function
somefuncbegins at line (2),
fptris created and
fl's address is copiedinto
fptr.
![](http://hi.csdn.net/attachment/201203/23/0_1332481296Osux.gif)
The
*operator is used on
fptrat line (4) -- do step 2, the contents stored in an address are retrieved. In this example, the contents at address 924 are retrieved.
The contents at address 924 are assigned the value
99.9.
![](http://hi.csdn.net/attachment/201203/23/0_1332481325pHlL.gif)
The function finishes. Control returns to line (12).
The contents of
flare printed to the screen.
Pointer Variables
Even though we have shown that an address is nothing more than a simpleinteger, the creators of the language were afraid we might confusevariables in our programs. We might confuse integers we intend to usefor program values (e.g. variables storing ages,measurements, counters,etc.) with integers we intend to use for holding the addresses of ourvariables.
The language creators decided the best way to
eliminate confusion was to create a different type of variable for holding addresses. A first attempt at this might have looked something like this:
...float fl=3.14;float Ptr addr = &fl;
...
[/code]
On line (3), here is how to describe the addr variable:
![](http://hi.csdn.net/attachment/201203/23/0_13324813645NSX.gif)
(A)
addris an integer. (B) However, it is a special integer designed to hold the address of a(C)
float
In the code above, line (3) is close to what the creators of the languagewanted except for one thing: using
Ptrwould requireintroducing another keyword into the language. If there is one thingthat all C instructors like to brag about, it is
how there are onlya very small number of keywords in the language. Well, using line (3)as shown above would mean adding
Ptras another keyword tothe language.
To avoid this threat to the very fabric of the universe, the creatorscast about for something already being used in the language that coulddo double duty as
Ptrshown above. What they came up withwas the following:
...float fl=3.14;float * addr = &fl;
...
[/code]
Even with the
*instead of
Ptr,
addris described the same way:
![](http://hi.csdn.net/attachment/201203/23/0_1332481396Lk3K.gif)
(A)
addris an integer. (B) However, it is a special integer designed to hold the address of a(C)
float
These variables are described this way, regardless of the type:
![](http://hi.csdn.net/attachment/201203/23/0_1332481424mlcl.gif)
(A)
addris an integer. (B) However, it is a special integer designed to hold the address of a(C)
char
![](http://hi.csdn.net/attachment/201203/23/0_1332481449X7sX.gif)
(A)
addris an integer. (B) However, it is a special integer designed to hold the address of an(C)
int
This "...special integer..." way of describing these variables is amouthful, so we shorten it and just say "addr is a float pointer" or"addr is a pointer to a float" (or char, or int, etc.).
Unfortunately, the language creators chose the
*characterto replace Ptr. The
*character is confusing because the
*character is also used to get the contents at an address("do step 2 on a number"). These two
uses of the
*character have
nothing to do with each other.
What is all that "syntax sugar" anyway? (Casting)
Let's take one last look at our original code that illustrates theutility of separating out steps 1 & 2.C Code Listing 7
#include <stdio.h>
void somefunc(unsigned int fptr)
{
*(float*)fptr=99.9;
}
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
somefunc(addr);
printf("%.2f\n", fl);
return 0;
}
[/code]
C++ Code Listing 7
#include <iostream>
void somefunc(unsigned int fptr)
{
*(float*)fptr=99.9;
}
int main()
{float fl=3.14;
unsigned int addr=(unsigned int) &fl;
somefunc(addr);
std::cout << fl << std::endl;
return 0;
}
[/code]
In nearly all of the code samples, you have been asked toignore certain bits of the code. These bits of code havealways appeared around those areas where we are either takingthe address of a variable or getting the contents at an address(doing step 1 or
step 2 on a variable)
Those bits of "syntax sugar" are there to keep the compilerfrom complaining. The first example of this in the aboveprogram is on line (10).
On line (10) we are taking the address of the floatingpoint number
fl("do only step 1 on a number").After we get that address, we store it in
addr.
Why would the compiler complain? Because when we assign theaddress of
flto
addr, the compilerdoes not expect
addrto be an
unsignedint. The compiler expects
addrto be a
float *.
That is,a special integer designedto hold the address of a float. To keep the compiler fromcomplaining, we tell the compiler to treat
&flasan
unsigned intrather than a
float *.
This "syntax sugar" that causes the compiler to treatvariables and expressions differently is calledcasting.The way a programmer describes line (10) is: "The address of
flis beingcast
intoan
unsigned intand assigned to
addr"
The other place casting occurs is on line (4). On line (4),we are getting the contents at an address ("do step 2 on anumber/address"). Why would the compiler complain? Because thecompiler should get the contents of the address of a float.The address of
our float is in stored in
fptr,which is an
unsigned int, not a
float*. We tell the compiler to treat
fptras the address of a floating point number by casting it into a
float *. Once we tell the
compiler this, we canget the contents at the address without complaint.
Putting it all together
From the previous section, you might be left with the impressionthat whenever you deal with addresses and pointers, there isa lot of casting. Not so. The only reason our examples uptill now have required casting is because we were storing ouraddressesin
unsigned intvariables. The languagedesigners want us to store addresses in the "special integer"variables, that is, the pointer variables they designed forjust such a purpose.
Once we replace our
unsigned intvariableswith these pointer variables, none of the castingis required:
C Code Listing 8
#include <stdio.h>
void somefunc(float* fptr)
{
*fptr=99.9;
}
int main()
{float fl=3.14;float* addr = &fl;
somefunc(addr);
printf("%.2f\n", fl);
return 0;
}
[/code]
C++ Code Listing 8
#include <iostream>
void somefunc(float* fptr)
{
*fptr=99.9;
}
int main()
{float fl=3.14;float* addr = &fl;
somefunc(addr);
std::cout << fl << std::endl;
return 0;
}
[/code]
On line (10), when we take the address of
flthe address is assigned to a variable designed to holdit. No casting is required.
When
addris passed to the function in line (11),
addris copied to
fptron line(2).
Line (2) shows that
fptris created as a floatpointer, that is a variable designed to hold the address ofa floating point number. As a result, no casting is neededon line (4) where the contents at the address are retrieved.
相关文章推荐
- The pointer in C++ /CLI
- Implementing a simple smart pointer in c++
- 'this' pointer in C++
- Type of 'this' pointer in C++
- Implementing a simple smart pointer in C++
- C++ 指针的偏移 The offset of a pointer in C++
- Virtual method and base-type pointer make polymorphism in C++
- ‘this’ pointer in C++
- Difference between pointer variable and reference variable in C++
- The differences between pointer and reference in C++
- Jump Tables via Function Pointer Arrays in C/C++
- Effective C++ Item 17 Store newed objects in smart pointer in standalone statements
- Implementing a simple smart pointer in C++
- The two-dimensional pointer operation in C++
- What are the differences between a pointer variable and a reference variable in C++?
- ‘this’ pointer in C++
- What are the differences between a pointer variable and a reference variable in C++?
- LeetCode 237. Delete Node in a Linked List 题解(C++)
- key word 'static' in c++/java
- You Can Program in C++: A Programmer's Introduction