Какие операции поддерживаются необработанным указателем и указателем на функцию в C/C++?
Чем все операции, поддерживаемые указателем функции, отличаются от необработанного указателя? Поддерживаются ли операторы>, <, <=,>= необработанными указателями, если да, то какая польза?
5 ответов
Как для указателей на функции, так и для объектов они компилируются, но их результат гарантированно будет согласованным только для адресов подобъектов одного и того же законченного объекта (вы можете сравнить адреса двух членов класса или массива), а если вы сравните функцию или возразить против себя.
С помощью std::less<>
, std::greater<>
и так далее будет работать с любым типом указателя и будет давать согласованные результаты, даже если результат соответствующего встроенного оператора не указан:
void f() { }
void g() { }
int main() {
int a, b;
///// not guaranteed to pass
assert((&a < &b) == (&a < &b));
///// guaranteed to pass
std::less<int*> lss1;
assert(lss1(&a, &b) == lss1(&a, &b));
// note: we don't know whether lss1(&a, &b) is true or false.
// But it's either always true or always false.
////// guaranteed to pass
int c[2];
assert((&c[0] < &c[1]) == (&c[0] < &c[1]));
// in addition, the smaller index compares less:
assert(&c[0] < &c[1]);
///// not guaranteed to pass
assert((&f < &g) == (&f < &g));
///// guaranteed to pass
assert((&g < &g) == (&g < &g));
// in addition, a function compares not less against itself.
assert(!(&g < &g));
///// guaranteed to pass
std::less<void(*)()> lss2;
assert(lss2(&f, &g) == lss2(&f, &g));
// note: same, we don't know whether lss2(&f, &g) is true or false.
///// guaranteed to pass
struct test {
int a;
// no "access:" thing may be between these!
int b;
int c[1];
// likewise here
int d[1];
test() {
assert((&a < &b) == (&a < &b));
assert((&c[0] < &d[0]) == (&c[0] < &d[0]));
// in addition, the previous member compares less:
assert((&a < &b) && (&c[0] < &d[0]));
}
} t;
}
Все это должно скомпилироваться (хотя компилятор может предупреждать о любом фрагменте кода, который он хочет).
Поскольку типы функций не имеют sizeof
стоимость, операции, которые определены в терминах sizeof
типа pointee не будут работать, к ним относятся:
void(*p)() = ...;
// all won't work, since `sizeof (void())` won't work.
// GCC has an extension that treats it as 1 byte, though.
p++; p--; p + n; p - n;
Одинарный +
работает с любым типом указателя и просто возвращает его значение, в нем нет ничего особенного для указателей на функции.
+ p; // works. the result is the address stored in p.
В заключение отметим, что указатель на указатель на функцию больше не является указателем на функцию:
void (**pp)() = &p;
// all do work, because `sizeof (void(*)())` is defined.
pp++; pp--; pp + n; pp - n;
Вы можете сравнить указатели, если они указывают на одно и то же распределение. Например, если у вас есть два указателя, указывающие на элементы одного и того же массива, вы можете использовать операторы сравнения неравенства для этих указателей. С другой стороны, если у вас есть два указателя, указывающих на разные объекты, тогда сравнение "неопределено", хотя на практике большинство компиляторов, вероятно, просто сравнивают адреса.
char *text[] = "hello";
const char *e_ptr = strchr(text, 'e');
const char *o_ptr = strchr(text, 'o');
if (e_ptr < o_ptr) { ... } // this is legal
char *other_text[] = "goodbye";
const char *b_ptr = strchr(other_text, 'b');
if (b_ptr > o_ptr) { ... } // not strictly legal
# 1: Функциональные указатели могут быть вызваны.
# 2: реляционные операторы поддерживаются для указателей, потому что вы можете использовать их в арифметике указателей и сравнивать адреса друг с другом. Практический пример: обход массива
int data[5] = { 1, 2, 3, 4, 5 };
// Increment pointer until it reaches the end-address.
for (int* i = data; i < data + 5; ++i) {
std::cout << *i << endl;
}
Операторы <,>, <=, >= поддерживаются для указателей, но гарантированно дают надежные результаты только в том случае, если сравниваемые два указателя являются частью одного и того же выделения памяти (например, сравнение двух указателей с индексами при выделении массива). Для них это указывает относительную позицию в распределении (то есть, если a , используя только нижний 32 бита, если одно выделение не может превышать размер, разрешенный для 32-битного указателя). На самом деле это не имеет смысла в контексте указателей на функции, поскольку они не обращаются к непрерывному выделению памяти.
Другие операции с необработанными указателями: == возвращает true, если указатели указывают на один и тот же объект. - производит количество байтов между двумя указателями (я думаю, хорошо только для одного и того же распределения?). + не компилируется, так как это было бы бессмысленно.
Для указателей на функции они могут быть разыменованы * и вызваны.
Для функций указателя на член есть операторы ->* и.*
Указатель представляется как обычное целочисленное значение. Вы можете делать все с указателями, что также разрешено для всех других числовых типов. + - * / << >> ==!= ^ & |! ~ % Я надеюсь, что ничего не забыл.
Указатель на функцию отличается только тем, что его можно вызвать с помощью оператора ().