2016-12-26 11 views
0

С помощью функции есть какой-либо законный способ интерпретировать параметр char * заданной длины как указатель другого целочисленного типа, а затем обратиться к указанному преобразованному указателю? Там, кажется, много незаконных (UB) способов сделать это ...Любой законный способ переосмыслить char * как массив другого примитивного типа для операций только для чтения?

К примеру, учитывая следующий прототип функции:

int32_t sum_32(char *a, int len); 

Я хотел бы знать, если есть способ писать что-то функционально эквивалентное следующий код, по закону:

int32_t sum_32(char *a, int len) { 
    assert(len % 4 == 0); 
    int32_t total = 0; 
    for (int i = 0; i < len/4; i++) { 
     total += ((int32_t *)a)[i]; 
    } 
    return total; 
} 

конечно, один из способов сделать это просто, чтобы сломать доступы в доступ символов размера со смещением рекомбинировать в большее значение (с некоторым предположением о е ndianness, вот если предположить LE):

int32_t sum_32(char *a, int len) { 
    assert(len % 4 == 0); 
    int32_t total = 0; 
    for (int i = 0; i < len; i += 4) { 
     int32_t val = (int32_t) 
         (a[i+0] << 0) + 
         (a[i+1] << 8) + 
         (a[i+2] << 16) + 
         (a[i+3] << 24) ; 
     total += val; 
    } 
    return total; 
} 

... но здесь я ищу решение, которые имеют доступ к основному массиву один int32_t в то время.

Если ответ «это не возможно», не изменится ответ, если я знаю, что источник char *a является функцией распределения - или, в более широком смысле, есть ли какие-либо дополнительные ограничения, я могу поставить на a таким образом, что доступ к нему как более крупный тип является законным?

+0

Первой проблемой является, вероятно, выравнивание. Если это происходит из функции распределения, вы, по крайней мере, знаете, что это правильно выравнивание. – melpomene

+0

Я предлагаю вам взглянуть на реализацию GNU stdlib. Строковые функции содержат много такого рода обработки слова в момент времени. (после выравнивания, конечно, заботятся) – wildplasser

+1

Как вы уже писали, вам нужно заботиться о выравнивании и энсианности. Например, [этот ответ] (http://stackoverflow.com/a/4840428/69809) имеет несколько функций для округления адреса указателя по модулю 4 или 8. Если вам нужно обрабатывать разные endianess, то вы также можете работать с отдельными байтами. Поскольку это помечено как «производительность», вы уверены, что не слишком быстро оптимизируете? Последняя функция не имеет никакого сдвига, кстати. – Groo

ответ

2

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

+0

Как насчет того, что последняя запись в область памяти была из чего-то вроде 'fread' - из файла или сокета или что-то еще. Тогда я ничего не могу сказать о «последней записи», верно? – BeeOnRope

+0

Кроме того, нет ли escape-люка в правилах aliasing для 'char *'? – BeeOnRope

+3

Да, вы можете псевдонимы любого типа с помощью символа 'char *', но не наоборот. – alain

2

Чтобы избежать жестких проблем сглаживания, total += ((int32_t *)a)[i]; может быть заменен:

int32_t temp; 
memcpy(&temp, a+i*4, sizeof temp); 
total += temp; 

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

(Примечание: как написано в вопросе, версия бит-сдвига неверна из-за возможности подписания char - вам нужно либо сменить функцию на unsigned char *, либо использовать эквивалентные роли).

Я использовал compiler explorer и нашел, что для этого кода gcc проверит, если a выровнено, и если да, то используйте инструкции XMM, а если нет, используйте старые инструкции.

+0

И это * снова * является мощью реализации libc. Встраивание? Нет проблем! – wildplasser

+0

@wildplasser, что реализация libc? –

+0

Почему '* 4'? Предложите 'memcpy (& temp, a + sizeof temp * i, sizeof temp);' или если вы предпочитаете магические числа, тогда согласитесь с 'memcpy (& temp, a + i * 4, 4);'. – chux

 Смежные вопросы

  • Нет связанных вопросов^_^