Почему “длинен *” и “интервал *” не совместимый в 32-битном коде?

Наивный взлом для этого должен был бы создать список или массив как

1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10

И затем выбрать случайным образом из этого.

8
задан Greg Rogers 22 September 2009 в 18:58
поделиться

9 ответов

Тот факт, что long и int оба являются 32-битными на вашем конкретном компиляторе и оборудовании, не означает, что они всегда будут 32-разрядная версия для любого оборудования и каждого компилятора.

C (и C ++) были разработаны для обеспечения переносимости исходного кода между разными компиляторами и разным оборудованием.

28
ответ дан 5 December 2019 в 04:28
поделиться

Если вам нужно действительно краткое введение: Unicode за 5 минут

Или, если вам нужны однострочные символы:

  • Unicode: отображение символов в целые числа («кодовые точки») в диапазоне от 0 до 1,114 111; охватывает почти все используемые письменные языки
  • UTF7: кодирование кодовых точек в поток байтов с очисткой старшего бита; обычно не используют
  • UTF8: кодирование кодовых точек в поток байтов, где для представления каждого символа может потребоваться один, два, три или четыре байта; должен быть вашим основным выбором кодировки
  • UTF16: кодирование кодовых точек в поток слов (16-битные единицы), где каждый символ может принимать одно или два слова (два или четыре байта) для представления
  • UTF32: кодирование кодовых точек в поток 32-битных единиц, где каждый символ занимает ровно одну единицу (четыре байта); иногда используется для внутреннего представления.
  • Кодовые страницы: система в DOS и Windows, в которой символы назначаются целым числам и соответствующая кодировка; каждый охватывает только подмножество языков. Обратите внимание, что эти назначения обычно отличаются от назначений Unicode
  • ASCII: очень распространенное назначение символов целым числам и прямое кодирование в байты (все старшие биты очищены); присвоение - это подмножество Unicode, а кодирование - подмножество UTF-8
  • ANSI: орган стандартизации
  • Windows 1252: Обычно используемая кодовая страница; он похож на ISO-8859-1 или Latin-1, но не то же самое, и их часто путают

Почему вас это волнует? Потому что, не зная используемого набора символов и кодировки, вы действительно не знаете, какие символы представляет данный поток байтов.

26
ответ дан 5 December 2019 в 04:28
поделиться

long и int - два разных типа, даже если они одного размера. Если бы некоторые компиляторы обрабатывали их одинаково, в мире шаблонов С ++ были бы огромные последствия.

7
ответ дан 5 December 2019 в 04:28
поделиться

Здесь я вижу два разных вопроса.

Во-первых, на современных архитектурах довольно безопасно предположить, что указатели имеют одинаковый размер (то есть нет ближних / дальних указателей; но указатели на функции-члены не являются обычными указателями и могут иметь другой размер); а в 32-битной системе этот размер обычно составляет 32 бита. C даже заходит так далеко, что автоматически приводит void * к чему-либо еще, потому что, в конце концов, указатель - это просто адрес памяти. Однако в определениях языка разные указатели различаются как разные типы. Я считаю, что причина этого в том, что разные типы могут иметь разное выравнивание (правило void * заключается в том, что на самом деле ничто не может быть типа void , поэтому, если у вас есть указатель на ] void вы, вероятно, знаете правильный тип и, неявно, правильное выравнивание ( см. примечание ))

Во-вторых, как указывали другие, long s и int являются принципиально разными типами с преобразованиями по умолчанию встроен. Стандарты требуют, чтобы long s были не меньше, чем int s, но, возможно, больше. В вашей архитектуре выравнивание, вероятно, такое же, но на других архитектурах, которые также могут отличаться.

В вашем случае можно обмануть, но это не переносимо. Если вы не #include правильных объявлений функций, а вместо этого просто пересылаете их сами, все должно волшебным образом сработать, потому что в вашем случае long s и int s являются совместим (при условии отсутствия проблем с подписью; также в C ++ вам потребуется extern " s назад к исходному типу, и в этом случае выравнивание и размер будут такими же.

