#c #arrays #casting
#c #массивы #Кастинг
Вопрос:
Мне нужна помощь в синтаксисе C, подробнее о синтаксисе приведения C.
Вся информация, которую я нашел в Интернете, касается простых приведений, таких как (int)
или (char)
и т.д…
Я всегда застреваю при приведении void*
к массиву или многомерному массиву или указателям на такие вещи, но я никогда не знаю, как это сделать! Все, что я делал в этих случаях, пробовал такие вещи, как (char [])
или (char *[])
или (*char [])
, не имея ни малейшего представления о том, что я делаю, пока я не получу никаких ошибок о приведении типов.
У кого-нибудь есть правило, которому нужно следовать, или несколько советов или хитростей для этого?
Например, у меня есть множество указателей void, и я передаю его в функцию, как снова превратить его в массив?
main () {
int data1, data2;
char data3, data4;
void *function_data[] = {data1, data2, data3, data4};
some_function (function_data);
return;
}
some_function (void *data) {
void *function_d[4];
function_d = (void *[]) data; //It not work, how to cast data?
}
РЕДАКТИРОВАТЬ: я написал неправильно, я подумал, что это не важно, поэтому я изменил переменные data * моего кода для лучшего понимания.
Комментарии:
1. Ваш код предполагает, что
data1
,data2
,data3
иdata4
имеют типvoid**
. Так ли это?2. забудьте об этом, предполагается, что они имеют тип void *, но это не важно
3. Да, это важно, если вы хотите написать легальный рабочий код. Если
data1
имеет типvoid*
, то*data1
это недопустимо. Поскольку трудно сказать, что вы пытаетесь сделать, конкретный пример еще более важен.4. Хорошо, извините за это, я не думал, что это будет действительно проблемой.
5. Боюсь, ваша модифицированная версия еще хуже. Вы пытаетесь инициализировать
void*
элементы массива значениями типаint
иchar
; это даже не будет компилироваться. Не могли бы вы показать нам какой-нибудь реальный код, пожалуйста?
Ответ №1:
Ваша проблема в том, что void *function_d[4];
создается новый массив. Вы не можете назначить ему другой массив. function_d
должно быть типа void**
.
Комментарии:
1. Я продолжаю получать это: ошибка: приведение указывает тип массива, почему?
2. @drigoSkalWalker Вам тоже нужно изменить приведение на
void**
.3. Почему я не могу назначить ему другой массив? Я узнал, что это взаимозаменяемо, почему я не могу? Хорошо, мой код теперь работает, но я продолжаю, не понимая сложного приведения типов… например, почему мне пришлось изменить ( void *[]) на (void **)? и о многомерных массивах? Спасибо.
Ответ №2:
Основная идея состоит в том, чтобы использовать определение типа предполагаемого типа без имени переменной и помещать его в круглые скобки в качестве приведения к этому типу. Например:
int c;
c = (int) 4;
и
char * (*functionptr)(float, double);
functionptr = (char * (*)(float, double))myfunction;
Конечно, всегда предполагается, что приведение типа возможно и имеет смысл. Имейте в виду: C не предотвращает большинство бессмысленных приведений, поэтому вам придется позаботиться о себе.
В вашем случае function_data
определяется как массив указателей на void . Следовательно, все данные должны иметь тип void **
, как уже указал Кит. Вызывая some_function
с function_data
в качестве параметра, вы передаете указатель на function_data[0]
в функцию. Для того, чтобы ваша функция снова использовала его как массив из 4 указателей на void, вам нужно было бы использовать приведение, подобное вашему, (void*[]). Однако массив function_d
является массивом, в котором также зарезервировано место для четырех указателей, и вы не можете изменить указатель function_d (он имеет тип void * * const
!). Чтобы делать то, что вы, кажется, хотите, вам понадобится неконстантный указатель, например
void * * function_d = (void*[])data;
Затем вы все равно можете использовать его таким же образом, как function_data
, используя подписку как массив. function_d[2]
даст вам значение, равное *data3
.
Комментарии:
1. Отличный ответ, большое спасибо, но я не понял эту часть: однако массив function_d — это массив, в котором также зарезервировано место для четырех указателей, и вы не можете изменить указатель function_d (он имеет тип void * * const!). Чтобы делать то, что вы, кажется, хотите, вам понадобится неконстантный указатель, например . Не могли бы вы объяснить мне немного проще? thnaks много!!!
2. Если вы объявляете переменную локального массива, в стеке для нее выделяется место, и вы можете использовать массив, как ожидалось. Но: у вас уже есть массив, заполненный данными, и вы передаете указатель на него в качестве параметра функции. Вы не хотите создавать новый, а используете уже существующий. Это более понятно?
Ответ №3:
Вы не можете назначить объекту массива.
Как объявляются data1
, data2
, data3
и data4
?
void*
является (или может использоваться как) универсальным типом указателя. Это может дать вам огромную гибкость, но также может препятствовать проверке типов.
function_data
это массив из 4 указателей на void. При вызове some_function (function_data)
он неявно преобразуется в void**
указатель на первый элемент.
some_function
ожидает void*
, а не void**
— но любой тип указателя (кроме указателя на функцию) может быть неявно преобразован в void*
, поэтому компилятор не жалуется на вызов.
Вот версия вашей программы, которая, по крайней мере, корректна по типу (я думаю).
#include <stddef.h>
void some_function(void **data, size_t count);
int main(void) {
void **data1, **data2, **data3, **data4;
void *function_data[] = { *data1, *data2, *data3, *data4 };
some_function (function_data,
sizeof function_data / sizeof function_data[0]);
return 0;
}
void some_function(void **data, size_t count) {
size_t i;
for (i = 0; i < count; i ) {
/*
* do something with data[i], which is of type void*
*/
}
}
Обратите внимание, что data1
и друзья не инициализированы, поэтому поведение этой программы не определено. Я подозреваю, что вы хотели написать что-то вроде:
void *function_data[] = { amp;data1, amp;data2, amp;data3, amp;data4 };
но трудно сказать, что именно вы пытаетесь сделать.
Если вам действительно нужно, чтобы функция имела копию массива, вы можете использовать memcpy()
(вы не можете назначать значения массива). Однако для большинства целей имеет смысл использовать указатели для доступа к исходному массиву. Вам нужно будет создать копию, если вы хотите изменить копию, не затрагивая оригинал.
И я исправил объявления для main
и some_function
и добавил объявление для some_function
в начало, чтобы оно было видно при его вызове.
Вы должны всегда явно объявлять возвращаемый тип для всех ваших функций. В версии C 1990 года вы можете опустить его, и по умолчанию оно будет равно int
(но все же лучше объявить его как int
явно). В версии C 1999 года тип является обязательным, и вы не можете вызвать функцию без видимого объявления.
Опять же, из кода, который вы нам показали, трудно сказать, что именно вы пытаетесь сделать, что затрудняет угадывание, как это сделать.
Комментарии:
1. Хорошо, но сначала остаются некоторые вопросы: я не могу изменить объявление функции, потому что оно из библиотеки, а прототип должен быть void , а не void ** . второе: я работаю только с массивом размером в один десятичный размер и с многомерным массивом? Я должен преобразовать его в указатели типа void *** ( столько измерений, сколько у меня есть)? Я думаю, что я никогда не буду использовать что-то вроде (char *[]) или (void *[]), потому что gcc выдает мне ошибку: ошибка: приведение указывает тип массива. Спасибо за вашу помощь.
2. Каков фактический прототип функции? Что это должно делать? Действительно ли это объявлено без явного возвращаемого типа? Если вы работаете с многомерным массивом, как именно он объявлен? Существует несколько способов реализации вещей, которые действуют как многомерные массивы. Если это настоящий многомерный массив (массив массивов), вы не можете использовать с ним указатель на указатель. Покажите нам реальный код , а не краткое изложение фрагментов, которые вы считаете релевантными.
3. Я не смогу помочь, если вы не будете отвечать на запросы о разъяснениях. Я не могу читать твои мысли. Больше рекомендуемого чтения: catb.org /~esr/faqs/smart-questions.html
4. Мой вопрос касается сложных приведений C, если вы прочитали заголовок вопроса, вероятно, должны знать, что речь идет не о том, как правильно объявлять массив, или прав ли мой код? это псевдокод, не для реального использования, только для примера, только для справки о сложных приведениях C, я думаю, вы достаточно умны, чтобы поговорить со мной. снова сбой.
Ответ №4:
Несколько проблем:
Это не сработает:
int data1, data2;
char data3, data4;
void *function_data[] = {data1, data2, data3, data4};
потому что int
и char
не совместимы с void *
. Вы могли бы исправить это следующим образом:
void *function_data[] = {amp;data1, amp;data2, amp;data3, amp;data4};
потому что int *
и char *
совместимы с void *
.
Когда вы передаете function_data
в some_function
, выражение массива преобразуется в значение указателя, так что some_function
получается void **
. Вы можете использовать оператор подстрочного индекса для указателя, как если бы это был массив:
some_function(function_data);
...
void some_function(void **data)
{
int *x = data[0]; // remember, we stored the *addresses*
int *y = data[1]; // of data1, data2, data3, and data4;
char *a = data[2]; // since the type of data is void **,
char *b = data[3]; // the type of data[i] is void *.
...
}
Итак, вам не нужно приводить data
к типу массива, чтобы использовать его как массив.