指针减法给出奇怪的结果 [英] Pointer subtraction gives weird result

查看:149
本文介绍了指针减法给出奇怪的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

code:

  int main()
{
Z * a =(Z *)malloc(sizeof(Z)* 8);
Z * b =(Z *)malloc(sizeof(Z)* 8);
printf(%lu \\\
,sizeof(Z));
printf(%p%p \\\
,b,a);
printf(%lu \\\
,b - a);
}

输出:

  12 
0x89a080 0x89a010
12297829382473034420

为什么在最后一行的价值如此之大?真正的地址差异是0x70(堆分配头的16个字节加上数组a的12 * 8个字节),所以从指针的算术我期望值0x70 / 12 = 9。(3)或9被转换为整数。我知道减法指针并不指向相同的数组,但我期望更合理的结果,这会让我知道内存映射如何。
它是在64b Ubuntu和gcc 4.8.2上编译的。



汇编:

  .fileaa.c
.section .rodata
.LC0:
.string%lu\\\

.LC1:
.string%p%p \\\

.text
.globl main
.type main,@function
main:
.LFB2:
.cfi_startproc
pushq%rbp
.cfi_def_cfa_offset 16
.cfi_offset 6,-16
movq%rsp,%rbp
.cfi_def_cfa_register 6
subq $ 16,%rsp
movl $ 96,%edi
调用malloc
movq%rax,-16(%rbp)
movl $ 96,%edi
调用malloc
movq%rax,-8(%rbp)
movl $ 12,%esi
movl $ .LC0,%edi
movl $ 0,%eax
call printf
movq -16(%rbp),%rdx
movq -8(%rbp),%rax
movq%rax,%rsi
movl $ .LC1,%edi
movl $ 0,%eax
调用printf
movq -8(%rbp),%rdx
movq -16(%rbp),%rax
subq%rax,%rdx
movq%rdx,%rax
sarq $ 2,%rax
movq%rax ,%rdx
movabsq $ -6148914691236517205,%rax
imulq%rdx,%rax
movq%rax,%rsi
movl $ .LC0,%edi
movl $ 0,%eax
call printf
leave
.cfi_def_cfa 7,8
ret
.cfi_endproc
.LFE2:
.size main, 。 - main
.identGCC:(Ubuntu 4.8.2-19ubuntu1)4.8.2
.section .note.GNU-stack,,@ progbits

解决方案

从草稿1570开始,这是


§ 6.5.6添加运算符


  1. 当减去两个指针时,都应指向相同数组对象的元素,或者超过数组对象的最后一个元素;结果是两个数组元素的下标差异。结果的大小是实现定义的,其类型(有符号整数类型)是< stddef.h>中定义的 ptrdiff_t / code>标题。如果结果在该类型的对象中不可表示,则行为是未定义的。换句话说,如果表达式 P Q 分别指向第i个和第j个元素对于一个数组对象来说,表达式(P) - (Q)的值为 i-j ptrdiff_t 类型的对象中。此外,如果表达式P指向
    的一个元素或一个数组对象的最后一个元素,并且表达式 Q 指向
    到同一个数组对象的最后一个元素,表达式((Q)+1) - (P)与$ <$ c具有相同的
    值((Q) - (P))+ 1
    - ((P) - ((Q)+1)) ,如果
    表达式 P 指向一个超过数组对象的最后一个元素,即使
    表达式(Q)+1 不指向数组对象的元素。


你不能假定这些指针是连续的,但是如果它们是实际问题,那么指针上的算术运算的方式是因为poitners的类型为 Z (void *)b - (void * a)!= b - a

检查这个例子来说明我的意思是什么

  Z * a; 
Z * b;

Z * a = malloc(sizeof(Z)* 16);
if(a == NULL)
return -1;
b = a + 8;

printf(%lu \\\
,sizeof(Z));
printf(%p,%p \ n,b,a);

printf(%lu \\\
,(ptrdiff_t)(b - a)); / *这会给出错误的结果* /
printf(%lu \\\
,(ptrdiff_t)((void *)b - (void *)a));

请记住包含 stddef.h ptrdiff_t 类型。


I got structure:

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

code:

int main()
{
    Z *a = (Z*)malloc(sizeof(Z)*8);
    Z *b = (Z*)malloc(sizeof(Z)*8);
    printf("%lu\n", sizeof(Z));
    printf("%p %p\n", b, a);
    printf("%lu\n", b - a);
}

output:

12
0x89a080 0x89a010
12297829382473034420

Why value in last line is so huge? Real address difference is 0x70 (16 bytes of heap allocation header plus 12*8 bytes of elements of array a), so from pointer's arithmetic I would expect value 0x70/12=9.(3) or 9 casted to integer. I know that substracted pointers don't point on the same arrays but I expected more reasonable result which would give me clue how memory map looks like. It was compiled on 64b Ubuntu and gcc 4.8.2.

assembly:

    .file   "aa.c"
    .section    .rodata
.LC0:
    .string "%lu\n"
.LC1:
    .string "%p %p\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB2:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $96, %edi
    call    malloc
    movq    %rax, -16(%rbp)
    movl    $96, %edi
    call    malloc
    movq    %rax, -8(%rbp)
    movl    $12, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movq    -16(%rbp), %rdx
    movq    -8(%rbp), %rax
    movq    %rax, %rsi
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    movq    -8(%rbp), %rdx
    movq    -16(%rbp), %rax
    subq    %rax, %rdx
    movq    %rdx, %rax
    sarq    $2, %rax
    movq    %rax, %rdx
    movabsq $-6148914691236517205, %rax
    imulq   %rdx, %rax
    movq    %rax, %rsi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE2:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

解决方案

From the draft 1570, this is

§6.5.6 Additive operators

  1. When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, espectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i−j provided the value fits in an object of type ptrdiff_t. Moreover, if the expression P points either to an element of an array object or one past the last element of an array object, and the expression Q points to the last element of the same array object, the expression ((Q)+1)-(P) has the same value as ((Q)-(P))+1 and as -((P)-((Q)+1)), and has the value zero if the expression P points one past the last element of the array object, even though the expression (Q)+1 does not point to an element of the array object.

You cannot assume that the pointers will be contiguous, but in case they were the actual problem is the way arithmetic is peformed on the pointers because the poitners have type Z, and (void *)b - (void *a) != b - a.

So check this example to illustrate what I mean

Z *a;
Z *b;

Z *a = malloc(sizeof(Z) * 16);
if (a == NULL)
    return -1;
b = a + 8;

printf("%lu\n", sizeof(Z));
printf("%p, %p\n", b, a);

printf("%lu\n", (ptrdiff_t)(b - a)); /* this will give wrong result */
printf("%lu\n", (ptrdiff_t)((void *)b - (void *)a));

remember to include stddef.h for the ptrdiff_t type.

这篇关于指针减法给出奇怪的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