无索引的指针算法的双指针内存分配访问问题

时间:2019-06-14 02:17:41

标签: c

我正在尝试使用双指针内存分配而不使用索引。我只允许使用指针算法访问地址。

char** p2 = (char**) malloc(sizeof(char) * 2);
*p2 = (char*)malloc(sizeof(char) * 3);


**p2 = 's';
printf("%c\n", **p2);
printf("%p\n", **p2);
printf("%c\n", p2[0][0]);
printf("%p\n\n", p2[0][0]);

(**p2)++;
**p2 = 'k';
printf("%c\n", **p2);
printf("%p\n", **p2);
printf("%c\n", p2[0][1]);
printf("%p\n\n", p2[0][1]);

After fail above code,

int* ptr = *p2;
*ptr = 'h';
printf("%c\n", *ptr);
printf("%i\n", *ptr);
printf("%c\n", p2[0][0]);
printf("%i\n\n", p2[0][0]);

(*ptr)++;
*ptr = 'a';
printf("%c\n", *ptr);
printf("%i\n", *ptr);
printf("%c\n", p2[0][1]);
printf("%i\n\n", p2[0][1]);

这也没有用。

因此,我能够访问til p2 [0] [0],返回s和0x73。

但是,当我尝试使用指针算法(** p ++)访问下一个索引时,** p2值和p2 [0] [1]不同。

我应该使用哪种指针算法来访问p2 [0] [1]和更多内容?

1 个答案:

答案 0 :(得分:0)

指针是相对简单的,但是在您的情况下,您似乎很难理解“我的指针是什么?” 茶叶之间的阅读距离稍远,这似乎是您的主要困惑环绕“与 '*' s有什么作用?”

让我们退后一步,回想一下,指针只是一个普通变量,将其他地址作为其值。 (即,它指向指向其他内容在内存中的存储位置)。通常您会想到int a = 5;其中a本身将5作为其值,而指针int *b = &a;则将a的地址保持(指向)值(即b指向存储5的内存中的地址)。

现在让我们考虑如何保持声明和指针的直接使用。当您声明指针时,例如char **p2;,您已声明 pointer-to-pointer-to char。因此,当您考虑可以分配给p2的内容时,您只需回答以下问题(1)我的指针是什么? (p2指针:什么? pointer-to char)和(2)什么是类型地址存储为它的值? (p2保留指向char)作为其值。

当您声明p2并尝试分配存储空间时,您使用的是哪种类型? sizeof(char)。现在应该清楚的是,p2并不将char的地址作为其值,而是将 pointer-to char的地址作为其值。值,例如(char*)

每次正确使用此方法的方法是使用取消引用的指针作为您的 type-size ,而不是尝试使用基本类型char, short, int, etc..和然后向其中添加'*',以使其正确。由于您都一次性声明并初始化了对malloc的调用,因此通过使声明的**部分与分配相同,可以掩盖简单性。而是以这种方式来思考,以解决您的想法。

char **p2;                      /* declare a pointer-to-pointer-char */
p2 = malloc (sizeof *p2 * 2);   /* use the dereferenced pointer to set typesize */

p2是您的指针,因此*p2设置类型。 (例如,p2是指向的指针到 char),因此在取消引用(即删除一级间接)时,将留下一个指向char指针。您需要分配哪些(指针)。因此sizeof *p2 * 2分配了一块内存,足以存储两个指向char的指针。您无法猜测要添加到'*'的{​​{1}}数目是多少,您只需要使用取消引用的指针即可-编译器会自动知道该类型需要多少存储空间。然后char将该内存块的起始地址分配给p2 = malloc ....

注意:您必须通过检查返回值来验证是否成功p2的调用,例如malloc分配失败。)< / p>

基本上就是这样。类型大小控制指针算法。因此,如果您有一个指向if (p2 == NULL)的指针(例如char),则char *p;会将指针前进1个字节,直到下一个 character 。如果您有一个指向p++;的指针(例如int),那么int *pi;会将指针前进4个字节,因此它指向下一个整数

以此为背景,您尝试执行的操作类似于以下简短示例。 (注意:我更改了输出格式以使其更容易阅读)

pi++;

注意:#include <stdio.h> #include <stdlib.h> int main (void) { /* don't cast the return of malloc, * use dereferenced pointer as typesize. * allocates 2 pointers, p2[0], p2[1] - unused. */ char **p2 = malloc (sizeof *p2 * 2); if (!p2) { /* validate every allocation */ perror ("malloc-p2"); return 1; } /* use p2[0] instead of *(p2 + 0), i.e. *p2 * allocates 3-chars of storage, assigns starting address to p2[0]. */ p2[0] = malloc (sizeof *p2[0] * 3); if (!p2[0]) { /* ditto */ perror ("malloc-p2[0]"); return 1; } **p2 = 's'; /* assigns 's' to first char of first pointer */ /* you can't advance p2 or you lose your original pointer (memory-leak) */ char *ptmp = p2[0]; /* use a temp pointer instead to 1st 3-chars */ ptmp++; /* advance the temp pointer */ *ptmp = 'k'; /* assign 'k' as 2nd char */ printf (" p2 (%p)\n %c (%p)\n %c (%p)\n", (void*)p2, p2[0][0], (void*)&p2[0][0], p2[0][1], (void*)&p2[0][1]); free (p2[0]); /* free storage */ free (p2); /* free pointers */ } 的地址无法更改。如果您为p2分配了其他内容或在p2之前添加了地址,则它不再指向开头分配的内存块中的所有内容,因此(除非保存指针的副本)该内存块将不再被释放,从而导致内存泄漏。)

使用/输出示例

p2++;

您在$ ./bin/ptr2ptrfun p2 (0x73a010) s (0x73a030) k (0x73a031) 上方看到的是指向2指针p2的内存块的指针,而0x73a010's'的存储地址是顺序的,在第二个对'k'的调用创建的3个字符的存储区中,已将起始地址分配给已分配的第一个指针(您分配的第二个指针仍未使用)

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,malloc是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

valgrind

始终确认已释放已分配的所有内存,并且没有内存错误。

简而言之就是指针。如果您还有其他问题,请告诉我。