本人声明
1.本栏仅为归档自己看到的优秀文章;
2.文章版权归原作者所有;
3.因为个人水平有限,翻译难免有错误,请多多包涵。
原文地址
https://www.codeguru.com/cpp/cpp/algorithms/understanding-c-pointers.html
文章正文
理解C++指针
作者:Manoj Debnath 发表于:2017.02.17
指针可以说是C/C++编程中的最强大的功能。指针也被认为是一种可怕的东西,然而我们并没有必要这么想。现代编程语言已经将这个想法置之不理,认为它弊大于利。从高峰时间的生产力角度来看,这种担忧似乎是合乎逻辑的。使用指针的危险在于必须对症下药,并要对其的误用负全部的责任。去除指针毫无疑问可以减少编程中的一个风险,但却牺牲了我们本可以拥有的调整内存的一个手段。这就是C / C++在编写低级程序时无法被替代的原因。但是,在C ++中,我们也可以完全不使用指针(显示使用)来获得相同的结果,那么,使用指针的乐趣在哪里呢?使用指针的理念,可以真正加深我们对工作复杂性的了解。本文试图为这个概念提供一个指针(双关语),并将其进一步引申到更底层(sic)。
- 变量
在C /C++编程中,变量是我们可以通过程序操作的内存位置的自定义名称。它被定义为特定的类型,例如int,char,float,double,short,long,Employee,Item等。定义的类型包含了一些信息:首先是使用的内存大小,其次是可以存储的值的范围。例如,当我们声明一个简单的字符变量时,我们得到其支持的值范围的不同的变体,而不必改变使用内存的大小。
char ch; // size=1 byte/8 bit, range=-128 to127,
// equivalent to signed char ch;
unsigned char uch; // size=1 byte/8 bit, range=0 to 255
对于C / C ++中的其他数据类型,也符合类似的推论。
- C++指针和引用
指针是其存储值的内存地址,而不是变量自身的值。这是C++中使用的一种复合类型。另一种叫做引用。引用基本上是指相同内存地址变量的别名或替代名称。另一方面,指针用于变量的间接访问。指针与引用对象的主要区别:指针是一个单独的实体,可以单独处理,如赋值,复制和在其生命周期内改变其指向的位置。另一方面,引用在这个问题上则非常严格。我们甚至无法声明未进行分配操作的引用。
int val=10; // integer variable with a initialized value
int &refval1; // uninitialized, Not OK
int &refval2=val; // initialized, OK
int *pval1; // uninitialized, OK
int *pval2=0; // initialized, OK, equivalent to nullptr
int *pval3=nullptr; // initialized, OK
int *pval4=&val; // initialized OK, pointing to the
// location designated by val
注意:在C++中,nullptr表示符是一种指针字符串。其表示该指针是空的,这种含义是不言自明的。指针内容都有包含一种类型,如真假是bool类型的内容;整数(比如12),是int类型的内容,其他的则依此类推。
- C++指针变量的处理
很明显,指针用于指代存储器中的地址,因为它保存的值是存储器位置的地址。另外,指针变量必须与它指向的变量的类型一致。这意味着,整形指针可以保存整形变量的内存地址,浮点型指针可以保存浮点型变量的地址。
为了更好的理解下面的语句,请记住,*表示解除引用,而&符号表示取地址。因此,当我们进行以下声明时:
int ival=12; // integer variable initialized with value 12
int *pi=&i; // integer pointer holding the address of ival,
// & means the address
在内存中,它的表示形式如下。内存地址以十六进制数来进行表示的。每次运行代码时,指针指向的实际地址可能会有所不同。
图1: 内存地址表示
现在,请观察下面的代码,并注意代码中给出的相应注释。
#include<iostream>
using namespace std;
int main()
{
int ival=12;
int *pi=&ival;
cout<<ival<<endl; // prints the value of ival, 12
cout<<*pi<<endl; // prints the value of ival, 12
// through pointer dereference
cout<<&ival<<endl; // address of ival
cout<<pi<<endl; // address of ival or the contents
// of pointer pi
cout<<&pi<<endl; // address of pi
cout<<*(&ival)<<endl; // address of ival dereferenced,
// i.e. the value of ival, 12
return 0;
}
我们也可以创建一个指针,用来指向另一个指针。这称为双级指针。按照类似的方式,也可以存在三级指针,以此类推。将以下代码片段添加到前面的程序中,并结合给出的注释来计算程序输出结果。
图2:双级指针
int main(){
// ...
int **ppi=π
// ...
cout<<**ppi<<endl; // contents of ival
cout<<&*ppi<<endl; // address pi
cout<<*ppi<<endl; // contents of ppi=address of pi
cout<<&ppi<<endl; // address of ppi
// ...
}
指针还涉及其他一些概念,我们会在另一篇文章中讲解它们。但是,这里讲述的是处理指针变量的基本思想。现在,让我们转至指针的另外一种有趣的功能,它被称为函数指针。
- 函数指针
将指针的概念向前推进一步,我们可以得到一种特殊的指针,其被称为指向函数的指针或函数指针,该指针指向的是一个函数,而不是一个对象。它的具体用途是提供编程的灵活性。与其他类型的指针相类似,它的类型是由函数的返回值和函数声明的参数类型来确定的。例如,函数声明为以下形式:
int len(const string&);
该函数的返回值类型为int,参数为const string&。现在,如果我们想要一个指向该函数的指针,我们按照以下形式来进行声明:
int (*pf) (const string &); // pointer to function
// that returns an integer value
int* func(const string &); // a function that returns
// an integer pointer
括号(* pf)是必不可少的;否则,它将表达其他的含义,正如上面所展示的那样。现在,为了初始化它,我们可以按照以下的方式进行。以下两者都是正确的初始化方法。
pf=len;
pf=&len ;
因此,我们按照以下的任意一种方式来调用该函数:
int i1=pf("hello");
int i2=(*pf)("hello");
int i3=len("hello");
下面是讲述该关于该概念的完整代码列表:
#include<iostream>
using namespace std;
int len(const string &str){
return str.length();
}
int main()
{
int (*pf)(const string &);
pf=&len;
cout<<(*pf)("hello")<<endl;
cout<<len("hello")<<endl;
cout<<pf("hello")<<endl;
return 0;
}
- 重载函数指针
乍一看,指针重载的函数可能会让人觉得比较混乱,但编译器会非常智能地处理它们。
#include<iostream>
using namespace std;
int add(int a, int b){
return a+b;
}
float add(float a, float b){
return a+b;
}
int main()
{
int (*pf1)(int,int);
float (*pf2)(float,float);
pf1=add;
pf2=add;
cout<<pf1(10,20)<<endl;
cout<<pf2(3.4,1.2)<<endl;
return 0;
}
编译器会根据上下文,来确定函数指针的类型,该函数指针必须指向确切的函数。
- 将函数指针作为参数使用
有趣的是,函数指针可以用作另一个函数的参数。例如,我们可以向前面的代码添加一个函数,该函数将函数指针作为其参数。
float product(float f1,float(*pf)(float,float)){
return f1 * pf(2.5,2.5);
}
// ...
cout<<product(2.6,add)<<endl;
结论
严格来说,指针的概念可以追溯至汇编语言时代; C语言适用于通用编程,C ++甚至可以用于在今代进行编程。 C / C ++中指针的强大功能,使得它们可用于任何类型的软件开发,从高级应用程序到低级驱动程序。指针涉及极大的复杂性。本文只是对其使用做了简单的介绍,仅是其冰山一角。需要更多的文章,才能对其进行更全面地介绍。然而,这里给出的简洁理念,可以作为一个较好的开端。关于作者
Manoj Debnath
manosolireap@outlook.com相关文章
Last.fm Open Sources C++ Moost Library Now Available
CppDepend Pro License for C\C++ open source project contributors
栏目导航
上一篇:技术文章翻译(八) – 理解C++文件处理
下一篇:技术文章翻译(十) – 跨平台测试的五大问题