繁体   English   中英

sizeof运算符在按位和C语言中失败

[英]sizeof operator fails on bitwise & in C

感谢您的回应,%zu说明符可以与sizeof运算符一起正常工作,可以正常打印size以及x和y值。 但是,当我将所有这些说明符都更改为%lld时,应打印实际的x和y值(因为它们很长),它会打印一些随机垃圾值。

    #include<stdio.h>

    int main()
    {
      long long x=500000000007;
      long long y=1;
      printf("size of x : %lld,x = %lld, size of y : %lld, y : %lld\n",sizeof(x),x,sizeof(y),y); 

      if(x & y)
      {
        printf("ODD IT IS :), x&1 = %lld, size of x&1 = %lld\n",x&y,sizeof(x&y) );//<--- this line
      }



      printf("After If statement ---> size of x : %lld,x = %lld\n",sizeof(x),x); 

      return 0;
    }

    Output on linux 32 bit system(Ubuntu) using gcc compiler
      size of x : 7661335479756783624,x = 34359738484, size of y : 1, y : -4160453359
      ODD IT IS :), x&1 = 1, size of x&1 = 34359738376
      After If statement ---> size of x : 7661335479756783624,x = 34359738484

现在的问题是-为什么变量x和y中的值受到sizeof运算符使用说明符的影响?

理论

问题是您没有使用正确的格式规范来打印sizeof()

printf("size of x : %d,x = %lld, size of y : %d, y : %lld\n", sizeof(x), x, sizeof(y), y); 

您正在使用%d ,它期望一个int来打印size_t ,它是(a)一种无符号类型,并且(b)最有可能是8个字节,而不是像int那样的4个字节。 打印size_t的正确方法(如sizeof的结果)是使用z修饰符:

printf("size of x: %zu, x = %lld, size of y: %zu, y = %lld\n", sizeof(x), x, sizeof(y), y);

如果您的库中不支持%zu ,则将sizeof(x)等的结果显式转换为int 但是,如果您long long提供支持,则该库不太可能不支持%zu

如果您使用GCC,应该已经向您警告了printf()的参数列表中的类型不匹配。

当您使用错误的类型时,会从printf()的堆栈中拾取错误的值。 您将4个单位的8字节压入堆栈,但告诉printf()读取4字节,8字节,4字节和8字节。


编译并修复代码

以原始代码sz1.c进行一些小修改:

#include <stdio.h>

int main(void)
{
    long long x=500000000007;
    long long y=1;

    printf("size of x : %d, x = %lld, size of y : %d, y : %lld\n", sizeof(x), x, sizeof(y), y); 
    printf("ODD IT IS :), x&1 = %d, size of x&1 = %lld\n", x&y, sizeof(x&y));
    printf("After If statement ---> size of x : %d, x = %lld\n", sizeof(x), x); 

    return 0;
}

然后编译它会给我很多警告(这是GCC 4.8.1,由于某种原因,它会为格式警告生成每个警告的两个副本-不错,但还不算太糟糕!):

$ gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition     sz1.c -o sz1
sz1.c: In function ‘main’:
sz1.c:8:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
     printf("size of x : %d, x = %lld, size of y : %d, y : %lld\n", sizeof(x), x, sizeof(y), y); 
     ^
sz1.c:8:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]
sz1.c:8:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
sz1.c:8:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]
sz1.c:9:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long long int’ [-Wformat=]
     printf("ODD IT IS :), x&1 = %d, size of x&1 = %lld\n", x&y, sizeof(x&y));
     ^
sz1.c:9:5: warning: format ‘%lld’ expects argument of type ‘long long int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
sz1.c:9:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long long int’ [-Wformat=]
sz1.c:9:5: warning: format ‘%lld’ expects argument of type ‘long long int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
sz1.c:10:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
     printf("After If statement ---> size of x : %d, x = %lld\n", sizeof(x), x); 
     ^
sz1.c:10:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
$

此版本sz2.c具有正确的类型说明符(因此它可以进行编译而不会发出任何警告):

#include <stdio.h>

int main(void)
{
    long long x=500000000007;
    long long y=1;
    printf("size of x : %zu, x = %lld, size of y : %zu, y : %lld\n", sizeof(x), x, sizeof(y), y); 
    printf("ODD IT IS :), x&1 = %lld, size of x&1 = %zu\n", x&y, sizeof(x&y) );//<--- this line
    printf("After If statement ---> size of x : %zu, x = %lld\n", sizeof(x), x); 
    return 0;
}

有趣的是,在带有GCC 4.8.1的Mac OS X 10.9.4上,两个程序的输出基本相同:

$ ./sz1 
size of x : 8, x = 500000000007, size of y : 8, y : 1
ODD IT IS :), x&1 = 1, size of x&1 = 8
After If statement ---> size of x : 8, x = 500000000007
$ ./sz2
size of x : 8, x = 500000000007, size of y : 8, y : 1
ODD IT IS :), x&1 = 1, size of x&1 = 8
After If statement ---> size of x : 8, x = 500000000007
$

但是,编译与32位而不是64位可执行文件相同的代码,然后有所不同:

$  ./sz1 
size of x : 8, x = 500000000007, size of y : 8, y : 1
ODD IT IS :), x&1 = 1, size of x&1 = 34359738368
After If statement ---> size of x : 8, x = 500000000007
$ ./sz2
size of x : 8, x = 500000000007, size of y : 8, y : 1
ODD IT IS :), x&1 = 1, size of x&1 = 8
After If statement ---> size of x : 8, x = 500000000007
$

为什么?

如果我使用%lld打印,为什么它提供的大小高达34359738368?

由于数据的堆叠方式。 假设您具有32位编译,则对printf()的'ODD'调用将推送:

  1. 指向格式的指针。
  2. x&y的值的8个字节(因为xy都是long long ,所以x&y也是long long )。
  3. sizeof(x&y)的值的4个字节(假设是32位编译,其中sizeof(size_t) == 4

不正确的格式告诉printf()

  1. 它应该使用4个字节的堆栈来打印第一个值( %dx&y而不是正确的8个字节。
  2. 它应该使用8个字节的堆栈来打印第二个值( %lld )— sizeof(x&y) —而不是正确的4个字节。

因为计算机是低位字节序的(可能是Intel计算机),所以无论值是4字节还是8字节,前4个字节都是相同的,但是由于x&y末尾的4个字节为零,所以发生了对其他字节的错误解释。值被视为值的一部分。 将格式从%lld更改为0x%llX (在原始代码中),从%zu更改为0x%zX ,并且ODD行更改:

ODD IT IS :), x&1 = 1, size of x&1 = 0x800000000

ODD IT IS :), x&1 = 1, size of x&1 = 0x8

0x8之后有8个零。

sizeof是一个编译时间运算符(操作数是可变长度数组时除外)。 给定long long xsizeof(x)等同于sizeof(long long) ,它在运行时不会改变。

long long类型至少为64位,在您的计算机中为8个字节(64位)。 所以没有什么惊喜。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM