Неплохой пример для разбора указателей в С\С++

Пытаясь написать мелкую учебную програмку на С нашел способ не слабо усложнить решение, но тем не менее сделать его идеальным для образовательных целей. И так, код:

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

using namespace std;

struct TestCase{
    int ida;
    int idb;
};

int main()
{
    TestCase ** tests; //create pointer to pointer
    cout << "Size of TestCase: " << sizeof(TestCase) << endl;
    tests = malloc(sizeof(TestCase*)*5);
    if(tests == NULL)
        exit(1);
    for(int i=0; i< 5; i++)
    {
        tests[i] = malloc(sizeof(struct TestCase));
        if(tests[i] == NULL)
            exit(1);
    }
    cout << "*tests = " << *tests << endl;
    for(int i = 0; i < 5; i++)
    {
        tests[i]->ida = i;
        tests[i]->idb = i+6;
        cout << std::hex << &(tests[i]->idb) << ": " << *(long*)((long)(long*)(tests[i])+(long)&(((TestCase *)NULL)->idb)) << endl;
    }
    for(int i=0; i<5; i++)
    {
        free(tests[i]);
    }
    cout << "*tests = " << *tests << endl;
    free(tests);
    return 0;
}

Вобщем, ничего страшного за исключением этой строчки:

*(long*)((long)(long*)(tests[i])+(long)&(((TestCase *)NULL)->idb))

Не буду долго глагольствовать, а просто сразу разберу её на составляющие. И так, эта строчка возвращает значение параметра idb элемента test[i]
Начнем разбираться с середины:

  1. tests[i] - это значение указателя, тоесть адрес в памяти, по которому находится структура test[i] (вместо i подставляем 1,2,3,4,5).

  2. (TestCase *)NULL - Мы создаем временный указатель на структуру типа TestCase. Адрес по которому указывает этот парень NULL, или проще говоря - 0.

  3. ((TestCase *)NULL)->idb) - по этому адресу будет находиться значение параметра idb относительно указателя на структуру NULL. По скольку адрес указателя на структуру = 0, то очень легко узнать, как далеко от него будет находиться параметр структуры idb

  4. (long)&(((TestCase *)NULL)->idb) - узнаем адрес параметра idb в NULL структуре. У меня 64 разрядный процессор, по этому я использую long, потому как int не хватает для хранения адресов такого процессора. Адрес idb относительно NULL структуры будет его смещением в структуре. Тоесть, во всех структурах типа TestCase в этой программе параметр idb будет находиться через такое количество байт относительно этой структуры, которое мы получим здесь.

  5. Узнаем адрес смещения параметра idb относительно нашего указателя на tests[i]:
    (tests[i])+(long)&(((TestCase *)NULL)->idb))

  6. Ну и финал - получим значение по смещению из предыдущего шага:

    (long)((long)(long*)(tests[i])+(long)&(((TestCase *)NULL)->idb))