【问题标题】:Dynamic Memory Allocate, how to use pointer correctly when dealing with Structure?动态内存分配,处理结构时如何正确使用指针?
【发布时间】:2017-11-22 09:47:42
【问题描述】:

我现在正在学习动态内存分配,很难理解,尤其是当讲师提到DMA和使用指针的结构时。

首先,在我的代码的第 1 行,我对“&ptr->eno”的含义感到困惑。我们将ptr作为指针初始化,所以它意味着ptr保存了我们保留的内存地址,假设ptr保存了地址2046,“&ptr->eno”是指将值写入它指向的2046地址吗?

其次,在第 2 行,我如何打印出值,因为“ptr->eno”包含值“2046”,而不是当我打印出来时,它会给我数字 2046,而不是我尝试的值存储在内存位置 2046 中。

我的代码是从讲义中复制的,我尝试在 Visual Studio 上运行它,在我输入值后它崩溃了。我对 C 很陌生,我的假设可能看起来很愚蠢而且很难理解。如果你不能理解我的解释,请告诉我如何使用结构指针,也许我能弄清楚我的错误。谢谢

#include<stdio.h>
#include<stdlib.h>
struct emp
{
    int eno;
    char ename[20];
    float esal;
};
void main()
{
    struct emp* ptr;
    ptr = (struct emp*)malloc(sizeof(struct emp));
    if (ptr=NULL)
    {
        printf("out of memory error");
    }
    else
    {
        printf("enter the value of employee: \n");
        scanf_s("%d%s%f", &ptr->eno, ptr->ename, &ptr->esal); // line 1
    }
    printf("the name is: %s \t the number: %d \t the salary: %f", ptr->ename, ptr->eno, ptr->esal); //line2
}

【问题讨论】:

  • @StoryTeller 我的错误,已修复
  • 请记住,指针只是一个变量,它将其他东西的地址存储为它的值。所以int a=5;int *b = &amp;a; 指针b 只是存储a 的地址作为它的值。当您使用malloc, calloc or realloc 动态分配时,这些函数会保留所请求大小的内存块并返回内存块开头的地址。因此,您只是将地址存储到分配返回的指针变量中的内存块中。
  • @DavidC.Rankin:这是您今天发表的第二条(我遇到的)与指针相关的评论,我很喜欢这些评论! +1 给它。
  • 它怎么会崩溃那里?!您的代码在那里有ptr = NULL,它甚至没有进入 input 分支。
  • @WedaPashi - 是的,这似乎是新 C 程序员在指针方面遇到的最大绊脚石之一。因此,如果这看起来是他们正在努力解决的问题,我会尝试发表评论。 (这意味着我最终放弃了很多:)

标签: c pointers


【解决方案1】:

假设

if (ptr=NULL)

是一个错字(因为后面的“我输入值后它崩溃了”不可能发生),这里有一个错误:

scanf_s("%d%s%f", &ptr->eno, ptr->ename, &ptr->esal);

缺少%s 所需的大小参数

scanf_s("%d%s%f", &ptr->eno, ptr->ename, 20, &ptr->esal);

或者代替硬编码,也许

scanf_s("%d%s%f", &ptr->eno, ptr->ename, (unsigned)sizeof ptr->ename, &ptr->esal);

【讨论】:

  • 这使它按预期工作,我尝试仅使用 %19s 但它不起作用,但添加 sizeof (ptr->ename) ,或简单地添加 20,使其按预期工作。
  • %19s 将是纯 scanf 的解决方案。
  • 请注意,需要转换 (unsigned)(unsigned int),因为 sizeof 的结果本身可能不是参数的正确大小。在某些系统上,sizeof 可能会返回 8 字节大小的值,但 scanf_s 需要 int 大小的参数。
  • 你好,我很好奇,内存的大小是字节,假设返回无符号整数值。它安全但有必要吗?
  • sizeof 返回一个 size_t 类型的值,而不是 unsigned int。在我的系统上,它们都是 4 个字节,但可能不是,这就是应该使用强制转换的原因。出于同样的原因,在打印尺寸时,您应该使用%zu 而不是%u
【解决方案2】:

这个:

if (ptr=NULL)

没有做你想做的事。您需要比较运算符==,而不是赋值运算符=。这将始终将ptr 设置为 NULL,从而使其余代码具有未定义的行为(这在实践中通常会导致崩溃)。启用并查看编译器警告是避免此问题的好方法。

对于实际问题,&amp;ptr-&gt;eno 表示“ptr 指向的结构中的字段eno 的地址”。在实践中,它将评估为ptr 的值,加上从结构开始到字段eno 的偏移量,在这种情况下将为0,因为eno 是第一个字段。

此外,printf() 当然应该在else 内,因为只有在ptr 不为NULL 时才能运行它。

而且,重要的是:您必须为scanf_s() 提供字符串的缓冲区大小。):

scanfwscanf 不同,scanf_swscanf_s 需要为cCsS 或字符串类型的所有输入参数指定缓冲区大小包含在[] 中的控制集。以字符为单位的缓冲区大小作为附加参数传递,紧跟在指向缓冲区或变量的指针之后。

所以,应该是:

const int num = scanf_s("%d%s%f", &ptr->eno, ptr->ename, (unsigned int) sizeof ptr->ename, &ptr->esal);
if (num == 3)  // All conversions succeeded, we can rely on them
{
  printf("the name is: %s \t the number: %d \t the salary: %f", ptr->ename, ptr->eno, ptr->esal); //line2
}

并且你应该检查它的返回值,否则如果由于某种原因输入失败,你可能会使用未初始化的变量。

请注意,缓冲区的大小似乎指定为unsigned int,而不是size_t,这在我看来不是最佳的,因此需要强制转换。

顺便说一句,“DMA”通常表示“direct memory access”,与此无关;这让我困惑了一段时间。

【讨论】:

  • 顺便说一句,如果 OP 在编译时启用了所有警告(例如gcc -Wall -Wextra -g),他会因错误if 测试而被警告
  • 你好,我编辑了它,我输入值后它仍然崩溃。
  • scanf_s("%d%s%f", &amp;ptr-&gt;eno, ptr-&gt;ename, &amp;ptr-&gt;esal); 是可疑的。请参阅[ scanf_s documentation ]备注部分
  • @sjsam 这不是可疑的,但是错了,我将删除的评论转换为答案。
  • @WeatherVane 确实,我只是指编写此类语句的做法。在那里很难找出错误。
【解决方案3】:

强制转换malloc 的输出(检查[ this ])不是一个好习惯,最简单的方法是

struct emp *ptr;
ptr = malloc(1 * sizeof *ptr);

那么,==用于逻辑比较,所以

if (NULL == ptr)
/* = changed to ==, also put the value first which will help you easily
* fix accidental omission of the second =
*/
{
    printf("out of memory error");
    exit(1); // Exiting with a non-zero status
} 

注意这里应该作为 [ this ]答案的补充

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-03
    • 2022-01-01
    • 2013-04-18
    • 2019-04-23
    • 1970-01-01
    • 2015-07-04
    • 2023-04-02
    相关资源
    最近更新 更多