结构变量的值不是在指针操作中按预期打印

时间:2018-03-29 07:00:30

标签: c pointers malloc structure

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct type {
    int a;
    char b[10];
}MyType;

MyType* p;
void appenddata(MyType* p);
void cleanup(MyType* pointer);

void mycode() {
  p = (MyType *)malloc(sizeof(MyType));

  if(p != NULL)
  {
    p->a = 10;
    strcpy(p->b, "sample");
    printf("p->a - %d\n",p->a);
    printf("p.b - %s\n",p->b);
  }

  appenddata(p);

  /* set the values for p and do some stuff with it */
  cleanup(p);
  if(p != NULL)
  {
    printf("p->a - %d\n",p->a); << Why value I am getting is '0' here and 
    printf("p.b - %s\n",p->b);  << No value is printed here
  }

}

void appenddata(MyType* p)
{
  if(p != NULL)
  {
    p->a = 10;
    strcpy(p->b, "sample appenddata");
    printf("p->a - %d\n",p->a);
    printf("p.b - %s\n",p->b);
  }
  else
  {
      printf("p is null\n");
  }
}

void cleanup(MyType* pointer) {
  free(pointer);
  pointer = NULL;
}

int main()
{
    printf("Hello, World!\n");
    mycode();
    return 0;
}

输出:

Hello, World!
p->a - 10
p.b - sample
p->a - 10
p.b - sample appenddata
p->a - 0
p.b - 

在上面的节目中我为什么会得到&#39; 0&#39;并且mycode功能中没有显示任何数据,它应该是&#39; 10&#39;&#39; sample&#39;应该打印,程序有什么问题?,据我所知,当我们进行清理时,我不会作为参考传递,所以它不应该影响结构的本地副本。 请澄清我的疑问

4 个答案:

答案 0 :(得分:2)

您的程序有几个未定义的行为。

第一个未定义的行为:

struct type中,您将成员b作为10个字符数组:

char b[10];

在函数appenddata()中,您要将长度超过10个字符的字符串复制到b

strcpy(p->b, "sample appenddata");
              ^^^^^^^^^^^^^^^^^

访问数组越界是未定义的行为。

要解决此问题,请增加数组b的大小,例如b [50],并使用strncpy,这样可以控制要复制到目标的字符数。在使用之前请仔细阅读strncpy

第二个未定义的行为:

释放动态分配的内存块时,其生命周期结束。

来自C标准#6.2.4p2

  

对象的生命周期是程序执行的一部分,在此期间保证为其保留存储。一个对象存在,具有一个常量地址,33)并在其整个生命周期内保留其最后存储的值.34)如果一个对象在其生命周期之外被引用,则该行为是未定义的。当指针指向(或刚刚过去)的对象到达其生命周期结束时,指针的值变得不确定。    [强调我的]

在此,您在释放后访问p

  if(p != NULL)
  {
    printf("p->a - %d\n",p->a); << Why value I am getting is '0' here and 
    printf("p.b - %s\n",p->b);  << No value is printed here
  }

导致未定义的行为。这是因为if条件p != NULL,即使在true中调用p后,cleanup()也是cleanup()。在p函数中,您释放指针NULL,但将pointer分配给MyType *类型的局部变量p = NULL;。相反,您应该在调用cleanup()函数后执行{{1}}。

答案 1 :(得分:1)

  

据我所知,当我们进行清理时,我不会作为参考传递,因此它不应该影响结构的本地副本。请澄清我的疑问

你是部分正确的。 在“清理”中,您只有p的副本。为其分配NULL不会改变任何内容。

但是您将全局p的值传递给该函数,然后调用free。 这不会改变p,但会释放它所指向的内存。

调用cleanup后,您不能再次访问该内存位置。

您的期望

10
sample

基于错误的假设。 你可以获得任何价值,因为这是未定义的bahaviour。

顺便说一句: 如果内存无效,你会得到

10
sample appenddata

这是由于同样的原因。

appenddata中,您不会更改p,但是在您离开该功能后更改p->b仍然存在。

答案 2 :(得分:0)

我认为您的问题出在cleanup(p) 在打印数据之前,你不应该这样做。

答案 3 :(得分:0)

要避免与free添加#include<stdlib.h>相关的警告,请在清理时清理结构数据并取消引用本地指针而不是主指针。因此主指针成为悬空指针。

在demo中添加了一些printf:

Hello, World!
p->a - 10
p.b - sample
p->a - 10
p.b - sample appenddata
Pointer Address before cleanup : 0x14a5020
Pointer Address before free : 0x14a5020
Pointer Address after free : (nil)
Pointer Address after cleanup : 0x14a5020
p->a 1- 0
p.b 2-  

因此试图打印悬挂指针的垃圾。

为了避免你可以做任何一种方式。

将清理功能替换为MACRO

#define cleanup(p) free(p); p=NULL

或从清理功能

返回指针
MyType* cleanup(MyType* pointer);

....................
 /* set the values for p and do some stuff with it */
p = cleanup(p);
.......................

MyType* cleanup(MyType* pointer) {

  free(pointer);
  pointer = NULL;
  return pointer;
}