当前位置:  开发笔记 > 编程语言 > 正文

为什么要使用指针?

如何解决《为什么要使用指针?》经验,为你挑选了12个好方法。

我知道这是一个非常基本的问题,但是我在用高级语言编写一些项目之后开始使用一些基本的C++编程.

基本上我有三个问题:

    为什么使用指针超过正常变量?

    我应该何时何地使用指针?

    你如何使用指针与数组?

Tooony.. 170

为什么使用指针超过正常变量?

简短的回答是:不要.;-)指针用于不能使用其他任何东西的地方.这可能是因为缺乏适当的功能,缺少数据类型或纯粹的性能.更多......

我应该何时何地使用指针?

这里简短的回答是:你不能使用任何其他东西.在C中,您不支持复杂的数据类型,例如字符串.也没有办法将"通过引用"变量传递给函数.那是你必须使用指针的地方.您也可以让他们指向几乎任何东西,链接列表,结构成员等等.但是我们不要在这里讨论.

你如何使用指针与数组?

只需很少的努力和困惑.;-)如果我们谈论简单的数据类型,如int和char,那么数组和指针之间几乎没有区别.这些声明非常相似(但不相同 - 例如,sizeof将返回不同的值):

char* a = "Hello";
char a[] = "Hello";

您可以像这样到达数组中的任何元素

printf("Second char is: %c", a[1]);

索引1,因为数组以元素0开头.:-)

或者你也可以这样做

printf("Second char is: %c", *(a+1));

因为我们告诉printf我们要打印一个字符,所以需要指针运算符(*).如果没有*,将打印内存地址本身的字符表示.现在我们正在使用角色本身.如果我们使用%s而不是%c,我们会要求printf打印'a'指向的内存地址的内容加上一个(在上面的例子中),我们不必放*在前:

printf("Second char is: %s", (a+1)); /* WRONG */

但是这不会只打印第二个字符,而是打印下一个内存地址中的所有字符,直到找到空字符(\ 0).这就是事情开始变得危险的地方.如果您不小心尝试使用%s格式化程序打印整数类型的变量而不是char指针,该怎么办?

char* a = "Hello";
int b = 120;
printf("Second char is: %s", b);

这将打印在内存地址120上找到的任何内容并继续打印,直到找到空字符.执行这个printf语句是错误的和非法的,但无论如何它可能会起作用,因为在许多环境中指针实际上是int类型.想象一下,如果您使用sprintf()而将这种方式分配给另一个变量的"char数组",只会得到一定的有限空间.您很可能最终会在内存中写入其他内容并导致程序崩溃(如果您很幸运).

哦,如果你在声明它时没有给char数组/指针赋一个字符串值,你必须在给它一个值之前为它分配足够的内存量.使用malloc,calloc或类似的.这是因为您只在数组中声明了一个元素/单个内存地址指向.所以这里有一些例子:

char* x;
/* Allocate 6 bytes of memory for me and point x to the first of them. */
x = (char*) malloc(6);
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* Delete the allocation (reservation) of the memory. */
/* The char pointer x is still pointing to this address in memory though! */
free(x);
/* Same as malloc but here the allocated space is filled with null characters!*/
x = (char *) calloc(6, sizeof(x));
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* And delete the allocation again... */
free(x);
/* We can set the size at declaration time as well */
char xx[6];
xx[0] = 'H';
xx[1] = 'e';
xx[2] = 'l';
xx[3] = 'l';
xx[4] = 'o';
xx[5] = '\0';
printf("String \"%s\" at address: %d\n", xx, xx);

请注意,在执行已分配内存的free()之后仍然可以使用变量x,但是您不知道其中的内容.另外请注意两个printf()可能会给你不同的地址,因为不能保证第二次内存分配是在与第一个相同的空间中执行的.



1> Tooony..:

为什么使用指针超过正常变量?

简短的回答是:不要.;-)指针用于不能使用其他任何东西的地方.这可能是因为缺乏适当的功能,缺少数据类型或纯粹的性能.更多......

我应该何时何地使用指针?

这里简短的回答是:你不能使用任何其他东西.在C中,您不支持复杂的数据类型,例如字符串.也没有办法将"通过引用"变量传递给函数.那是你必须使用指针的地方.您也可以让他们指向几乎任何东西,链接列表,结构成员等等.但是我们不要在这里讨论.

你如何使用指针与数组?

只需很少的努力和困惑.;-)如果我们谈论简单的数据类型,如int和char,那么数组和指针之间几乎没有区别.这些声明非常相似(但不相同 - 例如,sizeof将返回不同的值):

char* a = "Hello";
char a[] = "Hello";

您可以像这样到达数组中的任何元素

printf("Second char is: %c", a[1]);

索引1,因为数组以元素0开头.:-)

或者你也可以这样做

printf("Second char is: %c", *(a+1));

因为我们告诉printf我们要打印一个字符,所以需要指针运算符(*).如果没有*,将打印内存地址本身的字符表示.现在我们正在使用角色本身.如果我们使用%s而不是%c,我们会要求printf打印'a'指向的内存地址的内容加上一个(在上面的例子中),我们不必放*在前:

printf("Second char is: %s", (a+1)); /* WRONG */

但是这不会只打印第二个字符,而是打印下一个内存地址中的所有字符,直到找到空字符(\ 0).这就是事情开始变得危险的地方.如果您不小心尝试使用%s格式化程序打印整数类型的变量而不是char指针,该怎么办?

char* a = "Hello";
int b = 120;
printf("Second char is: %s", b);

这将打印在内存地址120上找到的任何内容并继续打印,直到找到空字符.执行这个printf语句是错误的和非法的,但无论如何它可能会起作用,因为在许多环境中指针实际上是int类型.想象一下,如果您使用sprintf()而将这种方式分配给另一个变量的"char数组",只会得到一定的有限空间.您很可能最终会在内存中写入其他内容并导致程序崩溃(如果您很幸运).

哦,如果你在声明它时没有给char数组/指针赋一个字符串值,你必须在给它一个值之前为它分配足够的内存量.使用malloc,calloc或类似的.这是因为您只在数组中声明了一个元素/单个内存地址指向.所以这里有一些例子:

char* x;
/* Allocate 6 bytes of memory for me and point x to the first of them. */
x = (char*) malloc(6);
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* Delete the allocation (reservation) of the memory. */
/* The char pointer x is still pointing to this address in memory though! */
free(x);
/* Same as malloc but here the allocated space is filled with null characters!*/
x = (char *) calloc(6, sizeof(x));
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* And delete the allocation again... */
free(x);
/* We can set the size at declaration time as well */
char xx[6];
xx[0] = 'H';
xx[1] = 'e';
xx[2] = 'l';
xx[3] = 'l';
xx[4] = 'o';
xx[5] = '\0';
printf("String \"%s\" at address: %d\n", xx, xx);

请注意,在执行已分配内存的free()之后仍然可以使用变量x,但是您不知道其中的内容.另外请注意两个printf()可能会给你不同的地址,因为不能保证第二次内存分配是在与第一个相同的空间中执行的.


