我开始学习C。
现在,据我了解,指针用于指向另一个变量的地址并通过不直接接近它来更改该变量值。
现在,我有 3 个问题:
这对吗?如果是这样,我为什么需要它?
还有其他用途吗?
以下代码行之间有什么区别:
// 1 int x = 10; int *ptr = &x; // 2 int x = 10; int ptr = &x;
指针的值是另一个对象在内存中的位置。所以给定第一组声明:
int x = 10;
int *ptr = &x;
你会在内存中有类似下面的内容(地址是凭空提取的,并不意味着代表任何真实的平台):
项目地址 0x00 0x01 0x02 0x03 ---- -------- ---- ---- ---- ---- x 0x08001000 0x00 0x00 0x00 0x0A 指针 0x08001004 0x08 0x00 0x10 0x00
x
包含值10
。 ptr
包含 的地址x
。因此,表达式 x
和*ptr
是等价的;读或写与*ptr
读或写相同x
。
FWIW,那和之间的区别
int x = 10;
int ptr = &x;
是ptr
被视为整数,而不是指向整数的指针,因此如果您尝试类似*ptr
的操作(一元的操作数*
必须是指针类型),编译器会发出警告。
那么,为什么是指针呢?
C 中的指针有 3 个用途:
首先,指针允许我们模仿传递引用的语义。在 C 中,所有函数参数都是按值传递的;这意味着函数的形式参数与实际参数在内存中是不同的对象。例如,使用这个swap
函数:
void swap(int a, int b)
{
int t = a;
a = b;
b = t;
}
int main(void)
{
int x = 1, y = 2;
swap(x, y);
...
}
如果我们查看内存,我们有如下内容:
项目地址 0x00 0x01 0x02 0x03 ---- -------- ---- ---- ---- ---- x 0x08001000 0x00 0x00 0x00 0x01 是 0x08001004 0x00 0x00 0x00 0x02 一个 0x08001010 0x00 0x00 0x00 0x01 b 0x08001014 0x00 0x00 0x00 0x02
a
这意味着对或所做的任何更改b
都不会反映在x
或中y
;调用swap
,后将x
保持y
不变。
但是,如果我们将指针传递给x
和y
,swap
就像这样:
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
int main(void)
{
int x = 1, y = 2;
swap(&x, &y);
...
}
那么我们的记忆看起来像这样:
项目地址 0x00 0x01 0x02 0x03 ---- -------- ---- ---- ---- ---- x 0x08001000 0x00 0x00 0x00 0x01 是 0x08001004 0x00 0x00 0x00 0x02 0x08001010 0x08 0x00 0x10 0x00 b 0x08001014 0x08 0x00 0x10 0x04
a
andb
包含 and 的地址,所以从表达式中读取和写入and等价于从and中读取和写入。因此,在调用之后,和的值将被更改。x
y
*a
*b
x
y
swap
x
y
我们使用指针的第二个原因是跟踪动态分配的内存。当我们想在运行时创建一个新对象时,我们使用malloc
orcalloc
调用,它分配内存并返回一个指向它的指针:
int *p = malloc(sizeof *p * N); // dynamically allocates enough memory
// to hold N integer values and saves
// the location to p
指针是在 C 中跟踪动态分配内存 的唯一方法。
最后,指针允许我们创建动态数据结构,例如列表、树、队列等。其思想是结构中的每个元素显式指向下一个元素。例如,这是一个定义简单整数列表的类型:
struct elem
{
int data;
struct elem *next;
};
每个类型的对象都struct elem
明确指向以下类型的项目struct elem
。这允许我们根据需要在列表中添加或删除它们(与数组不同),它允许我们在构建列表时对列表进行排序(我会发布一个示例,但我累死了,它可能没有意义;无论如何,您很快就会找到它们)。
指针存储给定类型的内存地址。&x
是变量的地址x
,当前存储的值是10。一个int* ptr
可以保存这个位置。改变 10 (aka x
) 你可以说
*ptr = 3;
您的第二个代码块将地址存储ptr
为实际数字。这意味着当您尝试打印时,ptr
您会注意到它是一个半随机数(可能非常高)。
我建议您研究这些基本数据类型是如何存储在内存中的。
至于用途,您可以创建能够在其范围之外更改内存的函数,而不是为自己制作本地副本。如果您继续学习并了解功能,应该更好地解释它。你用什么来学习C?我强烈建议放弃你所拥有的并获得 K&R。
指针一般用来存放变量的地址。这里在第一种情况下&x
给出地址x
和变量*ptr
的值存储在地址值存储ptr
指向的位置。在第二种情况下,ptr 存储变量 x 的地址。
#include<stdio.h>
#include<inttypes.h>
int main()
{
int x = 10;
int *ptr = &x;
uintptr_t p = (uintptr_t)&x;
printf("x=%d ,*ptr=%d,ptr=%p,p=%p",x,*ptr,ptr,p);
return 0;
}
在我的机器上,这段代码产生:
x=10 ,*ptr=10,ptr=0028FF14,p=0028FF14
指针是内存地址,对。
但它不一定指向单个变量。
想象一下:
int * x = malloc( 2 * sizeof( int ) );
您在这里有一个指向可以包含 2 个整数的内存区域的指针。
所以:
x[ 0 ]
是第一个 intx[ 1 ]
是第二个 int取消引用时,您将在该示例中分配第一个:
*x = 0; // same as x[ 0 ] = 0
关于你的第三个问题,确实有区别:
int x = 10;
int *ptr = &x; // As expected, ptr is now a pointer to x
int x = 10;
int ptr = &x; // Certainly not what you expect as ptr is not a pointer
在第二种情况下,您将内存地址分配给整数。它不是指针,如果指针的长度与 int 不同(例如,在 64 位平台中),您将遇到问题。
指针很好,因为你可以做算术。
int * x = malloc( 2 * sizeof( int ) );
int * x2 = x; // So we don't touch x, as we'll need to free it.
x2++; // Now x2 points to the second integer.
x2[ 0 ] = 0; // same as x[ 1 ] = 0;
增量是根据指针类型完成的。这通常是魔术开始的地方。
如果我们在一个 short 大小为 2 的平台上,你可以想象如下:
short * x = malloc( 2 * sizeof( short ) );
char * x2 = ( char * )x;
由于 a 的大小char
为 1,您现在可以访问之前短指针的每个字节(4 个字节,因为我们分配了其中两个)。
所以:
x2[ 0 ] -> First short / First byte
x2[ 1 ] -> First short / Second byte
x2[ 2 ] -> Second short / First byte
x2[ 3 ] -> Second short / Second byte
最后,指针在处理结构时也很好,例如,您需要将它们作为参数传递。
在这种情况下,使用指针将传递结构地址。否则,将需要在调用时复制结构,这根本没有效率。
void foo( struct x p ); // Structure data will be copied
void bar( struct x * p ); // Only the address will be passed, no data copied