12

谁能解释如何添加a和的逻辑b

#include <stdio.h>

int main()
{
     int a=30000, b=20, sum;
     char *p;
     p = (char *) a;
     sum = (int)&p[b];  //adding a and b!
     printf("%d",sum);
     return 0;
}
4

9 回答 9

20

+隐藏在这里:

&p[b]

这个表达式等价于

(p + b)

所以我们实际上有:

(int) &p[b] == (int) ((char *) a)[b]) == (int) ((char *) a + b) == a + b

请注意,这在技术上会调用未定义的行为,因为(char *) a它必须指向一个对象,并且对象外部的指针算术或超过该对象的指针算法会调用未定义的行为。

于 2013-07-03T14:39:39.273 回答
10

C 标准说E1[E2]相当于*((E1) + (E2)). 所以:

 &p[b] = &*((p) + (b)) = ((p) + (b)) = ((a) + (b)) = a + b
于 2013-07-03T14:40:36.253 回答
5

p[b]是数组的第 b 个p元素。就像写作一样*(p + b)

现在,添加&时就像是这样写:p + b * sizeof(char)which is p + b. 现在,
您将拥有(int)((char *) a + b)...a + b

但是..当你+的键盘还有,使用它。


正如@gerijeshchauhan 在评论中澄清的那样*&它们是逆运算,它们相互抵消。&*(p + b)也是如此p + b

于 2013-07-03T14:40:33.393 回答
3
  1. p 是指向 char 的指针

  2. a 被转换为指向 char 的指针,从而使 p 指向地址为 a 的内存

  3. 然后下标运算符用于在 p 指向的地址之外的偏移量 b 处到达对象。b 是 20 并且 p+20=30020 。然后在结果对象上使用 address-of 运算符将地址转换回 int,你就得到了 a+b 的效果

以下评论可能更容易理解:

#include <stdio.h>

int main()
{
     int a=30000, b=20, sum;
     char *p; //1. p is a pointer to char
     p = (char *) a; //2. a is converted to a pointer to char and p points to memory with address a (30000)
     sum = (int)&p[b];  //3. p[b] is the b-th (20-th) element from address of p. So the address of the result of that is equivalent to a+b
     printf("%d",sum);
     return 0;
}

参考:这里

于 2013-07-03T14:48:56.083 回答
2
char *p;  

p是一个指针(指向大小为 1 字节的元素)


p=(char *)a;  

现在p指向带有地址的内存a


sum= (int)&p[b]; 

p指针可以用作数组p[](该数组的起始地址(在内存中)是a

p[b]表示获取第 b 个元素 - 这个元素的地址是a+b

[(起始地址)a+ b(第 b 个元素 * 元素大小(1 字节))]

&p[b]意味着获取元素的地址,p[b]但它的地址是a+b


如果您使用指向 int 的指针(主要是 4 个字节)

int* p
p = (int*)a;

你的总和将是 a+(4*b)

于 2013-07-03T15:00:39.943 回答
1
 int a=30000, b=20, sum;
 char *p; //1. p is a pointer to char
 p = (char *) a;

a是类型int,并且具有值30000。上述赋值将值30000从转换intchar*并将结果存储在p.

将整数转换为指针的语义(部分)由 C 标准定义。引用N1570草案,第 6.3.2.3 节第 5 段:

整数可以转换为任何指针类型。除非前面指定,结果是实现定义的,可能没有正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示。

带有(非规范性)脚注:

将指针转换为整数或将整数转换为指针的映射函数旨在与执行环境的寻址结构保持一致。

该标准不保证类型的相对大小intchar*; 任何一个都可能比另一个大,并且转换可能会丢失信息。这种特定转换的结果不太可能是有效的指针值。如果它是一个陷阱表示,那么赋值的行为是未定义的。

在您可能使用的典型系统char*上,至少与 一样大int,并且整数到指针的转换可能只是将构成整数表示的位重新解释为指针值的表示。

 sum = (int)&p[b];

p[b]根据定义等价于*(p+b),其中+表示指针算术。由于指针指向char,并且char根据定义 a 是 1 个字节,因此加法将指向的地址b在内存中推进字节(在本例中为 20)。

Butp可能不是一个有效的指针,因此任何对其执行算术运算甚至访问其值的尝试都有未定义的行为。

实际上,大多数 C 编译器生成的代码不执行额外检查。重点是正确代码的快速执行,而不是检测不正确的代码。因此,如果先前的赋值将p其设置为与数字相对应的地址30000,那么向该地址添加b或 20 可能会产生与数字相对应的地址30020

该地址是(p+b); 现在[]运算符隐式地将运算符应用于*该地址,为您提供该地址指向的对象 - 从概念上讲,这是一个char存储在与 integer 对应的地址的对象30020

我们立即将&运算符应用于该对象。有一条特例规则说,应用于运算符&的结果[]等同于只进行指针加法;参见上述参考标准草案中的 6.5.3.2p2。

所以这:

&p[b]

相当于:

p + b

正如我上面所说,它产生一个char*对应于整数值的地址(类型)30020——当然,假设整数到指针的转换以某种方式运行,并且构造和访问无效指针的未定义行为值不要做任何令人惊讶的事情。

最后,我们使用转换运算符将此地址转换为 type int。指针值到整数的转换也是实现定义的,并且可能未定义。引用 6.3.2.3p6:

任何指针类型都可以转换为整数类型。除非前面指定,结果是实现定义的。如果结果不能以整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。

char*a大于a 的情况并不少见int(例如,我在具有 32-bitint和 64-bit的系统上键入它char*)。但在这种情况下,我们相对不会溢出,因为该char*值是转换范围内int值的结果。不能保证将给定的值从intto转换char*回 toint会产生原始结果,但它通常以这种方式工作,至少对于范围内的值。

因此,如果恰好运行代码的实现满足了许多特定于实现的假设,那么这段代码很可能会产生与30000 + 20.

顺便说一句,我曾在一个可能会失败的系统上工作。Cray T90 是一个字寻址机器,硬件地址指向 64 位字;没有对字节寻址的硬件支持。但是char是 8 位,因此必须在硬件中构造和操作指针char*void*一个char*指针由一个 64 位字指针组成,字节偏移存储在其他未使用的高 3 位中。指针和整数之间的转换没有特别对待这些高位;他们只是被复制了。因此ptr + 1,并且(char*)(int)ptr + 1)可能会产生非常不同的结果。

