Проблема увеличения и уменьшения количества ошибочных массивов нескольких типов данных в C - PullRequest
1 голос
/ 19 февраля 2020

В моем курсе по компьютерным наукам нас учили методу сохранения значения в 0-м элементе неправильно выделенного массива с последующим приращением массива, чтобы такие вещи, как размер массива, можно было сохранить в этом элементе и извлечь потом. Я попытался использовать модифицированную версию этого метода для хранения различных типов данных в этих увеличенных элементах.

Вот пример того, как создается такой массив:

int *array; 
array = malloc(sizeof(int) + sizeof(double) + (n * sizeof(int)))
*(array) = n;
array++;
(double*)array++;
return array;

В этом примере sizeof(int) и sizeof(double) в выражении mallo c являются элементами, которые будет хранить такие вещи, как размер массива в элементе int, а в элементе double мы можем хранить что-то вроде среднего значения всех чисел в массиве (за исключением, разумеется, этих двух элементов)

(n * sizeof(int)) предназначен для создания остальных элементов в массиве, где n - это количество элементов, а sizeof(int) - требуемый тип данных для этих элементов, и теоретически это должно работать для массива любого типа данных. .

Теперь вот проблема, с которой я столкнулся:

Я создал другую функцию для получения размера массива, но у меня возникают проблемы с уменьшением и увеличением массива. Вот мой код:

getArraySize(void* array){
(double*)array--;//Decrement past the double element
(int*)array--;//Decrement past the int element

int size = *((int*)array);//Acquire size of the array

(int*)array++;//Increment past int element
(double*)array++;//Increment past the double element

return size;}

Эта функция не может получить размер массива, и я понял, что это потому, что компилятор сначала увеличивает массив , а затем тип приводит к его приведению. Однако, когда я пытаюсь исправить такие операторы увеличения / уменьшения следующим образом:

((int*)array)++;

, я получаю сообщение об ошибке lvalue required as increment operand. Я не знаю, как исправить это обозначение таким образом, чтобы оно правильно увеличивалось и уменьшалось. Любые предложения будут высоко оценены.

Ответы [ 2 ]

6 голосов
/ 19 февраля 2020

В моем курсе по компьютерным наукам нас учили методу сохранения значения в 0-м элементе неправильно выделенного массива с последующим приращением массива, чтобы в нем могли храниться такие вещи, как размер массива. элемент и получен позже.

Жаль это слышать, так как это полная чушь. Вместо этого используйте struct.

Что хуже, чем бессмысленная задача, так это то, что она также вызывает неопределенное поведение (см. C стандарт 6.5.6). Вы не можете делать арифметику указателей c, если они не указывают на массив того же типа, что и сам указатель.

Кроме того, это может привести к неправильному доступу. В зависимости от ЦП неправильное выравнивание может привести к излишне медленным перехватам кода или инструкций, приводящим к программе cra sh. Неправильное обращение также является неопределенным поведением.

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

Немедленно забудьте обо всей этой чепухе. Ваша программа не может быть исправлена ​​или отремонтирована, так как сама идея в корне неверна. Сделайте так вместо этого:

typedef struct
{
  int i;
  double d;
  int array[];
} something;

something* s = malloc(sizeof(something) + sizeof(int[n]));
s->i = ...;
s->d = ...;
for(int i=0; i<n; i++)
  s->array[i] = ...;

...
free(s);

В частности, ваш код вызывает неопределенное поведение в соответствии с C17 6.5.6 §7 и §8:

Для целей этих операторов указатель на объект, который не является элементом массива, ведет себя так же, как указатель на первый элемент массива длиной один с типом объекта в качестве его типа элемента.

Когда выражение с целочисленным типом добавляется или вычитается из указателя, результат имеет тип операнда указателя. / - / Если и операнд-указатель, и результат указывают на элементы одного и того же объекта массива ... / - / ... в противном случае поведение не определено.

Существует также проблема псевдонимов указателей, но (к счастью?) в данном случае это не применимо, поскольку данные, размещенные в куче, не имеют «эффективного типа», пока не будут записаны. Пока вы пишете на указанный c адрес с тем же типом указателя, это не неопределенное поведение.

Соответствующие части, касающиеся несоосности, - C17 6.3.2.3/7:

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель неправильно выровнен для ссылочного типа, поведение не определено.

0 голосов
/ 19 февраля 2020

Что вы можете сделать для достижения своей цели и (на мой взгляд) в любом случае более читабельно:

array -= sizeof(double);  // get to position where double starts
array -= sizeof(int);     // get to position where int starts

ПРИМЕЧАНИЕ Это работает только на некоторых компиляторах и в getArraySize с тех пор вы навели указатель массива на void*. Так что это также не рекомендуется вообще

Но я действительно думаю, что это НЕ путь к go, и я также рекомендую использовать struct вместо этого, как указывает @Lundin.

Если вы вызываете вашу getArraySize функцию с любым другим указателем или с указателем ожидаемого массива, но не в правильной позиции, это, скорее всего, приведет к ошибкам сегментации

...