使用指针的参考






3.43/5 (11投票s)
本文旨在帮助解释 C/C++ 如何使用指针。
如何理解 C/C++ 中的指针
典型的终端用户计算机使用的是 x86 系列微处理器,如 Pentium、AMD 或 Intel 的某些原型。这意味着采用了 32 位内存地址方案。由于计算机本质上使用基于 2 的二进制数字系统,这意味着有 2 的 32 次方个可能的内存位置——即 4,294,967,296 (4 GB)。这些内存地址位置不能被物理移动,因此必须进行引用。指针是一种特殊的数据类型,它指向存储着另一个变量地址的地址的位置。当声明一个变量时,编译器会被告知其存在,并由其数据类型定义。当一个变量被初始化时,它会被赋予一个值,该值将根据它所代表的数据类型(整数、字符、浮点小数等)进行存储。尽管 .NET Framework 确实大量使用了指针,但 C/C++ 语言在编程实践中,例如 COM 或 MFC 和 ATL 等 Windows 框架中,也大量使用指针。理解指针有助于学习这些框架,同时也要理解,如果被指向的位置不清楚,就不应该使用指针。本文将使用旧式 C++ 语法(使用标准模板库)以及 Visual C++ Express Edition 上的 Windows C++ 来举例说明指针的使用。同时,本文并非替代 CodeProject 网站上其他更高级的指针文章。
指针允许您在不知道其真实值的情况下操作地址。以下是一些找出指针中存储内容的示例代码。‘\t’ 转义序列字符表示制表符。在此示例中,使用“地址运算符”(&
)来显示存储的值的地址。
#include <iostream> int main() { using namespace std; unsigned short_int = 5; unsigned long_int = 65535; long signed_int = -65535; cout << "short_int:\t" << short_int; cout << "\tAddress of short int:\t"; cout << &short_int << endl; cout << "long_int:\t" << long_int; cout << "\tAddress of long_int:\t" ; cout << &long_int << endl; cout << "signed_int:\t\t" << signed_int; cout << "\tAddress of signed_int:\t" ; cout << &signed_int << endl; return 0; }
代码块的第一部分显示了这些变量的声明和初始化:一个无符号短整型、一个无符号长整型和一个长整型。如果它是无符号的,那么我们可以假设它是带符号的(在某些情况下,可能带有负号或正号)。这是在使用命令行上的 cl.exe 编译器时的输出。
short_int : 5 Address of short int: 0012FF34
long_int : 65535 Address of long_int: 0012FF3C
signed_int: -65535 Address of signed_int: 0012FF38
当变量被声明和初始化(或赋值)时,编译器会负责分配内存并自动为其分配地址。然而,上面的示例仅显示了值及其对应的地址。这并不是非常有用的信息。因此,我们将尝试另一个示例。
include <iostream> int main() { using namespace std; unsigned short int mAge = 32, yAge = 40; // a binary pointer unsigned short int *pAge = &mAge; cout << "mAge:\t" << mAge << "\t\tyAge:\t" << yAge << endl; cout << "&mAge:\t" << &mAge << "\t&yAge:\t" << &yAge << endl; cout << "pAge:\t" << pAge << endl; cout << "*pAge:\t" << *pAge << endl; cout << "\nReassigning: pAge = &yAge..." << endl << endl; pAge = &yAge; cout << "mAge:\t" << mAge << "\t\tyAge:\t" << yAge << endl; cout << "&mage:\t" << &mAge << "\t&yAge:\t" << &yAge << endl; cout << "pAge:\t" << pAge << endl; cout << "*pAge:\t" << *pAge << endl; cout << "\n&pAge:\t" << &pAge << endl; return 0; }
不可否认,这种语法是旧式 C++,有点令人困惑,但当我们查看 Visual C++ 示例时,基本原理应该会变得更清晰。但是,为了简单起见,我们应该避免开发任何类型的 Windows Forms 应用程序。这是输出。
mAge: 32 yAge: 40
&mAge: 0012FF34 &yAge: 0012FF38
pAge: 0012FF34
*pAge: 32
Reassigning: pAge = &yAge...
mAge: 32 yAge: 40
&mAge: 0012FF34 &yAge: 0012FF38
pAge: 0012FF38
*pAge: 40
&pAge: 0012FF3C
虽然 C/C++ 使用指针,但像 Java 这样的其他高级面向对象语言则不使用。Java 开发人员认为指针会产生有缺陷的程序。然而,对象用户(应用程序)访问对象是面向对象的内在特征。对象或模块是一组包含语义上相关的函数,并且永远不能作为一个整体被访问。访问对象是通过间接引用实现的。这是与 COM 客户端和 .NET 对象进行互操作的一些基本原则。COM 可调用包装器 (CCW) 存在于 COM 的非托管平台和 .NET 的托管平台边界。这个 CCW 能够读取元数据并形成类型库。regasm.exe 工具能够为 .NET 程序集创建注册表项,以便它可以显示为 COM 组件。因为我们处理的是对象的使用,所以必须跟踪其生命周期。CCW 能够跟踪 COM 客户端的引用计数,当引用计数达到零时,它就可以将所有涉及的指针映射到该对象的引用,从而允许对其进行垃圾回收。
使用 Visual C++ Express Edition 的示例
只需启动一个新的 C++ 项目,选择 Win32 控制台,然后单击“完成”,而不是单击“空项目”。请务必转到项目的属性,并将“使用 UNICODE 响应文件”设置为“否”,并将“字符集”设置为“使用多字节字符集”。
// Example_pointer.cpp : Defines the entry point // for the console application.#include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { int i = 5; printf(" i : %8.X (value)\n", i ); // the Address of operator '&' tells what the address of i is // printf(" &i : %8.X (address)\n", &i); // declare a pointer and assign to it the value of the address of i // int *p = &i; printf (" p : %8.X (value)\n", p); printf(" &p : %8.X (address)\n", &p); printf(" *p : %8.X (indirection)\n", *p); // use indirection to dereference the variable we// // are pointing to and returns the value at that memory location// // since contains the address of i, when we use indirection, // we get the value of i as a return // getc(stdin); // the getc function with the standard input parameter // passed makes the console remain on your screen. return 0; }
输出如下
i : 5 (value)
&i : 2BF774 (address)
p : 2BF774 (value)
&p : 2BF768 (address)
*p : 5 (indirection)
请注意,p
的值与 i
的地址相同。p
的实际地址如果出现在堆栈上稍远的位置,就会显示出来。最重要的是,请注意 *p
(解引用)然后再次解引用会返回 i
的值,而不是内存地址。或者 *p = 5
。
这是最后一个示例的扩展,但增加了对变量 i
的引用。
#include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { int i = 5; printf(" i : %8.X (value)\n", i ); printf(" &i : %8.X (address)\n", &i); int *p = &i; printf (" p : %8.X (value)\n", p); printf(" &p : %8.X (address)\n", &p); printf(" *p : %8.X (indirection)\n", *p); // since contains the address of i, when we use indirection, // we get the value of i as a return // int &r = i; // a reference to i or an alias for i printf(" r : %8.X (value)\n", r); // printf the address of r printf (" &r : %8.X (address)\n", &r); int **pp = &p; printf(" pp : %8.X (value)\n", pp); printf(" &pp : %8.X (address)\n", &p); printf(" *pp : %8.X (indirection)\n", *pp); printf(" **pp : %8.X (double indirection)\n", **pp); getc(stdin); return 0; }
输出如下
i : 5 (value)
&i : 1DFDDC (address)
p : 1DFDDC (value) // p has the value of the address of i
&p : 1DFDD0 (address) // the address of p is a short distance on the stack
*p : 5 (indirection) // the binary pointer for indirection returns the value of i
r : 5 (value) // r as a reference is just an alias for i.
&r : 1DFDDC (address) // the address of r is thus the same as the address of i
pp : 1DFDD0 (value)
&pp : 1DFDD0 (address)
*pp : 1DFDDC (indirection) // the indirection redirect the original indirection
// to return the address of i.
**pp: 5 (double indirection) // the double indirection dereferences the above
// to return the value of i.
指针提供了通过间接引用访问数据的强大方法。每个变量都有一个地址,可以使用地址运算符(&
)获得。该地址可以存储在指针中。