Сумма (добавление 2 чисел) без оператора

Может ли кто-нибудь объяснить логику добавления 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;
}

Ответ 1

Здесь + скрывается:

&p[b]

это выражение эквивалентно

(p + b)

Итак, у нас есть:

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

Обратите внимание, что это технически вызывает поведение undefined, поскольку (char *) a должно указывать на арифметику объекта и указателя вне объекта или один за объектом, вызывающим поведение undefined.

Ответ 2

Стандарт C говорит, что E1[E2] эквивалентен *((E1) + (E2)). Поэтому:

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

Ответ 3

p[b] - это b-й элемент массива p. Это как писать *(p + b).

Теперь, добавляя &, это будет похоже на запись: p + b * sizeof(char), которая равна p + b.
Теперь у вас будет (int)((char *) a + b), который... a + b.

Но если у вас еще есть + на клавиатуре, используйте его.


Как пояснил @gerijeshchauhan в комментариях, * и & являются обратными операциями, они отменяют друг друга. Итак, &*(p + b) p + b.

Ответ 4

  • p делается указателем на char

  • a преобразуется в указатель на char, тем самым делая p точкой в ​​памяти с адресом a

  • Затем индексный оператор используется для перехода к объекту со смещением b за адресом, на который указывает p. b равно 20 и p + 20 = 30020. Затем адресный оператор используется для результирующего объекта для преобразования адреса обратно в int, и вы получили эффект + 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;
}

Ссылка: здесь

Ответ 5

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)

Ответ 6

 int a=30000, b=20, sum;
 char *p; //1. p is a pointer to char
 p = (char *) a;

a имеет тип int и имеет значение 30000. Вышеприведенное назначение преобразует значение 30000 из int в char* и сохраняет результат в p.

Семантика преобразования целых чисел в указатели (частично) определяется стандартом C. Цитируя проект N1570, раздел 6.3.2.3, пункт 5:

Целое число может быть преобразовано в любой тип указателя. Кроме того, как ранее указано, результат определяется реализацией, может и не быть правильно выровненный, может не указывать на объект ссылочной тип и может быть ловушечным представлением.

с (ненормативной) сноской:

Функции отображения для преобразования указателя на целое число или целое число с указателем предназначены для согласования с адресацией структура среды выполнения.

Стандарт не дает никаких гарантий относительно относительных размеров типов int и char*; либо может быть больше, чем другой, и конверсия может потерять информацию. Результат этих конкретных преобразований вряд ли будет действительным значением указателя. Если это представление ловушки, то поведение присваивания undefined.

В типичной системе, которую вы, вероятно, будете использовать, char* по крайней мере такой же большой, как int, а конверсии целого-на-указателя, вероятно, просто переинтерпретируют биты, составляющие целочисленное представление, как представление значение указателя.

 sum = (int)&p[b];

p[b] по определению эквивалентен *(p+b), где + обозначает арифметику указателя. Так как указатель указывает на char, а a char по определению 1 байт, добавление увеличивает адрес в виде b байтов в памяти (в данном случае 20).

Но p, вероятно, не является допустимым указателем, поэтому любая попытка выполнить арифметику на нем или даже получить доступ к его значению имеет поведение undefined.

На практике большинство компиляторов C генерируют код, который не выполняет дополнительные проверки. Акцент делается на быстрое выполнение правильного кода, а не на обнаружение неправильного кода. Поэтому, если предыдущее назначение p установило его на адрес, соответствующий номеру 30000, то добавление b или 20 к этому адресу, вероятно, приведет к адресу, соответствующему номеру 30020.

Этот адрес является результатом (p+b); теперь оператор [] неявно применяет оператор * к этому адресу, предоставляя вам объект, на который указывает этот адрес - концептуально, это объект char, хранящийся по адресу, соответствующему целому числу 30020.

Мы немедленно применяем оператор & к этому объекту. Там правило особого случая, в котором говорится, что применение & к результату оператора [] эквивалентно простому добавлению указателя; см. 6.5.3.2p2 в вышеупомянутой стандартной черновике.

Итак, это:

&p[b]

эквивалентно:

p + b

который, как я сказал выше, дает адрес (типа char*), соответствующий целочисленному значению 30020 - при условии, конечно, что преобразования целых-на-указателей ведут себя определенным образом и что undefined поведение построения и доступа к недопустимому значению указателя не вызывает ничего удивительного.

Наконец, мы используем оператор трансляции для преобразования этого адреса в тип int. Преобразование значения указателя в целое число также определяется реализацией и, возможно, undefined. Цитирование 6.3.2.3p6:

Любой тип указателя может быть преобразован в целочисленный тип. Кроме того, ранее указанный, результат определяется реализацией. Если результат не может быть представлен в целочисленном типе, поведение undefined. Результат не должен находиться в диапазоне значений любых целочисленный тип.

Это не редкость для char* больше, чем int (например, я набираю это в системе с 32-разрядными int и 64-разрядными char*). Но в этом случае мы относительно безопасны от переполнения, потому что значение char* является результатом преобразования значения int в диапазоне. нет гарантии, что преобразование заданного значения из int в char* и обратно в int приведет к исходному результату, но обычно это работает, по крайней мере, для значений, находящихся в диапазоне.

Таким образом, если ряд реализационных предположений будет выполняться реализацией, на которой выполняется код, то этот код, вероятно, даст тот же результат, что и 30000 + 20.

Кстати, я работал над системой, где это было бы неудачно. Cray T90 был машиной с адресами, с аппаратными адресами, указывающими на 64-битные слова; не было аппаратной поддержки для байтовой адресации. Но char был 8 бит, поэтому char* и void* указатели должны были быть сконструированы и обработаны на аппаратном уровне. Указатель char* состоял из 64-битного указателя слова с смещением байта, хранящегося в неиспользуемых 3 разрядах высокого порядка. Конверсии между указателями и целыми числами не рассматривались специально для этих старших бит; они просто были скопированы. Таким образом, ptr + 1 и (char*)(int)ptr + 1) могут давать очень разные результаты.

Но эй, вам удалось добавить два небольших целых числа без использования оператора +, так что это.

Ответ 7

Альтернативой арифметике указателя является использование битов:

#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;
}

Ответ 8

В совершенно другой заметке, возможно, именно то, что искали, это понимание того, как выполняется бинарное добавление в аппаратном обеспечении, с 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, по крайней мере, распознает функцию листа и в любом случае оптимизирует рекурсию в итеративную версию, возможно, другие компиляторы тоже)....

Вероятно, нужно немного больше изучить отрицательные случаи, но это, по крайней мере, работает для положительных чисел.

Ответ 9

/*
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;
}