Какие операции поддерживаются необработанным указателем и указателем на функцию в C / C ++?

StackOverflow https://stackoverflow.com/questions/1418068

Вопрос

Чем все операции, поддерживаемые указателем функции, отличаются от необработанного указателя? Является ли & Gt; , < , < =, > = операторы, поддерживаемые необработанными указателями, если да, то какая польза?

Это было полезно?

Решение

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

Использование 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, операции, которые определены в терминах + типа 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.

Наконец, обратите внимание, что указатель на функцию pointer больше не является указателем на функцию:

void (**pp)() = &p;
// all do work, because `sizeof (void(*)())` is defined.
pp++; pp--; pp + n; pp - n;

Другие советы

Вы можете сравнить указатели, если они указывают на одно и то же распределение. Например, если у вас есть два указателя, указывающие на элементы одного и того же массива, вы можете использовать операторы сравнения неравенства для этих указателей. С другой стороны, если у вас есть два указателя, указывающих на разные объекты, тогда сравнение будет & Quot; undefined & Quot; хотя на практике большинство компиляторов, вероятно, просто сравнивают адреса.

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 & Lt; b, то a указывает на меньший индекс в массиве, чем b). Для указателей, которые не находятся в одном и том же распределении, результат определяется реализацией (и в некоторых архитектурах может нарушать не столько совместимость, сколько требуется для карт. Например, 64-битный указатель можно сравнить для & Lt; или < !>> использовать только младшие 32 бита, если одно выделение не может превышать размер, разрешенный для 32-битного указателя). На самом деле это не имеет смысла в контексте указателей на функции, поскольку они не обеспечивают непрерывное распределение памяти.

Другие операции с необработанными указателями: == возвращает true, если указатели указывают на один и тот же объект. - производит количество байтов между двумя указателями (я думаю, хорошо только для одного и того же распределения?). + не компилируется, так как это было бы бессмысленно.

Для указателей на функции они могут быть разыменованы * и вызваны.

Для функций указателя на член есть операторы - > * и. *

Указатель представляется как обычное целочисленное значение. Вы можете делать все с указателями, что также разрешено для всех других числовых типов. + - * / < < GT &; ГТ &; ==! = ^ & amp; | ! ~% Я надеюсь, что ничего не забыл.

Указатель на функцию отличается только тем, что его можно вызвать с помощью оператора ().

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top