Наивный взлом для этого должен был бы создать список или массив как
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
И затем выбрать случайным образом из этого.
Тот факт, что long
и int
оба являются 32-битными на вашем конкретном компиляторе и оборудовании, не означает, что они всегда будут 32-разрядная версия для любого оборудования и каждого компилятора.
C (и C ++) были разработаны для обеспечения переносимости исходного кода между разными компиляторами и разным оборудованием.
Если вам нужно действительно краткое введение: Unicode за 5 минут
Или, если вам нужны однострочные символы:
Почему вас это волнует? Потому что, не зная используемого набора символов и кодировки, вы действительно не знаете, какие символы представляет данный поток байтов.
long
и int
- два разных типа, даже если они одного размера. Если бы некоторые компиляторы обрабатывали их одинаково, в мире шаблонов С ++ были бы огромные последствия.
Здесь я вижу два разных вопроса.
Во-первых, на современных архитектурах довольно безопасно предположить, что указатели имеют одинаковый размер (то есть нет ближних / дальних указателей; но указатели на функции-члены не являются обычными указателями и могут иметь другой размер); а в 32-битной системе этот размер обычно составляет 32 бита. C даже заходит так далеко, что автоматически приводит void *
к чему-либо еще, потому что, в конце концов, указатель - это просто адрес памяти. Однако в определениях языка разные указатели различаются как разные типы. Я считаю, что причина этого в том, что разные типы могут иметь разное выравнивание (правило void *
заключается в том, что на самом деле ничто не может быть типа void
, поэтому, если у вас есть указатель на ] void
вы, вероятно, знаете правильный тип и, неявно, правильное выравнивание ( см. примечание ))
Во-вторых, как указывали другие, long
s и int
являются принципиально разными типами с преобразованиями по умолчанию встроен. Стандарты требуют, чтобы long
s были не меньше, чем int
s, но, возможно, больше. В вашей архитектуре выравнивание, вероятно, такое же, но на других архитектурах, которые также могут отличаться.
В вашем случае можно обмануть, но это не переносимо. Если вы не #include
правильных объявлений функций, а вместо этого просто пересылаете их сами, все должно волшебным образом сработать, потому что в вашем случае long
s и int
s являются совместим (при условии отсутствия проблем с подписью; также в C ++ вам потребуется extern " s назад к исходному типу, и в этом случае выравнивание и размер будут такими же.
Но есть складки. Во-первых, упаковка объекта должна быть одинаковой в обоих местах (не проблема с примитивными типами). С другой стороны, возможно указать произвольную память с помощью void *
s, но программисты, которые делают это, предположительно понимают, что они делают.
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:
So even though the underlying machine type is the same, it is not allowed to implicitly convert between the two types.
Это потому, что на некоторых платформах 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);
int * и long * - разные типы, которые не обязательно совпадают. В каждой реальной реализации, я думаю, они есть, но это ни здесь, ни там для компилятора, соответствующего стандартам.
Я считаю, что это была одна из первых машин PDP, в которой char * был больше, чем int *. Причиной этого был странный размер int на этой архитектуре (36 бит). Таким образом, система будет упаковывать несколько 9-битных символов в один int, поэтому char * содержит адрес в формате (int *, смещение внутри int). **
Стандарт определяет, что все указатели могут быть представлены как void *, и подразумевает, что char * должен быть таким же, как void *, но нет особого требования, чтобы другие типы указателей были конвертируемыми.
* * Я не могу найти ссылки на это, так что источником этого мог быть теоретический (но все еще действительный) пример, а не реальная реализация. C ++ FAQ Lite
int и long определены как разные типы, поэтому вы можете программировать переносимые.
Вам не нужно вручную писать явное приведение при каждом вызове. Это можно сделать с помощью простого макроса:
#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
}