但是,嘿,您已经设法在不使用+运算符的情况下添加两个小整数,所以就是这样。

于 2013-07-03T18:36:58.247 回答
1

指针算法的替代方法是使用 bitops:

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

unsigned addtwo(unsigned one, unsigned two);

unsigned addtwo(unsigned one, unsigned two)
{
unsigned carry;

for( ;two; two = carry << 1)    { 
        carry = one & two; 
        one ^= two;
        } 
return one;
}

int main(int argc, char **argv)
{
unsigned one, two, result;

if ( sscanf(argv[1], "%u", &one ) < 1) return 0;
if ( sscanf(argv[2], "%u", &two ) < 1) return 0;

result = addtwo(one, two);

fprintf(stdout, "One:=%u Two=%u Result=%u\n", one, two, result );

return 0;
}
于 2013-07-03T19:21:28.833 回答
1

在一个完全不同的方面,也许正在寻找的是理解二进制加法是如何在硬件中完成的,使用 XOR、AND 和位移。换句话说,类似这样的算法:

int add(int a, int b)
{ int partial_sum = a ^ b;
  int carries = a & b;

  if (carries)
    return add(partial_sum, carries << 1);
  else
    return partial_sum;
}

或迭代等价物(尽管 gcc 至少可以识别叶函数并将递归优化为迭代版本;可能其他编译器也会这样做)....

可能需要对负数进行更多研究,但这至少适用于正数。

于 2013-07-03T19:23:49.180 回答
1
/*
by sch.

001010101 = 85
001000111 = 71
---------
010011100 = 156
*/

#include <stdio.h>

#define SET_N_BIT(i,sum) ((1 << (i)) | (sum))

int sum(int a, int b)
{
    int t = 0;
    int i = 0;
    int ia = 0, ib = 0;
    int sum = 0;
    int mask = 0;

    for(i = 0; i < sizeof(int) * 8; i++)
    {   
        mask = 1 << i;
        ia = a & mask;
        ib = b & mask;

        if(ia & ib)
            if(t)
            {
                sum = SET_N_BIT(i,sum);
                t = 1;
                /*i(1) t=1*/
            }
            else
            {
                t = 1;
                /*i(0) t=1*/
            }
        else if (ia | ib)
            if(t)
            {
                t = 1;
                /*i(0) t=1*/
            }
            else
            {
                sum = SET_N_BIT(i,sum);
                t = 0;
                /*i(1) t=0*/
            }
        else
            if(t)
            {
                sum = SET_N_BIT(i,sum);
                t = 0;
                /*i(1) t=0*/
            }
            else
            {
                t = 0;
                /*i(0) t=0*/
            }

    }

    return sum;
}

int main()
{
    int a = 85;
    int b = 71;
    int i = 0;

    while(1)
    {
        scanf("%d %d", &a, &b);

        printf("%d: %d + %d = %d\n", ++i, a, b, sum(a, b));
    }

    return 0;
}
于 2013-11-16T01:47:51.257 回答