`char*a ="Hello";`和`char a [] ="Hello";`不一样,它们完全不同.一个声明指针另一个数组.尝试`sizeof`,你会看到差异.
-1,你在这里弄错了很多事实,我只是说你不配得到大量的赞成或接受的答案状态.
-1你开始很好,但第一个例子是错误的.不,这不一样.在第一种情况下,"a"是一个指针,在第二种情况下,"a"是一个数组.我已经提过了吗?这是不一样的!自己检查:比较sizeof(a),尝试为数组分配一个新地址.它不会起作用.
"执行此printf语句并没有错或违法".这是完全错误的.该printf语句具有未定义的行为.在许多实现中,它将导致访问冲突.指针实际上不是int类型,它们实际上是指针(没有别的).
你的120的例子是错误的.它使用的是%c而不是%s,所以没有错误; 它只打印小写字母x.此外,你后来声称指针是int类型的说法是错误的,并且给予没有经验的C程序员非常糟糕的建议.
[comp.lang.c FAQ](http://www.c-faq.com/)的第6节很好地解释了C中数组和指针之间的关系.这个答案没有.
@Sildoreth实际上,它没有.`void do_something(Base&x){x.virtual_method(); } int main(){Derived d; DoSomething的(d); 调用虚函数而不使用指针.指针可以轻松共享,复制和几乎所有其他内容.
"不要"-1.可选(null)参数.从函数返回的值"可选".
@milleniumbug实际上,在你的例子中,指针**涉及**.'&'只是语法糖,它使**看起来像你没有使用指针.
@Sildoreth,实际上,没有写到引用是用指针实现的.
@Sildoreth,C++ 11标准从未指定如何实现引用*.这是编译器的选择,[你不应该依赖它](http://stackoverflow.com/a/3954779/493122).事实上[大多数编译器用指针实现它](http://stackoverflow.com/a/3954803/493122)与**某些**无关,当使用引用时,涉及指针.
-1:"这些声明都是相同的`char*a ="Hello"; char a [] ="Hello";`" - 地狱,没有.他们是不一样的.

2> trshiv..:

使用指针的一个原因是可以在被调用的函数中修改变量或对象.

在C++中,使用引用而不是指针是一种更好的做法.尽管引用本质上是指针,但C++在某种程度上隐藏了这一事实并使它看起来好像是在传递价值.这使得更改调用函数接收值的方式变得容易,而无需修改传递它的语义.

请考虑以下示例:

使用参考:

public void doSomething()
{
    int i = 10;
    doSomethingElse(i);  // passes i by references since doSomethingElse() receives it
                         // by reference, but the syntax makes it appear as if i is passed
                         // by value
}

public void doSomethingElse(int& i)  // receives i as a reference
{
    cout << i << endl;
}

使用指针:

public void doSomething()
{
    int i = 10;
    doSomethingElse(&i);
}

public void doSomethingElse(int* i)
{
    cout << *i << endl;
}


是的,这可能是使用引用的最大优势.谢谢你指出来.没有双关语:)
提及引用可能是一个好主意,因为你不能传递空引用.
你肯定*可以*传递空引用.它并不像传递空指针那么容易.
@ n0rd:这样做是明确的未定义行为.

3> Kyle Cronin..:

    指针允许您从多个位置引用内存中的相同空间.这意味着您可以在一个位置更新内存,并且可以从程序中的其他位置查看更改.您还可以通过共享数据结构中的组件来节省空间.

    您应该在需要获取的任何地方使用指针,并将地址传递到内存中的特定位置.您还可以使用指针来导航数组:

    数组是已分配特定类型的连续内存块.数组的名称包含数组起始点的值.当你加1时,会带你到第二个位置.这允许您编写循环,增加一个向下滑动数组的指针,而没有用于访问数组的显式计数器.

这是C中的一个例子:

char hello[] = "hello";

char *p = hello;

while (*p)
{
    *p += 1; // increase the character by one

    p += 1; // move to the next spot
}

printf(hello);

版画

ifmmp

因为它取每个字符的值并将其递增1.



4> Bill the Liz..:

指针是获取间接引用另一个变量的一种方法.他们不是保留变量的,而是告诉你它的地址.这在处理数组时特别有用,因为使用指向数组中第一个元素的指针(其地址),您可以通过递增指针(到下一个地址位置)快速找到下一个元素.

我读过的关于指针和指针算法的最佳解释是在K&R的C编程语言中.开始学习C++的好书是C++ Primer.



5> Carl..:

让我试着回答这个问题.

指针类似于参考.换句话说,它们不是副本,而是一种引用原始值的方法.

除此之外,在您处理嵌入式硬件时,通常需要经常使用指针的地方.也许您需要切换数字IO引脚的状态.也许您正在处理中断并需要在特定位置存储值.你得到了照片.但是,如果您没有直接处理硬件而只是想知道要使用哪种类型,请继续阅读.

为什么使用指针而不是普通变量?当您处理复杂类型(如类,结构和数组)时,答案会变得更清晰.如果你要使用普通变量,你最终可能会制作一个副本(编译器足够聪明,在某些情况下可以防止这种情况,C++ 11也有帮助,但我们暂时不会讨论这个问题).

现在如果要修改原始值会发生什么?你可以使用这样的东西:

MyType a; //let's ignore what MyType actually is right now.
a = modify(a); 

这将工作得很好,如果你不确切知道为什么使用指针,你不应该使用它们.要小心"他们可能更快"的原因.运行您自己的测试,如果它们实际上更快,那么使用它们.

但是,假设您正在解决需要分配内存的问题.分配内存时,需要释放内存.内存分配可能成功也可能不成功.这是指针有用的地方 - 它们允许您测试已分配的对象的存在,并允许您通过取消引用指针来访问分配内存的对象.

MyType *p = NULL; //empty pointer
if(p)
{
    //we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if(p) //if the memory was allocated, this test will pass
{
    //we can do something with our allocated array
    for(size_t i=0; i!=50000; i++)
    {
        MyType &v = *(p+i); //get a reference to the ith object
        //do something with it
        //...
    }
    delete[] p; //we're done. de-allocate the memory
}

这是你使用指针的关键 - 引用假定你所引用的元素已经存在.指针不会.

您使用指针(或至少最终必须处理它们)的另一个原因是因为它们是引用之前存在的数据类型.因此,如果你最终使用库来做你知道他们更擅长的事情,你会发现很多这些库都使用指针遍布整个地方,因为它们存在了多长时间(很多)他们是在C++之前写的.

如果你没有使用任何库,你可以设计你的代码,使你可以远离指针,但鉴于指针是该语言的基本类型之一,你越习惯使用它们,越多便携式你的C++技能.

从可维护性的角度来看,我还应该提一下,当你使用指针时,你要么必须测试它们的有效性并在它们无效时处理这个案例,或者只是假设它们是有效的并接受你的事实.当假设被打破时,程序将崩溃或更糟.换句话说,您选择指针的方法是在出现问题时引入代码复杂性或更多维护工作,并且您试图追踪属于指针引入的整类错误的错误,例如内存损坏.

因此,如果您控制所有代码,请远离指针,而是使用引用,尽可能保持const.这将迫使您考虑对象的生命周期,并最终使您的代码更容易理解.

记住这个区别:引用本质上是一个有效的指针.指针并不总是有效.

所以我说不可能创建无效的引用?不,它完全有可能,因为C++几乎可以让你做任何事情.这是无意中做的更难,你会惊讶于有多少错误是无意的:)



6> Dan Lenski..:

以下是对C的许多功能有意义的一个略有不同但有见地的看法:http://steve.yegge.googlepages.com/tour-de-babel#C

基本上,标准CPU架构是Von Neumann架构,能够在这样的机器上引用数据项在内存中的位置并对其进行算术运算非常有用.如果您知道汇编语言的任何变体,您将很快看到它在低级别上的重要性.

C++使指针有点混乱,因为它有时会为你管理它们并以"引用"的形式隐藏它们的效果.如果你使用直接C,对指针的需求就更明显了:没有其他方法可以通过引用进行调用,这是存储字符串的最佳方式,它是迭代数组的最佳方式,等等.



7> MrZebra..:

指针的一种用法(我不会提及其他人的帖子中已经涵盖的内容)是访问您尚未分配的内存.这对于PC编程来说没什么用处,但它在嵌入式编程中用于访问内存映射的硬件设备.

回到DOS的旧时代,您曾经通过声明指向以下内容的指针直接访问视频卡的视频内存:

unsigned char *pVideoMemory = (unsigned char *)0xA0000000;

许多嵌入式设备仍然使用这种技术.



8> warren..:

在很大程度上,指针是数组(在C/C++中) - 它们是内存中的地址,如果需要可以像数组一样访问(在"正常"情况下).

因为它们是项目的地址,所以它们很小:它们只占用地址的空间.因为它们很小,所以发送它们很便宜.然后他们允许该函数处理实际项目而不是副本.

如果要进行动态存储分配(例如链接列表),则必须使用指针,因为它们是从堆中获取内存的唯一方法.


我认为说指针是数组是误导的.实际上,数组名是指向数组第一个元素的常量指针.只是因为你可以访问一个任意点,好像它的数组并不意味着它......你可能会遇到访问冲突:)
指针不是数组.阅读[comp.lang.c FAQ](http://www.c-faq.com/)的第6部分.

9> Ash..:

指针在许多数据结构中很重要,其设计需要能够有效地将一个"节点"链接或链接到另一个"节点".你不会"选择"一个指针而不是普通的数据类型,比如浮点数,它们只是有不同的用途.

在需要高性能和/或紧凑内存占用的情况下,指针非常有用.

可以将数组中第一个元素的地址分配给指针.然后,这允许您直接访问基础分配的字节.数组的重点是避免你需要这样做.



10> 小智..:

在变量上使用指针的一种方法是消除所需的重复内存.例如,如果您有一些大型复杂对象,则可以使用指针指向您所做的每个引用的该变量.使用变量,您需要为每个副本复制内存.



11> Sildoreth..:

在C++中,如果要使用子类型多态,必须使用指针.看这篇文章:没有指针的C++多态性.

真的,当你想到它时,这是有道理的.当您使用子类型多态时,最终,您不会提前调用哪个类或子类的方法实现,因为您不知道实际的类是什么.

拥有一个包含未知类对象的变量的想法与C++在堆栈中存储对象的默认(非指针)模式不兼容,其中分配的空间量直接对应于类.注意:如果一个类有5个实例字段而不是3个,则需要分配更多空间.


请注意,如果您使用'&'通过引用传递参数,则间接(即指针)仍然涉及幕后.'&'只是语法糖,(1)省去了使用指针语法的麻烦,(2)允许编译器更严格(例如禁止空指针).



12> Andrew Medic..:

因为在整个地方复制大对象会浪费时间和内存.


指针是如何帮助的呢?我认为来自Java或.Net的人不知道堆栈和堆之间的区别,所以这个答案是没用的.
推荐阅读
路人甲
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有