内存、指针和指向指针的指针
Posted
技术标签:
【中文标题】内存、指针和指向指针的指针【英文标题】:Memory, pointers, and pointers to pointers 【发布时间】:2017-09-21 21:01:22 【问题描述】:我正在开发一个读取 .txt 文件的短程序。最初,我在 main 函数中玩耍,并且我的代码可以正常工作。后来,我决定把它抽象成一个函数。现在,我似乎无法让我的代码工作,而且我已经被这个问题挂断了很长一段时间。
我认为我最大的问题是我并不真正了解内存/硬件级别的情况。我知道指针只是保存一个内存地址,而指向指针的指针只是将内存地址保存到另一个内存地址,这是我们真正想要的简短的面包屑路径。
然而,既然我正在引入 malloc() 来扩展分配的内存量,我似乎忽略了正在发生的事情。事实上,我已经完全不知道该如何看待记忆了。
所以,一个字符占用一个字节,对吗? 如果我理解正确,那么由一个 char* 占用一个字节的内存? 如果我们有一个:
char* str = "hello"
假设它占用 6 个字节的内存(包括空字符)可以说安全吗?
而且,如果我们想为一些在编译时未知的“大小”分配内存,那么我们需要动态分配内存。
int size = determine_size();
char* str = NULL;
str = (char*)malloc(size * sizeof(char));
到目前为止,这在语法上是否正确? 现在,如果你要判断我的解释。我们告诉编译器我们需要为字符保留“大小”数量的连续内存。如果 size 等于 10,那么 str* 将指向 10 个内存地址中的第一个地址,对吗?
现在,如果我们能更进一步。
int size = determine_size();
char* str = NULL;
file_read("filename.txt", size, &str);
这是我的脚开始离开地面的地方。我的解释是 file_read() 看起来像这样:
int file_read(char* filename, int size, char** buffer)
// Set up FILE stream
// Allocate memory to buffer
buffer = malloc(size * sizeof(char));
// Add characters to buffer
int i = 0;
char c;
while((c=fgetc(file))!=EOF)
*(buffer + i) = (char)c;
i++;
将字符添加到缓冲区并分配内存是我似乎无法理解的事情。
如果 **buffer 指向 *str 等于 null,那么我如何为 *str 分配内存并为其添加字符?
我知道这很长,但我很感谢大家花时间阅读这篇文章!如果我能澄清任何事情,请告诉我。
编辑:
哇,我的代码现在可以运行了,非常感谢!
虽然,我不知道为什么会这样:
*((*buffer) + i) = (char)c;
【问题讨论】:
buffer = malloc(size * sizeof(char));
--> *buffer = malloc(size * sizeof(char));
str = (char*)(size * sizeof(char);
应该是 str = (char*)malloc(size * sizeof(char);
char c; while((c=fgetc(file))!=EOF) *(buffer + i) = (char)c;i++;
--> int c; while((c=fgetc(file))!=EOF && i < size-1) (*buffer)[i++] = c; (*buffer)[i] = 0;
那是小说,不是问题。学习How to Ask并提供minimal reproducible example。
@FaridKaradsheh 正如 BLUEPIXY 指出的那样,变量 c 应该声明为 int 类型而不是 char 因为通常 char 可以表现为无符号字符。在这种情况下,条件 (c=fgetc(file))!=EOF 将始终为真。:)
【参考方案1】:
那么,一个字符占用一个字节,对吗?
是的。
如果我理解正确,默认情况下 char* 占用一个字节的内存。
您的措辞有些模棱两可。 char
占用一个字节的内存。一个char *
可以指向一个char
,即一个字节的内存,或者一个char array,即多个字节的内存。
指针本身占用了不止一个字节。确切的值是实现定义的,通常是 4 字节(32 位)或 8 字节(64 位)。您可以使用printf( "%zd\n", sizeof char * )
检查确切的值。
如果我们有一个
char* str = "hello"
,可以假设它占用 6 个字节的内存(包括空字符)吗?
是的。
而且,如果我们想为一些在编译时未知的“大小”分配内存,那么我们需要动态分配内存。
int size = determine_size();
char* str = NULL;
str = (char*)malloc(size * sizeof(char));
到目前为止,这在语法上是否正确?
Do not cast the result of malloc。 sizeof char
根据定义总是1
。
如果 size 等于 10,那么 str* 将指向 10 个内存地址中的第一个地址,对吗?
是的。嗯,差不多。 str*
没有意义,它是 10 个 char
s,而不是 10 个内存地址。但是str
将指向 10 个char
s 中的第一个,是的。
现在,如果我们能更进一步。
int size = determine_size();
char* str = NULL;
file_read("filename.txt", size, &str);
这是我的脚开始离开地面的地方。我的解释是
file_read()
看起来像这样:
int file_read(char* filename, int size, char** buffer)
// Set up FILE stream
// Allocate memory to buffer
buffer = malloc(size * sizeof(char));
没有。你会写*buffer = malloc( size );
。这个想法是,您在函数内部分配的内存可以由函数的调用者寻址。所以调用者提供的指针——str
,在调用时是NULL
——需要改变。这就是调用者传递str
的地址 的原因,因此您可以将malloc()
返回的指针写入该地址。在你的函数返回后,调用者的str
将不再是NULL
,而是包含malloc()
返回的地址。
buffer
是str
的地址,按值传递给函数。分配给buffer
只会改变那个(本地)指针值。
另一方面,分配给*buffer
与分配给str
相同。在您的file_read()
返回后,调用者将“看到”对str
的更改。
虽然,我不知道为什么会这样:
*((*buffer) + i) = (char)c;
buffer
是str
的地址。
*buffer
基本上与 str
相同——指向 char(数组)的指针。
(*buffer) + i)
是指针算术 - 指针 *buffer
加上 i
表示指向数组的第 i
个元素的指针。
*((*buffer) + i)
正在取消引用指向 i
th 元素的指针——单个 char
。
然后分配给它(char)c
。
做同样事情的更简单的表达式是:
(*buffer)[i] = (char)c;
【讨论】:
我现在意识到我忘记了 malloc()。我没有完全复制我的来源。它会是 10 个还是 11 个内存地址中的第一个(空字符 +1)?谢谢,最后一点真的为我澄清了!另外,既然 char 等于 1,那么我们就不需要使用 sizeof() 了,对吧?这将使代码更有效。 次要:“可以放心地假设它占用了 6 个字节”——更可能的是 6 用于文字,4 或 8 用于指针。 @chux 是 4 还是 8 由于用零填充位置? @FaridKaradsheh:我也不太清楚 chux 是什么意思。指针本身占用了一些空间——现在通常是 4(32 位)或 8(64 位)字节。它指向的字符串字面量占用 6 个字节—— 5 个用于字符,1 个用于终止空字节。但是您指的是指针 指向 的内容,而不是指针本身占用的空间。 @FaridKaradsheh 取决于实现。【参考方案2】:char **buffer
,buffer
代表指向 char 的指针,*buffer
访问指向 char 的指针,**buffer
访问 char 值本身。
要传回指向新字符数组的指针,请写入*buffer = malloc(size)
。
要将值写入 char 数组,请写入 *((*buffer) + i) = c
,或(可能更简单)(*buffer)[i] = c
看下面的 sn-p 演示发生了什么:
void generate0to9(char** buffer)
*buffer = malloc(11); // *buffer dereferences the pointer to the pointer buffer one time, i.e. it writes a (new) pointer value into the address passed in by `buffer`
for (int i=0;i<=9;i++)
//*((*buffer)+i) = '0' + i;
(*buffer)[i] = '0' + i;
(*buffer)[10]='\0';
int main(void)
char *b = NULL;
generate0to9(&b); // pass a pointer to the pointer b, such that the pointer`s value can be changed in the function
printf("b: %s\n", b);
free(b);
return 0;
输出:
0123456789
【讨论】:
那么,'*buffer = malloc(size)' 正在改变 *str?我也是这么想的。你能解释一下你的第三段/句子中发生了什么吗? @Stephan Lechner *buffer[i] = c;是无效语句 应该是 (*buffer)[i] = c以上是关于内存、指针和指向指针的指针的主要内容,如果未能解决你的问题,请参考以下文章
C 语言指针数据类型 ( 指针类型变量 与 指针指向的内存块 概念区别 | 指针赋值 | 指针运算 | 内存赋值 | 内存取值 | 内存修改注意事项 )
结构体指针在使用完free后,该指针所指向的内存区域是啥,这个指针是变成了NULL,还是野指针。