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

Как понимать char * ch = "123"?

Как я должен понимать char * ch="123"?

'1' является char, поэтому я могу использовать:

char x = '1';
char *pt = &x;

Но как я понимаю char *pt="123"? Почему char *pt указывает на строку?

Значит ли pt значение первого адреса для "123"? Если да, как мне получить длину для строки, на которую указывает pt?

4b9b3361

Ответ 1

Это действительно хороший вопрос, и это следствие нескольких странностей на языке C:

1. Указатель на char (char*) может, конечно, также указывать на конкретный char в массиве символов. Это то, на что опирается арифметика указателя:

// create an array of three chars
char arr[3] = { 'a', 'b', 'c'};
// point to the first char in the array
char* ptr = &arr[0]
// point to the third char in the array
char* ptr = &arr[2]

2. Строковый литерал ("foo") на самом деле не является строкой как таковой, а просто массивом символов, за которым следует нулевой байт. (So ​​ "foo" фактически эквивалентно массиву {'f', 'o', 'o', '\0'})

3. В C массивы "распадаются" на указатели на первый элемент. (Вот почему многие неверно говорят, что "нет разницы между массивами и указателями в C" ). То есть, когда вы пытаетесь назначить массив объекту-указателю, он устанавливает указатель на первый элемент массива. Поэтому, учитывая объявленный выше массив arr, вы можете сделать char* ptr = arr, а это значит, что char* ptr = &arr[0].

4. В любом другом случае синтаксис, подобный этому, заставит указатель указывать на rvalue (свободно говорящий, временный объект, который вы не можете принять по адресу), который обычно незаконным. (Вы не можете сделать int* ptr = &42). Но когда вы определяете строковый литерал (например, "foo"), он не создает rvalue. Вместо этого он создает массив char со статическим хранилищем. Вы создаете статический объект, который создается при загрузке программы, и, конечно, указатель может с уверенностью указать на это.

5. Строковые литералы на самом деле должны быть помечены как const (потому что они статичны и доступны только для чтения), но поскольку ранние версии C не имеют ключевого слова const вы можете опустить спецификатор const (по крайней мере до С++ 11), чтобы не ломать старый код (но вам все равно придется обрабатывать переменную как доступную только для чтения).

Итак, char* ch = "123" действительно означает:

  • напишите массив char {'1', '2', '3', '\0'} в статический раздел исполняемого файла (так что, когда программа загружается в память, эта переменная создается в разделе памяти только для чтения)
  • когда эта строка кода выполняется, создайте указатель, который указывает на первый элемент этого массива

В качестве бонусного забавного факта это отличается от char ch[] = "123";, что вместо этого означает

  • напишите массив char {'1', '2', '3', '\0'} в статический раздел исполняемого файла (так что, когда программа загружается в память, эта переменная создается в разделе памяти только для чтения)
  • когда эта строка кода выполняется, создайте массив в стеке, который содержит копию этого статически выделенного массива.

Ответ 2

char* ptr = "123"; совместим и почти эквивалентен char ptr[] = { '1', '2', '3', '\0' }; (см. http://ideone.com/rFOk3R).

В C указатель может указывать на одно значение или массив смежных значений. С++ унаследовал это. Таким образом, строка представляет собой массив символов (char), заканчивающийся на '\0'. А указатель на char может указывать на массив char.

Длина задается числом символов между началом и терминалом '\0'. Пример C strlen дает длину строки:

size_t strlen(const char * str)
{
    const char *s;
    for (s = str; *s; ++s) {}
    return(s - str);
}

Да, он терпит неудачу, если в конце нет '\0'.

Ответ 3

Строковый литерал - это массив из N const char, где N - длина литерала, включая неявный терминатор NUL. Он имеет статическую продолжительность хранения, а реализация определена там, где она хранится. Отсюда он тот же a с нормальным массивом - он распадается на указатель на его первый символ - a const char*. То, что у вас там есть, не является законным (уже не с начала стандарта С++ 11) в С++, оно должно быть const char* ch = "123";.

Вы можете получить длину литерала с помощью оператора sizeof. Однако, если он распадается на указатель, вам нужно пройти через него и найти терминатор (что делает функция strlen).

Итак, с const char* ch; вы получаете указатель на постоянный тип символа, который может указывать на один символ или на начало массива символов (или где-нибудь между началом и концом). Массив может быть динамически, автоматически или статически распределен и может быть изменен или нет.

В чем-то вроде char ch[] = "text"; у вас есть символ . Это синтаксический сахар для инициализатора нормального массива (как в char ch[] = {'t','e','x','t','\0'};, но обратите внимание, что литерал все равно будет загружен в начале программы). Что здесь происходит:

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

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

Ответ 4

Указатель на массив?

Указатель указывает только на один адрес памяти. Фраза, указывающая указатель на массив, используется только в свободном смысле - указатель не может хранить несколько адресов одновременно.

В вашем примере char *ch="123" указатель ch действительно указывает только на первый байт. Вы можете написать код, как показано ниже, и это будет иметь смысл:

char *ch = new char [1024];
sprintf (ch, "Hello");    
delete [] ch;

char x = '1';
ch = &x;

Обратите внимание на использование указателя ch, чтобы указать как память, выделенную линией new char [1024], так и адрес переменной x, при этом все тот же тип указателя.

Строки в стиле C заканчиваются на нуль

Строки в C, которые были завершены с нулем, т.е. специальный конец '\0' был добавлен в конец строки и предполагается, что он существует для всех char * функций (таких как strlen и printf) Таким образом, вы можете определить длину строки, начиная с первого байта, и продолжить, пока не найдете байт, содержащий 0x00.

Подробной примерной реализацией функции стиля strlen будет

int my_strlen (const char *startAddress)
{
  int count = 0;
  char *ptr = startAddress;
  while (*ptr != 0)
  {
     ++count;
     ++ptr;
  }

  return count;
}

Ответ 5

В C нет строк, но есть указатели на символы. *pt действительно не указывает на строку, а на отдельные символы ('1'). Однако некоторые функции принимают char*, поскольку аргумент предполагает, что байт на адрес, следующий за адресом, на который указывает их аргумент, установлен на 0, если они не будут работать на нем.

В вашем примере, если вы попытались использовать pt для функции, которая ожидает строку с нулевым завершением (в основном, которая ожидает, что она столкнется с байтом со значением 0, когда он должен прекратить обработку данных) вы столкнетесь с ошибкой сегментации, поскольку x='1' дает x значение ascii символа 1, но не более того, тогда как char* pt="123" дает pt значение адреса 1, но также помещает в это память, байты, содержащие значения ascii 1, 2, 3, за которыми следует байт со значением 0 (ноль).

Таким образом, память (в 8-разрядной машине) может выглядеть так:

Адрес = Содержимое (0x31 - это код Ascii для символа 1 (один))

0xa0 = 0x31
0xa1 = 0x32
0xa2 = 0x33
0xa3 = 0x00

Предположим, что вы на той же машине char* otherString = malloc(4), предположите, что malloc возвращает значение 0xb0, которое теперь является значением otherString, и мы хотели скопировать наш "pt" ( который имел бы значение 0xa0) в otherString, вызов strcpy выглядел бы так:

strcpy( otherString, pt );

То же, что и

strcpy( 0xb0, 0x0a );

strcpy затем примет значение адреса 0xa0 и скопирует его в 0xb0, он увеличит его указатели до "pt" до 0xa1, проверьте, равен ли 0xa1 нулю, если он не равно нулю, он будет увеличивать указатель на "otherString" и копировать 0xa1 в 0xb1 и т.д., пока не будет указатель "pt" 0xa3, в этом случае он вернется как он обнаружил, что достигнут конец "строки".

Это, по сути, не 100%, как это происходит, и может быть реализовано по-разному.

Вот один http://fossies.org/dox/glibc-2.18/strcpy_8c_source.html

Ответ 6

char* pt = "123"; does two things:

1. создает строковый литерал "123" в ПЗУ (обычно это в разделе .text)  2. создает a char*, которому назначается начало места памяти, в котором находится строка.

из-за этих операций like pt[1] = '2'; являются незаконными, поскольку вы пытаетесь записать в память ПЗУ.

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