Вопрос указателей — Что означает (двойные*)параметры?

#c #pointers

Вопрос:

У меня возникли некоторые проблемы с пониманием этого фрагмента кода.

 double F(double k,void *params){  double *p=(double *)params;   return p[0]; }   

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

Комментарии:

1. Это бросок от void* до double* . Которые не нужны и могут быть удалены (ну, некоторые любят добавлять их, чтобы продемонстрировать намерение).

2. Нигде не видно ни вектора (ни массива, как вы, вероятно, имеете в виду).

Ответ №1:

Выражение (double *)params означает «рассматривать params как указатель на double вместо указателя на void «. (double *) Это актерский состав.

В большинстве случаев вы не можете скопировать значение указателя одного типа в переменную указателя другого типа (вы не можете присвоить double * значение int * переменной) без явного приведения.

Единственным исключением в C является присвоение void * значения другому типу указателя или наоборот — в данном конкретном случае приведение не требуется. Однако C не допускает неявного преобразования из void * в другие типы указателей, поэтому в этом языке приведение необходимо.

Указатели на разные типы не обязательно должны иметь одинаковый размер и представление; единственными правилами являются

  • char * и void * имеют одинаковый размер и выравнивание;
  • Указатели на квалифицированные типы имеют тот же размер и выравнивание, что и указатели на неквалифицированный эквивалент (например, const int * и volatile int * имеют тот же размер и выравнивание, int * что и );
  • Указатели на все struct типы имеют одинаковый размер и выравнивание;
  • Указатели на все union типы имеют одинаковый размер и выравнивание;

Комментарии:

1. «Относиться params как» немного вводит в заблуждение относительно семантики, как их определяет спецификация языка: преобразование значения params в другой тип. Различие более выражено, если вы рассматриваете приведения среди арифметических типов, где, скажем, рассматривать a float как an int даже не имеет смысла.

Ответ №2:

(double *)params инструктирует компилятор думать о params (который изначально является указателем void — компилятор понятия не имеет, на какой тип он указывает) как о указателе на double только при обработке этого выражения. Это очень широко известно как «приведение в стиле Си от одного типа к другому». В этом случае мы «переводим» params тип из void* типа в double* тип, так как мы сохраняем его в указателе на double double *p .

Без приведения компилятор, скорее всего, выдаст вам type mismatch ошибку/предупреждение, так как вы попытаетесь сохранить значение указателя void в указатель на double.

Более конкретно, когда у вас есть двойной указатель или указатель на что-либо, кроме void, компилятор связывает с ним число, чтобы иметь возможность выполнять арифметику указателя на нем, когда вы пишете что-то подобное (ptr ) . Например, при ( ptr) указателе на удвоение компилятор знает, на сколько байтов нужно увеличить, потому что он знает размер удвоения. Однако пустота* не имеет связанного с ней размера, поэтому компилятор не может выполнять арифметику указателей на нее. Таким образом , при приведении указателя void к двойному указателю с помощью (double*)params вы позволяете компилятору связывать стандартный размер переменных типа double с тем, на что указывает этот указатель void, только во время обработки этого выражения, с целью сохранения его значения внутри фактического указателя на double. После того, как компилятор закончит чтение этого выражения, он вернется к мысли params , что это указатель на пустоту, а не двойной указатель, т. Е. Эффект приведения его к двойному указателю не является постоянным, он действителен только во время обработки этого выражения компилятором.

Комментарии:

1. В C это просто «слепок» или «типаж». Только в контексте C имеет смысл добавлять «C-стиль». Кроме того, ваше описание с точки зрения понимания ценности в некоторых других терминах вводит в заблуждение. Приведенное выражение преобразует значение операнда в другой тип. В этом случае он преобразует значение типа void * в тип double * .

2. «преобразовать» — это вводящее в заблуждение слово, которое следует использовать здесь, потому что можно подумать, что оно постоянно становится указателем на double. Я не использую «преобразовать» в своем объяснении по очень веской причине — указатель void становится указателем на удвоение только во время обработки этого выражения компилятором.

3. Вы бы не сказали, что написание (2 * k) заставляет компилятор думать k по-другому. Аналогично, приведение к double * не меняет того, как думает компилятор params … приведенное значение-это его собственная, отдельная вещь.

4. «Преобразовать» — это именно то слово, которое здесь следует использовать, поскольку именно так спецификация языка описывает эффект приведения. А «преобразование» — это определенная процедура в спецификации языка, поэтому выбор этого слова не случаен и не может быть изменен без потери смысла. Но обратите внимание, что я специально сказал «преобразует значение операнда», а не сам операнд.

5. Первый ответ на вопрос о stackoverflow, и я уже получаю старых динозавров на хвосте без видимой причины 🙂