手动单步执行C中的结构

时间:2017-11-06 23:19:02

标签: c pointers struct

我不明白为什么我不能用这种方式打印出b和c:

#include "stdio.h"

typedef struct{
  int a;
  int b;
  int c;
} myStruct;

int main(){

  myStruct MS;
  MS.a = 13;
  MS.b = 27;
  MS.c = 39;

  myStruct* pMS = &MS;

  printf("pMS = %u\n", pMS );
  printf("&a = %u\n", &pMS->a );  // addr of a is addr of struct
  printf("&b = %u\n", &pMS->b );  // addr of b is +4 from a
  printf("&c = %u\n", &pMS->c );  // addr of c is +8 from a

  printf("*pMS = %d\n",*(pMS) );
  printf("*pMS+4 = %d\n",*(pMS+4) );
  printf("*pMS+8 = %d\n",*(pMS+8) );
}

终端显示b和c的虚假值(至少,我认为b和c应位于pMS + 4和pMS + 8):

gcc version 4.6.3

pMS = 1926301980
&a = 1926301980
&b = 1926301984
&c = 1926301988
*pMS = 13
*pMS+4 = 32765
*pMS+8 = 32765

3 个答案:

答案 0 :(得分:3)

pMS+4 整数的地址,位于结构开头之外的四个字节。指针的添加是根据指针类型进行缩放的,因此,如果它是指向整数的指针,则它将是超出开始的四个整数(如果你有32位整数,则为16个字节)。例如,见:

int someInt[2] = {4, 9}; // assume 4-byte int, big-endian
int *x = &someInt[0];

// | someInt[0] @ 0x1000 | someInt[1] @ 0x1004 |
// |       0,0,0,4       |       0,0,0,9       |
// |     x = 0x1000      |     x+1 = 0x1004    |
// |      *(x) = 4       |      *(x+1) = 9     |

然而,在你的情况下甚至更糟,因为你的指针是实际的结构。这意味着它按整个结构的大小缩放,三个完整的整数(如果需要加上填充。)

答案 1 :(得分:1)

此行例如

printf("*pMS+4 = %d\n",*(pMS+4) );

当你添加到指针时,它与索引到数组相同,所以等于:

printf("*pMS+4 = %d\n", pMS[4]);

当然没有数组,所以传递了伪结构值。

然后printf根本无法打印结构,%d会打印一些非感性的东西。

换句话说,

双重未定义的行为。

答案 2 :(得分:-1)

通过对代码执行一些更改并实现@Jonathan Leffler对评论的评论,这种方式对我有用。

<强> 代码

#include <stdio.h>

struct t{
    int a;
    int b;
    int c;
};

int main(){

    struct t MS;
    MS.a = 13;
    MS.b = 27;
    MS.c = 39;

    struct t* pMS = &MS;

    printf("pMS =%p\n", pMS );

    printf("&a = %p\n", &pMS->a );  // addr of a is addr of struct
    printf("&b = %p\n", &pMS->b );  // addr of b is +4 from a
    printf("&c = %p\n", &pMS->c );  // addr of c is +8 from a

    printf("*pMS.a = %d\n", (* ((int *) ((void *) pMS))));//13

    printf("*pMS.b = %d\n", (* ((int *) ((void *) pMS)+1)));//27

    printf("*pMS.c = %d\n", (* ((int *) ((void *) pMS)+2)));//39

    return 0;

}

但是这也有效,正如你在@hyde中看到的那样用这种方式工作并不好。 C中的结构根据CPU体系结构打包,编译器意味着将struct成员放在打包形式中,例如在struct下面需要8个字节而不是5个。

struct example {
 int a;
 char b;
};  

我用我的方式,因为我之前检查过,sizeof(void *), sizeof(int *)都返回4。

考虑以下几点:

printf("*pMS.a = %d\n", (* ((int *) ((void *) pMS))));//13

printf("*pMS.b = %d\n", (* ((int *) ((void *) pMS)+1)));//27

printf("*pMS.c = %d\n", (* ((int *) ((void *) pMS)+2)));//39

为什么在括号内使用+1会导致访问我们的整数?

在我的系统中,整数数字占用4个字节,正如我在上面表达的void *int *的大小相同4字节。

首先删除void *以简化(int *) (pMS) + 1,将pMS表示为整数大小,当我们向其添加一个时,跳转到下一个指向27的字节地址。

我认为添加void *用于从代码中删除UB,因为指针具有void *类型指向结构的指针。

对于这种情况,删除void *也有效。

最后

printf("pMS =%u\n", sizeof(MS) );//12(3 * 4)
printf("pMS =%u\n", sizeof(pMS) );//4

在我的平台上没有任何填充或打包,如上所示,整个结构保持12字节3整数,大小为4字节。 但pMs是指向此结构的第一个地址的指针,因为第一个元素是整数,它有4个字节的大小。

<强> TODO

  1. 编辑帖子以添加关于指针类型和算术的进一步解释。
  2. 在这种情况下使用结构的两种方式之间存在一些额外的差异。
  3. 结构填充,打包取决于平台,操作系统,编译器。
  4. 为什么我们在main中声明Global或Local时会看到不同的结构地址,在这种情况下该情况会有不同的行为?