我知道之前已经问过这个问题,但其他一些问题没有涉及的是为什么
请允许我解释一下。我刚刚完成了一个输出整数和指针的教程,向您展示如何。
int anInteger = 50;
int *anIntPointer = &anInteger;
因此,要设置指针,我会正常分配变量值,然后将该变量赋值给指针。我理解这一点,但正如我已经说过的,这是如何而不是为什么。
如果我想返回值50
我只能NSLog
anInteger
那么为什么我需要一个指针。如果我可以NSLog
*anIntPointer
执行完全相同的操作,为什么我需要NSLog
anInteger
?
好的,我知道这是非常微不足道的,并且可能有完美的情况使用指针,但到目前为止,没有我阅读或观看过的教程会给我一个完美的情况。他们都处理如何
请帮我找到为什么。
答案 0 :(得分:3)
指针有很多用途。一个显而易见的问题是你想要调用一个函数并让它修改你的一个变量:
void f(int *i) { *i = 42; }
int g() { int i; f(&i); return i; }
另一种方法是返回一个没有大量复制的大型结构:
struct big_struct *f() {
big_struct *bs = malloc(sizeof(big_struct));
// Populate the big_struct;
return bs;
}
另一个是管理在编译时你不知道的大小的数组:
struct item *fetch_items(int n) {
item *i = malloc(n*sizeof(item));
load_items(i, n);
return i;
}
还有一种是递归数据类型,例如链表:
struct node {
int value;
struct node *next;
};
这只是一个抽样。指针就像钉木匠一样。它们是几乎所有非平凡编程问题中的关键工具。
答案 1 :(得分:3)
我们使用指针(在C和C派生语言中)的主要原因是:
模仿传递引用语义:在C中,所有函数参数都按值传递。形式参数和实际参数是内存中的不同对象,因此写入形参数对实际参数没有影响。例如,给定代码
void swap(int a, int b)
{
int tmp = a; a = b; b = tmp;
}
int main(void)
{
int x = 2, y = 3;
printf("before swap: x = %d, y = %d\n", x, y);
swap(x, y);
printf("after swap: x = %d, y = %d\n", x, y);
return 0;
}
a
和x
是物理上不同的对象;写入a
不会影响x
,反之亦然。因此,上述程序中的前后输出将是相同的。为了让swap
修改x
和y
的内容,我们必须传递指向这些对象的指针并取消引用函数中的指针:
void swap(int *a, int *b)
{
int tmp = *a; *a = *b; *b = tmp;
}
int main(void)
{
int x = 2, y = 3;
printf("before swap: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("after swap: x = %d, y = %d\n", x, y);
return 0;
}
a
和x
仍然是内存中的不同对象,但表达式 *a
引用与表达式x
相同的内存;因此,写*a
会更新x
的内容,反之亦然。现在swap
函数将交换x
和y
的内容。
请注意,C ++引入了引用的概念,它的行为类似指针,但不需要显式的解引用:
void swap(int &a, int &b)
{
int tmp = a; a = b; b = tmp;
}
int main(void)
{
int x = 2, y = 3;
std::cout << "before swap: x = " << x << ", y = " << y << std::endl;
swap(x, y);
std::cout << "after swap: x = " << x << ", y = " << y << std::endl;
return 0;
}
在这种情况下,表达式a
和x
确实引用相同的内存位置;写一个确实影响另一个。不过,这是一个C ++主义。
我对Obj-C不太熟悉,知道他们是否有类似的机制。
跟踪动态分配的内存: C内存分配函数malloc
,calloc
和realloc
以及C ++运算符{{1}所有返回动态分配内存的指针。如果必须动态分配内存,则必须使用指针来引用它。我再次对Obj-C不熟悉,知道他们是否使用不同的内存分配机制。
创建自引用和动态数据结构:new
或struct
类型等聚合类型不能包含自己的实例;例如,你不能做像
union
创建链表节点。在结束struct node
{
int value;
struct node next;
};
之前,struct node
不是完整类型,并且您无法声明不完整类型的对象。但是,struct可以包含指针到它自己的实例:
}
您可以声明指向不完整类型的指针,这样就可以了。列表中的每个节点都可以引用紧随其后的节点。由于您正在处理指针,因此您可以合理地轻松地添加或删除列表中的节点;你只需更新指针值,而不是物理移动数据。
我几乎可以保证Obj-C中的任何容器类型都使用指针操作。
因为有时语言会强迫您:在C和C ++中,在大多数情况下,数组类型的表达式将隐式转换为指针类型。数组下标是根据指针算法完成的;表达式struct node
{
int value;
struct node *next;
};
的评估方式就好像它是a[i]
一样。 IOW,您在*(a + i)
之后找到第i个元素的地址并取消引用它。
答案 2 :(得分:1)
指针并非特定于Objective-C,实际上它们用于C和[通常不是那么多C ++]。基本上,它是通过引用传递对象的方式。
void thisFunctionModifiesItsArgs(int *x, int *y, int *z)
{
*x = 4;
*y = *z;
*z = 100;
}
int main()
{
int a = 0;
int b = 1;
int c = 2;
thisFunctionModifiesItsArgs(&a, &b, &c);
// now, a = 4, b = 2, and c = 100
}
答案 3 :(得分:1)
最明显的原因:
1)您希望指向的对象超出其使用范围,因此您可以创建分配。访问超出其范围的int的地址是在寻找麻烦 - 该地址很可能被其他东西使用。如果你为它创建一个独特的内存位置,那么问题就解决了(或者......可能会被取代)。
2)你想通过引用/指针/地址传递它。这对于变异对象很有用,或者在类型很大时用作优化。
3)支持多态和/或不透明类型
4)指向实现的指针(抽象,依赖性减少)
然后......(我不希望你在这个阶段理解所有这些案例)
所以,你展示的例子是如此微不足道,它不代表(任何)这些情况 - 它只是试图引入语法。
有很多案例,由于各种原因,它们会在现实世界的C,C ++,ObjC等程序中定期使用。
答案 4 :(得分:1)
一个简单的答案:因为有些变量比简单的整数更复杂。本教程为您提供了一个非常简单的案例来解释这个概念,但他们描述的简单案例几乎不会被使用。
答案 5 :(得分:0)
贾斯汀的回答是你所要求的。如果你需要一个好的教程,那么我推荐第5章“Beginning Mac Programming”,它解释了内存寻址的工作原理以及如何使用指针,以及原因。
答案 6 :(得分:0)
计算机科学001
计算机(计算机芯片)只能做三件事,但他们每秒可以完成数百万甚至数十亿次。
他们可以将信息(数字)存储到内存中。 他们可以对这些数字做关节炎。 他们可以根据关节模型进行简单的判断,就像a = b然后转到地址X一样。
多数民众赞成。
我用来解释汇编程序编程初学者指针的一个非常简单的类比就是把内存想象成一排邮箱。第一个邮箱的地址为0,下一个邮箱的地址为加号,依此类推。
当计算机启动时,会告知它转到邮箱0并获取内容。
内容可以是信息或命令,邮箱0始终包含命令。该命令可能是转到邮箱1并获取其内容。
邮箱的内容只能容纳如此多的信息,就像真正的邮箱只能容纳如此多的信息一样。例如,如果邮递员需要提供包裹,他会在邮箱中发出通知去邮局领取邮件。通知就像一个指针。指针不保存信息,真实信息位于指针所在的位置,在这种情况下是邮局。
你甚至可以到邮局找到除了指向另一个位置的另一个指针之外什么都没有。我们称之为“句柄”或指向指针的指针。
答案 7 :(得分:0)
如果要将字节序列从一个地方复制到其他地方(自然),您必须知道源地址和目标地址。要在语言的抽象级别表达它,您可以使用指示内存位置的指针。
除了笔记之外,在较低级别中,经常使用指针。一个非常简单的例子:写入80x25屏幕。例如,屏幕的基地址是0xb8000,其中存储屏幕的第一个字符。您可以使用指针,您可以将字符写入屏幕中的适当位置。例如:unsigned short* sc = (unsigned short*)0xb8000; *sc = 'A' | (attr) << 8;
。等等...
N.B。:指针体现了间接性,有可能,你可以拥有“多个”指针:**(想象一下C主函数签名,以及它中的char **!)。或者例如您想在单独的函数中使用malloc创建列表结构。然后你可以传递一个结构列表**或你有参数的函数,并且在函数中你可以为列表分配一个值(一个内存地址),这意味着你已经在内存中创建了列表。