Подтвердить что ты не робот

Инициализация char * vs int *

Это возможно в С++:

const char* ch = "hello";

Но что-то вроде этого невозможно:

int* i = { 1, 2, 3 };

Оба char *ch и int* i являются простыми указателями. Почему char* может быть назначено с несколькими символами, а int* не может быть назначено несколькими ints?

Я знаю, что мы можем использовать

int x[] = {1, 2, 3};

но это не вопрос.

4b9b3361

Ответ 1

const char* ch = "hello";

похож на

static const char string_literal[] = { 'h', 'e', 'l', 'l', 'o', '\0' };
const char* ch = &string_literal[0];

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

То же самое возможно и для любого другого типа:

static int integer_list[] = { 1, 2, 3 };
int* i = &integer_list[0];
// or equivalently, just int* i = integer_list;

Теперь i[0] - 1, i[1] - 2, а i[2] - 3.

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

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

template <int a, int b, int c>
struct int_array { static const int values[3]; };
template <int a, int b, int c>
const int int_array<a, b, c>::values[] = { a, b, c };

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

const int* i = int_array<1, 5, 6>::values;

Часто бывает проще просто определить отдельную переменную массива, но бывают случаи, когда такой шаблон помогает.

Как отмечено в комментариях, можно более подробно определять шаблон, чтобы он работал для массивов произвольного типа и произвольной длины, но для этого требуется современный компилятор с хорошей поддержкой текущей версии С++ (для GCC и clang текущие версии являются точными, но обязательно передайте параметр -std=c++11 или -std=gnu++11, чтобы включить возможности С++ 11):

template <typename T, T... v>
struct static_array {
  static const T values[sizeof...(v)];
};

template <typename T, T... v>
const T static_array<T, v...>::values[sizeof...(v)] = { v... };

Теперь синтаксис для пользователя этого массива

const int* i = static_array<int, 1, 2, 3, 4>::values;
const unsigned* j = static_array<unsigned, 1, 2, 3, 4, 5>::values;

Ответ 2

Строковый литерал - это массив символов. Обратите внимание, что ch - это просто указатель на один символ, поэтому он действительно не указывает на строку в целом, а только на ее базовый адрес (адрес первого символа). Список инициализаторов (i.e {1, 2, 3}) не является массивом и поэтому не может использоваться для инициализации указателя.

Ответ 3

Литерал символов скомпилирован в часть инициализированного хранилища в сегменте данных вашего двоичного файла. const char * является указателем на это хранилище.

По сути, должно быть возможно сделать то же самое для const int *, если бы у вас был адрес блока памяти. Вы можете сделать это, используя встроенный ассемблер (но я никогда не пробовал), указав сегмент .data.