我正在阅读一本关于数据结构的书,并且难以理解指针的概念。让我说,我没有很多与C经验前言本但在这里不用....了解指针的用途
如果我做了以下内容:
int num = 5;
int *ptrNum;
ptrNum = #
这是我的理解是,指针为32位int保留足够的内存以及实际指针所需的内存,尽管它的值仅仅是变量的内存地址。
如果保留相同数量的内存,这样做的目的是什么?为什么我会使用指针而不是变量num?我完全脱离基地吗?
我正在阅读一本关于数据结构的书,并且难以理解指针的概念。让我说,我没有很多与C经验前言本但在这里不用....了解指针的用途
如果我做了以下内容:
int num = 5;
int *ptrNum;
ptrNum = #
这是我的理解是,指针为32位int保留足够的内存以及实际指针所需的内存,尽管它的值仅仅是变量的内存地址。
如果保留相同数量的内存,这样做的目的是什么?为什么我会使用指针而不是变量num?我完全脱离基地吗?
您可以在值无效的情况下使用指针。在你的例子中,你是正确的;没有好处。该archtetypal边界线有用的例子是交换功能:
void swap_int(int *i1, int *i2)
{
int t1 = *i1;
*i1 = *i2;
*i2 = t1;
}
调用顺序:
int main(void)
{
int v1 = 0;
int v2 = 31;
printf("v1 = %d; v2 = %d\n", v1, v2);
swap_int(&v1, &v2);
printf("v1 = %d; v2 = %d\n", v1, v2);
return 0;
}
如果你写了没有使用指针 - 这样的:
void swap_int(int i1, int i2)
{
int t1 = i1;
i1 = i2;
i2 = t1;
}
int main(void)
{
int v1 = 0;
int v2 = 31;
printf("v1 = %d; v2 = %d\n", v1, v2);
swap_int(v1, v2);
printf("v1 = %d; v2 = %d\n", v1, v2);
return 0;
}
那么你只需交换函数中的两个局部变量而不影响调用函数中的值。使用指针可以影响调用函数中的变量。
参见:
scanf()
的功能 - 家庭strcpy()
等我的理解的是,指针的储备足够的内存为32位的int以及实际指针所需的内存,尽管它的值只是内存地址变量。
你似乎是描述什么是仿佛:
int *p1;
做相同的工作为:
int _Anonymous;
int *p1 = &_Anonymous;
,没有关系。这是C.创建p1
为指针分配足够的空间。如第一次写入,它不初始化它,因此它指向一个不确定的位置(或没有位置)。它(指针)在使用之前需要初始化。因此:
int i1 = 37;
int *p1 = &i1;
但p1
分配仅保留用于指针足够的空间(通常,对于32位编译32位,对于一个64位编译64位);你必须分配它指向的空间,并且你必须初始化指针。初始化指针的另一种方法是使用动态分配的内存:
int *p2 = malloc(1000 * sizeof(*p2));
if (p2 != 0)
{
...use p2 as an array of 1000 integers...
free(p2);
}
您已覆盖的结构吗?如果不是,涵盖结构的例子(如树或链表)将无济于事。然而,一旦你已经覆盖的结构也一样,你就可以使用树或链接的列表:倚重三分球
struct list
{
int data;
struct list *next;
};
struct tree
{
int data;
struct tree *l_child;
struct tree *r_child;
};
这种结构依靠正确连接条目。
如何将一个元素添加到动态列表?通过每次创建一个新的数组?
您只需添加指向下一个元素的指针,并将前一个单元格的下一个指针链接到它。
如果没有指针,则会受限于数组的顺序和变量的对齐。 使用指针,您可以选择分配区域中的任何地址,以使您具有任何对齐方式,您可以使用指向和来自您分配的任何区域的列表元素。
所以,指针给你更多的自由,而每个指针只需要32或64位空间。
其他一些答案的重点是获取变量的地址并将其存储在指针中。这只是指针的一个用途。指针的一个完全不同的用途是指向动态分配的存储以及构建存储。
例如,假设您想读取文件并在内存中进行处理。但是,你不知道文件有多大。你可以把任意的上限,在你的代码:
#define MAX_FILE_SIZE (640 * 1024) /* 640K should be large enough for anyone */
char data[ MAX_FILE_SIZE ];
这浪费内存较小的文件,而不是更大的文件足够大。更好的方法是实际分配你需要的东西。例如:
FILE *f = fopen("myfile", "rb");
off_t len;
char *data;
fseek(f, 0, SEEK_END); /* go to the end of the file */
len = ftell(f); /* get the actual file size */
fseek(f, 0, SEEK_SET); /* rewind to the beginning */
data = malloc(len); /* Allocate just as much as you need */
另一个主要使用指针是组织数据,在列表中,或树木,或其他有趣的结构说。 (你的数据结构书会涉及到其中的许多内容。)如果你想重新组织你的数据,移动指针往往比复制数据要便宜得多。例如,假设您有这些列表:
struct mystruct
{
int x[1000];
int y[1000];
};
这是大量的数据。如果你只是存储在一个数组,然后整理这些数据可能是非常昂贵的:
struct mystruct array[1000];
尝试对qsort
...这将是非常缓慢的。
您可以通过改为存储指向元素的指针并对指针进行排序来加速。即。
struct mystruct *array[1000];
int i;
struct mystruct *temp;
/* be sure to allocate the storage, though: */
temp = malloc(1000 * sizeof(struct mystruct));
for (i = 0; i < 1000; i++)
array[i] = temp + i;
现在,如果你有这些结构排序,你会可以用三分球array[]
而不是整个结构。
我不会进入您的书中更好涵盖的发烧友数据结构。但是,我想我可能会给你一些指针的其他用途。
指针服务3个主要用途在C:
假传递引用语义:在C中,所有函数参数都是按值传递的。考虑下面的代码片段:
void foo(int a, int b)
{
a = 1;
b = 2;
}
void bar(void)
{
int x=0, y=1;
foo(x, y);
printf("x = %d, y = %d\n", x, y);
}
形式参数a
和foo
b
与实际参数x
和bar
y
,所以a
和b
任何更改内存不同对象x
和y
都没有体现出来。输出将是“x = 0,y = 1”。如果你想foo
改变的x
和y
值,则需要指针传递给这些变量来代替:
void foo(int *a, int *b)
{
*a = 1;
*b = 2;
}
void bar(void)
{
int x = 0, y = 1;
foo(&x, &y);
printf("x = %d, y = %d\n", x, y);
}
这一次,正式参数a
和b
是指针的变量x
和y
;写入表达式*a
和*b
int foo
相当于在bar
中写入x
和y
。因此,输出是“x = 1,y = 2”。
这是如何scanf()
和其他库函数的分数工作;他们使用指针参考我们想要操作的实际内存。
跟踪动态分配的内存:库函数malloc
,calloc
,并realloc
使我们能够在运行时内存分配,并且所有三个指针返回到分配的内存(如C89,所有的三个回程void *
)。例如,如果我们想在运行时分配的int
数组:
int *p = NULL;
size_t numItems;
// get numItems;
p = malloc(sizeof *p * numItems);
if (p)
{
// do stuff with p[0] through p[numItems - 1];
}
free(p);
指针变量p
包含的内存足够大,新分配的块的地址举行numItems
整数。我们可以通过解除引用p
使用*
运算符或[]
下标运算符(*(p+i) == p[i]
)来访问该内存。
那么,为什么不只是声明一个大小为numItems
的数组并且完成它呢?毕竟,作为C99的,你可以使用一个可变长度的数组,其中大小不必直到运行时被称为:
// get numItems
int p[numItems];
三个原因:第一,是没有得到普遍支持VLA的,并作为2011年标准,VLA支持现在是可选的;其次,声明后我们不能改变数组的大小,而我们可以使用realloc
来调整我们分配的内存块的大小;最后,沃拉斯被限制都在可以使用它们以及如何大的他们可以是 - 如果你需要在运行时分配的记忆很多,最好通过malloc/calloc/realloc
比VLAS做到这一点。
上指针运算快速注意:对于任何指针T *p
,表达p+1
将评估为T
类型的下一个元件的地址,这是不necessariy地址值+ 1,例如:
T sizeof T Original value of p p + 1
- -------- ------------------- -----
char 1 0x8000 0x8001
int 4 0x8000 0x8004
double 8 0x8000 0x8008
构建动态数据结构:有些时候,我们希望将数据存储在这样一种方式,可以很容易地插入新元素到一个列表,或快速搜索的值,或强制访问的特定顺序倍。有许多用于这些目的的不同数据结构,几乎所有情况下都使用指针。例如,我们可以使用一个二叉搜索树组织以这样的方式我们的数据搜索特定值是相当快的。树中的每个节点有两个孩子,分别指向到树的下一个元素:
struct node {
T key;
Q data;
struct node *left;
struct node *right;
};
的left
和right
成员指向树中的其他节点,或NULL如果没有孩子。通常情况下,left
child指向其值是某种当前节点的值“小于”的一个节点,而right
child指向其值是某种当前节点“大于”的节点。我们可以搜索树的值,像这样:
int find(struct node *root, T key, Q *data)
{
int result = 0;
if (root == NULL) // we've reached the bottom of the tree
{ // without finding anything
result = 0;
}
else if (root->key == key) // we've found the element we're looking for
{
*data = root->data;
result = 1;
}
else if (root->key < key)
{
// The input key is less than the current node's key,
// so we search the left subtree
result = find(root->left, key, data);
}
else
{
// The input key is greater than the current node's key,
// so we search the right subtree
result = find(root->right, key, data);
}
return result;
}
假设树是平衡的(即,在左子树的元素个数等于在右子树的元素个数),则检查元件的数目为大约登录 N,其中N是在树中元素的总数量。
指针不保留任何东西,他们只是持有数量没有什么比另一个值的地址了。 – James
用一个人为的例子来解释整个情况并不能解释。许多事情根本不可能不使用指针,并考虑到这一点;如果这不是一个整数,而是一个庞大的数组呢?你想复制整个数组来传递它吗?如果一个函数需要改变它的输入呢? –
我的书特别指出,它们占用的内存空间等于它指向的数据类型。 –