内存、指针和指向指针的指针

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 个 chars,而不是 10 个内存地址。但是str 将指向 10 个chars 中的第一个,是的。

现在,如果我们能更进一步。

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()返回的地址。

bufferstr的地址,按值传递给函数。分配给buffer 只会改变那个(本地)指针值。

另一方面,分配给*buffer 与分配给str 相同。在您的file_read() 返回后,调用者将“看到”对str 的更改。


虽然,我不知道为什么会这样:*((*buffer) + i) = (char)c;

bufferstr 的地址。 *buffer 基本上与 str 相同——指向 char(数组)的指针。 (*buffer) + i) 是指针算术 - 指针 *buffer 加上 i 表示指向数组的第 i 个元素的指针。 *((*buffer) + i) 正在取消引用指向 ith 元素的指针——单个 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 **bufferbuffer 代表指向 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,还是野指针。

二级指针的用法?

指向指针的指针的理解和应用

C语言中 内存消亡 指向她的指针就一定消亡或成了空指针为啥是错的啊

QT调试时怎么查看某个指针指向的一片内存区域的信息