Но есть складки. Во-первых, упаковка объекта должна быть одинаковой в обоих местах (не проблема с примитивными типами). С другой стороны, возможно указать произвольную память с помощью void * s, но программисты, которые делают это, предположительно понимают, что они делают.

18
ответ дан 5 December 2019 в 04:28
поделиться

Since I don't particularly like any of the answers given so far, I went to the C++ standard:

4.7 Integral conversions [conv.integral]

1 An rvalue of an integer type can be converted to an rvalue of another integer type. An rvalue of an enumeration type can be converted to an rvalue of an integer type.

This says it is allowed to implicitly convert one integer to another, so the two types (as they are the same size), are interchangeable as rvalues.

4.10 Pointer conversions [conv.ptr]

1 An integral constant expression (expr.const) rvalue of integer type that evaluates to zero (called a null pointer constant) can be converted to a pointer type. The result is a value (called the null pointer value of that type) distinguishable from every pointer to an object or function. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (conv.qual).

2 An rvalue of type "pointer to cv T," where T is an object type, can be converted to an rvalue of type "pointer to cv void." The result of converting a "pointer to cv T" to a "pointer to cv void" points to the start of the storage location where объект типа T находится, как если объект является наиболее производным объект ( intro.object ) типа T (то есть не подобъект базового класса).

3 rvalue типа "указатель на cv D," где D - тип класса, может быть преобразовано в rvalue типа "указатель на cv B", где B - основание класс ( класс. производный ) D. Если B - недоступный ( class.access ) или неоднозначный ( class.member.lookup ) базовый класс D, программа, которая требует этого преобразование плохо сформировано. Результат преобразования - это указатель на подобъект базового класса объект производного класса. Нуль значение указателя преобразуется в нуль pointer value of the destination type.

It is only allowed to implicitly convert:

  • 0 to any pointer type (making it a null pointer)
  • any pointer type to void* (properly cv-qualified)
  • derived pointer to a base pointer (properly cv-qualified)

So even though the underlying machine type is the same, it is not allowed to implicitly convert between the two types.

4
ответ дан 5 December 2019 в 04:28
поделиться

Это потому, что на некоторых платформах long и int имеют разный размер.

16 bit:
long=32bits
int=16bits

32bit:
long=32bits
int=32bits

64bit(ILP64):
long=64bits
int=64bits

64bit(LP64):
long=64bits
int=32bits

64bit(LLP64): (what windows uses for whatever reason)
long long=64bits
long=32bits
int=32bits

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

long foo(int bar);
int foo(int bar);
2
ответ дан 5 December 2019 в 04:28
поделиться

int * и long * - разные типы, которые не обязательно совпадают. В каждой реальной реализации, я думаю, они есть, но это ни здесь, ни там для компилятора, соответствующего стандартам.

Я считаю, что это была одна из первых машин PDP, в которой char * был больше, чем int *. Причиной этого был странный размер int на этой архитектуре (36 бит). Таким образом, система будет упаковывать несколько 9-битных символов в один int, поэтому char * содержит адрес в формате (int *, смещение внутри int). **

Стандарт определяет, что все указатели могут быть представлены как void *, и подразумевает, что char * должен быть таким же, как void *, но нет особого требования, чтобы другие типы указателей были конвертируемыми.

* * Я не могу найти ссылки на это, так что источником этого мог быть теоретический (но все еще действительный) пример, а не реальная реализация. C ++ FAQ Lite

2
ответ дан 5 December 2019 в 04:28
поделиться

int и long определены как разные типы, поэтому вы можете программировать переносимые.

1
ответ дан 5 December 2019 в 04:28
поделиться

Вам не нужно вручную писать явное приведение при каждом вызове. Это можно сделать с помощью простого макроса:

#include <stdio.h> 

void foo_int( int *a ){ printf("a: %i\n", *a ); }

#define foo_int(a)  foo_int( (int*)a ) 

int main(){
  long l = 12;
  foo_int( &l ); // no warning, runs as expected
  foo_int(  l ); // no warning, segmentation fault
}
0
ответ дан 5 December 2019 в 04:28
поделиться