Вам может быть легче, когда вы понимаете мотив , стоящий за кодом примера.
Код манипулирует 4-байтовыми значениями, поэтому p
преобразуется в long *
. Конструкция * (long *) p = -4;
позволяет вам установить 4 байта на 0xFFFFFFFC
с одним присваиванием. Если вы оставили p
как char *
, вам потребовалось бы четыре отдельных назначения, и вам также пришлось бы беспокоиться о endianness вашей платформы.
Так почему бы просто не объявить p
как long *
в первую очередь? Поскольку код использует арифметику указателя для вычисления целевых адресов: p += sizeof(shellcode) + 64 - 4;
Арифметика указателя легко с char *
, потому что добавление 1 к указателю продвинет его к следующему байту, как и следовало ожидать. Не так с указателями на другие типы данных! Если p
было объявлено как long *p;
, то p += 4
добавляет 4 * sizeof(long)
к p
.
Почему? Потому что это облегчает просмотр списка long
переменных:
long sum_of_longs(long vals[], int num) { // 'vals[]' contains 'num' long ints.
long *p; // This pointer traverses the array.
long sum; // Running total.
// Initialize 'p' to the first number in 'vals[]' and
// increment through the array until 'num' reaches 0.
//
// Note that 'p' increases by 4 bytes each time in order
// to advance to the next long.
for (sum=0, p=vals; num > 0; p++, num--)
sum += *p;
return sum;
}
Итак, в вашем примере определение p
как char *
облегчает арифметику указателей в виде байтов, а приведение его к long *
упрощает назначения.