📄 m2_pointers.hlp
字号:
{smcl}
{* 25mar2005}{...}
{cmd:help m2 pointers}
{hline}
{* index pointers}{...}
{* index NULL tt}{...}
{* index dereferencing}{...}
{title:Title}
{p 4 4 2}
{hi:[M-2] pointers -- Pointers}
{title:Syntax}
{p 8 12 2}
{cmd:pointer}[{cmd:(}{it:totype}{cmd:)}]
[{it:orgtype}]
[{cmd:function}]
...
{p 4 4 2}
where {it:totype} is
[{it:eltype}] [{it:orgtype}] [{cmd:function}]
{p 4 4 2}
and where {it:eltype} and {it:orgtype} are
{it:eltype orgtype}
{hline 17} {hline 10}{cmd}
transmorphic matrix
numeric vector
real rowvector
complex colvector
string scalar{txt}
{cmd:pointer}[{cmd:(}{it:towhat}{cmd:)}] {hline 10}
{hline 17}
{p 4 4 2}
{cmd:pointer}[{cmd:(}{it:totype}{cmd:)}]
[{it:orgtype}]
can be used in front of declarations, be they function declarations, argument
declarations, or variable definitions.
{title:Description}
{p 4 4 2}
Pointers are objects that contain the addresses of other objects.
The {cmd:*} prefix operator obtains the contents of an address.
Thus if {it:p} is a pointer, {cmd:*}{it:p} refers to the contents of
the object to which {it:p} points.
Pointers are an advanced programming concept. Most programs,
including involved and complicated ones, can be written without them.
{p 4 4 2}
In Mata, pointers are commonly used to
{p 8 12 2}
1. put a collection of objects under a single name
{p 8 12 2}
2. pass functions to functions.
{p 4 4 2}
One need not understand everything about pointers merely to pass
functions to functions; see {bf:{help m2_ftof:[M-2] ftof}}.
{title:Remarks}
{p 4 4 2}
Remarks are presented under the headings
{bf:What is a pointer?}
{bf:Pointers to variables}
{bf:Pointers to expressions}
{bf:Pointers to functions}
{bf:Pointers to pointers}
{bf:Pointer arrays}
{bf:Mixed pointer arrays}
{bf:Definition of NULL}
{bf:Use of parentheses}
{bf:Pointer arithmetic}
{bf:Listing pointers}
{bf:Declaration of pointers}
{bf:Use of pointers to collect objects}
{bf:Efficiency}
{title:What is a pointer?}
{p 4 4 2}
A pointer is the address of a variable or a function. For instance, say
variable {it:X} contains a matrix. Another variable {it:p} might contain
137,799,016, and if 137,799,016 were the address at which {it:X} were stored,
then {it:p} would be said to point to {it:X}. Addresses are seldom written in
base 10, so rather than saying {it:p} contains 137,799,016, we would be
more likely to say that {it:p} contains 0x836a568, which is the way we write
numbers in base 16. Regardless of how we write addresses, however, {it:p}
contains a number and that number corresponds to the address of another
variable.
{p 4 4 2}
In our program, if we refer to {it:p}, we are referring to {it:p}'s contents,
the number 0x836a568. The monadic operator {cmd:*} is defined as "refer to the
contents of the address" or "dereference": {cmd:*}{it:p} means {it:X}. We
could code {cmd:Y = *p} or {cmd:Y = X}, and either way, we would obtain the
same result. In our program, we could refer to
{it:X}{cmd:[}{it:i}{cmd:,}{it:j}{cmd:]} or
{cmd:(*}{it:p}{cmd:)[}{it:i}{cmd:,}{it:j}{cmd:]}, and either way, we would
obtain the {it:i}, {it:j} element of {it:X}.
{p 4 4 2}
The monadic operator {cmd:&} is how we put addresses into {it:p}. To load
{it:p} with the address of {it:X}, we code {it:p} {cmd:=} {cmd:&}{it:X}.
{p 4 4 2}
The special address 0 (zero, written in hexadecimal as 0x0), also known as
{cmd:NULL}, is how we record that a pointer variable points to nothing. A
pointer variable contains NULL or it contains the address of another
variable.
{p 4 4 2}
Or it contains the address of a function. Say that {it:p} contains
0x836a568 and that 0x836a568, rather than being the address of matrix {it:X},
is the address of function {it:f}(). To get the address of {it:f}() into
{it:p}, just as we coded {it:p} {cmd:=} {cmd:&}{it:X} previously, we code
{it:p} {cmd:=} {cmd:&}{it:f}{cmd:()}. The {cmd:()} at the end tells {cmd:&}
that we want the address of a function. We code {cmd:()} on the
end regardless of the number of arguments {it:f}() requires because we are
not executing {it:f}(), we are just obtaining its address.
{p 4 4 2}
To execute the function at
0x836a568 -- now we will assume that {it:f}() takes two arguments and
call them {it:i} and {it:j} --
we code {cmd:(*}{it:p}{cmd:)(}{it:i}{cmd:,}{it:j}{cmd:)}
just as we coded
{cmd:(*}{it:p}{cmd:)[}{it:i}{cmd:,}{it:j}{cmd:]}
when {it:p} contained the address of matrix {it:X}.
{title:Pointers to variables}
{p 4 4 2}
To create a pointer {it:p} to a variable, you code
{it:p} {cmd:=} {cmd:&}{it:varname}
{p 4 4 2}
For instance, if {it:X} is a matrix,
{it:p} {cmd:=} {cmd:&}{it:X}
{p 4 4 2}
stores in {it:p} the address of {it:X}. Subsequently, referring to
{cmd:*}{it:p} and referring to {it:X} amount to the same thing.
That is, if {it:X} contained a 3 {it:x} 3 identity matrix and you
coded
{cmd:*}{it:p}{cmd: = Hilbert(4)}
{p 4 4 2}
then after that you would find that {it:X} contained the 4 {it:x} 4 Hilbert
matrix. {it:X} and {cmd:*}{it:p} are the same matrix.
{p 4 4 2}
If {it:X} contained a 3 {it:x} 3 identity matrix and you coded
{cmd:(*}{it:p}{cmd:)[2,3] = 4}
{p 4 4 2}
you would then find {it:X}{cmd:[2,3]} equal to 4.
{p 4 4 2}
You cannot, however, point to the interior of objects. That is, you cannot
code
{it:p} {cmd:=} {cmd:&}{it:X}{cmd:[2,3]}
{p 4 4 2}
and get a pointer that is equivalent to {it:X}{cmd:[2,3]} in the
sense that if you later coded {cmd:*}{it:p}{cmd:=2}, you would see the change
reflected in {it:X}{cmd:[2,3]}. The statement {it:p} {cmd:=}
{cmd:&}{it:X}{cmd:[2,3]} is valid, but what it does, we will explain in
{bf:Pointers to expressions} below.
{p 4 4 2}
By the way, variables can be sustained by being pointed to.
Consider the program
{cmd}pointer(real matrix) scalar example(real scalar n)
{
real matrix tmp
tmp = I(3)
return(&tmp)
}{txt}
{p 4 4 2}
Ordinarily, variable {cmd:tmp} would be destroyed when {cmd:example()}
concluded execution. In this case, however, {cmd:tmp}'s existence
will be sustained because of the pointer to it. We might code
{it:p}{cmd: = example(3)}
{p 4 4 2}
and the result will be to create {cmd:*}{it:p} containing the 3 {it:x} 3
identity matrix. The memory consumed by that matrix will be freed when
it is no longer being pointed to, which will occur when {it:p} itself
is freed, or, before that, when the value of {it:p} is changed, perhaps by
{it:p} {cmd:= NULL}
{title:Pointers to expressions}
{p 4 4 2}
You can code
{it:p} {cmd:= &(2+3)}
{p 4 4 2}
and the result will be to create {cmd:*}{it:p} containing 5.
Mata creates a temporary variable to contain the evaluation of the
expression and sets {it:p} to the address of the temporary variable.
That temporary variable will be freed when {it:p} is freed or, before
that, when the value of {it:p} is changed, just as {cmd:tmp} was
freed in the example in the previous section.
{p 4 4 2}
When you code
{it:p} {cmd:=} {cmd:&}{it:X}{cmd:[2,3]}
{p 4 4 2}
the result is the same. The expression is evaluated and the result of the
expression stored in a temporary variable. That is why subsequently coding
{cmd:*}{it:p}{cmd:=2} does not change {it:X}{cmd:[2,3]}. All
{cmd:*}{it:p}{cmd:=2} does is change the value of the temporary variable.
{p 4 4 2}
Setting pointers equal to the value of expressions can be useful.
In the following code fragment, we create {it:n} 5 {it:x} 5 matrices
for later use:
{cmd}pvec = J(1, n, NULL)
for (i=1; i<=n; i++) pvec[i] = &(J(5, 5, .)){txt}
{title:Pointers to functions}
{p 4 4 2}
When you code
{it:p} {cmd:= &}{it:functionname}{cmd:()}
{p 4 4 2}
the address of the function is stored in {it:p}. You can later execute
the function by coding
... {cmd:(*}{it:p}{cmd:)(}...{cmd:)}
{p 4 4 2}
Distinguish carefully between
{it:p} {cmd:= &}{it:functionname}{cmd:()}
{p 4 4 2}
and
{it:p} {cmd:= &(}{it:functionname}{cmd:())}
{p 4 4 2}
The latter would execute {it:functionname}() with no arguments and then
assign the returned result to a temporary variable.
{p 4 4 2}
For instance, assume that you wish to write a function {cmd:neat()} that will
calculate the derivative of another function, which function you will pass to
{cmd:neat()}. Your function, we will pretend, returns a real scalar.
{p 4 4 2}
You could do that as follows
{cmd:real scalar neat(pointer(function) p,} {it:other args}...{cmd:)}
{cmd:{c -(}}
...
{cmd:{c )-}}
{p 4 4 2}
although you could be more explicit as to the characteristics of the
function you are passed:
{cmd:real scalar neat(pointer(real scalar function) p,} {it:other args}...{cmd:)}
{cmd:{c -(}}
...
{cmd:{c )-}}
{p 4 4 2}
In any case, inside the body of your function, where you want to call
the passed function, you code
{cmd:(*p)(}{it:arguments}{cmd:)}
{p 4 4 2}
For instance, you might code
{cmd:approx = ( (*p)(x+delta)-(*p)(x) ) / delta}
{p 4 4 2}
The caller of your {cmd:neat()} function, wanting to use it with, say,
function {cmd:zeta_i_just_wrote()}, would code
{it:result} {cmd:= neat(&zeta_i_just_wrote(),} {it:other args}...{cmd:)}
{title:Pointers to pointers}
{p 4 4 2}
Pointers to pointers (to pointers ...) are allowed, for instance,
if {it:X} is a matrix
{it:p1}{cmd: = &X}
{it:p2}{cmd: = &p1}
{p 4 4 2}
In this case, {cmd:*}{it:p2} is equivalent to {it:p1}, and
{cmd:**}{it:p2} is equivalent to {it:X}.
{p 4 4 2}
Similarly, we can construct a pointer to a pointer to a function:
{it:q1}{cmd: = &}{it:f}{cmd:()}
{it:q2}{cmd: = &p1}
{p 4 4 2}
In this case, {cmd:*}{it:q2} is equivalent to {it:q1}, and
{cmd:**}{it:q2} is equivalent to {it:f}().
{p 4 4 2}
When constructing pointers to pointers, never type {cmd:&&} -- such as
{cmd:&&}{it:x} -- to obtain the address of the address of {it:x}. Type
{cmd:&(&}{it:x}{cmd:)} or {cmd:& &}{it:x}. {cmd:&&} is a synonym for {cmd:&},
included for those used to coding in C.
{title:Pointer arrays}
{p 4 4 2}
You may create an array of pointers, such as
{it:P}{cmd: = (&}{it:X1}{cmd:, &}{it:X2}{cmd:, &}{it:X3}{cmd:)}
{p 4 4 2}
or
{it:Q}{cmd: = (&}{it:f1}{cmd:(), &}{it:f2}{cmd:(), &}{it:f3}{cmd:())}
{p 4 4 2}
In this case,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -