2016-02-24 4 views
-1

Прошу прощения за заданный основной вопрос, который определенно был задан раньше, но я просто не мог его найти! Почему в C вы можете назначить строковый литерал для char *, но не можете назначить целочисленный литерал для int *?Разница между char * и int *

+1

Это неправильный вопрос. Правильный вопрос: «Почему я не могу назначить целочисленный литерал для« int * »и почему я также не могу назначить литерал символа символу' char * '. –

+0

Вам нужен код, чтобы продемонстрировать, что вы пытаетесь сделать. В C вы можете что-то бросить. – Claris

+0

Поскольку то, что вы называете «строковым литералом», фактически анализируется как постоянный массив символов. char * var = "string" - это то же самое, что и char var [] = {'s', 't', 'r', 'i', 'n', 'g'}; –

ответ

1

Строка в C не является примитивным типом данных. Это фактически оконечная последовательность символов с нулем. Таким образом, char *sp = "literal"; фактически работает с указателем на последовательность символов.

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

+0

Это звучит разумно, но тогда почему я помню чтение (я думаю, на языке программирования C++), что они разрешали присваивать строковый литерал char *, потому что в предыдущих определениях C тип строкового литерала был char *, но сегодня, когда это технически тип char [], его не следует разрешать (но для обратной совместимости). – Yamor

+0

ОП спрашивает о _string literal_, и в этом ответе обсуждается _string_. Похоже, но другое. «Строковый литерал не обязательно должен быть строкой (см. 7.1.1), потому что нулевой символ может быть встроен в него с помощью escape-последовательности \ 0." C11 сноска 78 – chux

+1

@Yamor: в стандарте C ясно, что модификации результирующего массива с литералом являются неопределенным поведением. Тем не менее, это явно не 'const char []', поэтому назначение символу 'char *' четко определено. Хотя я согласен, что это проблематично, иногда полезно использовать строку по умолчанию. Это запрещено в C++. Обратите внимание, что C++ и C - разные языки. Портирование правил от одного к другому чаще всего является проблемой, чем ожидают многие люди. – Olaf

3

Поскольку строковый литерал переводится в массив char (char []) во время перевода. C не имеет определенного типа строки.

В каждом массиве, он преобразуется для большинства использований (для sizeof, кроме _Alignof а также адрес-оператор &), он преобразуется («затухает») в указатель на его первый элемент. Поскольку это char *, нет никакой проблемы с его назначением переменной char * или передачей в качестве аргумента char *.

int/целочисленная константа (стандарт C не использует термин «литерал» здесь). OTOH можно преобразовать в указатель, но это не то, что вы обычно хотите. Также выполняется преобразование. Таким образом, компилятор выдает предупреждение для такого назначения без явного приведения. Чтобы подчеркнуть: очень мало случаев, когда вы хотите назначить целочисленную константу указателю (например, встроенным системам). Нет в обычных программах для ПК.

Чтобы получить адрес в intпеременной, используйте & оператор:

int i = 5; 
int *p = &i; 

Если вам нужно что-то похожее на строковый литерал, квалифицирует переменной const:

const int i = 5; 

Примечание что это все еще одно целое, поэтому вы не должны добавлять смещение к указателю, например p[1] - это неопределенное поведение.

+0

Хороший ответ, кроме «C не имеет строкового типа». поскольку это смущает проблему, и ИМО не требуется. C определяет _string literal_ и _string_, но не определяет, что вы подразумеваете под типом _string_. – chux

+0

@chux: Я добавил слово, чтобы уточнить. Но я думаю, что эта информация не является ни запутанной, ни ненужной. Как только вы согласитесь, что это просто «char []», и ничего больше, все становится намного яснее. Btw. 6.2.5 хорошо описывает концепцию типов. И термин «строка» используется только в контексте _string-literal_ или библиотек обработки строк. – Olaf

1

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

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

int *intptr = 123; 

той же причине вы не можете сделать:

char *charptr = 'a'; 

Обратите внимание, что нет ничего особенного char*. Вы можете назначить int* из массива int, например.

int intarray[] = {1, 2, 3}; 
int *intptr = intarray; 
+0

Это не совсем правильно. Массив не всегда преобразуется в «указатель ...». – Olaf

+0

Когда он может использоваться как r-значение без преобразования? – Barmar

+1

В стандарте C вообще не используется термин rvalue, но упоминается только один раз в сноске 64: _What иногда называют «rvalue» в этом Международном стандарте, описываемом как «значение выражения» _. См. Соответствующий раздел 6.3.2.1p3 за исключениями. – Olaf

1

Литература строки C представляет собой массив символов типа (состоит из множества символов). Целое число - это единственный элемент типа int.

Именно поэтому синтаксис для назначения каждого указателю отличается.

Назначение массива типа char или int указателю будет использовать тот же синтаксис, что и назначение одного элемента типа char или int указателю.

0

Когда вы говорите

char *str = "Hello"; 

что происходит, примерно такой же, как если бы вы сказали

const char tmpstr[] = {'H', 'e', 'l', 'l', 'o', '\0'}; 
char *str = tmpstr; 

То есть, всякий раз, когда вы пишете строку постоянным, как «Hello», компилятор автоматически выделяет небольшой статический массив символов, содержащий вашу строку. И это вполне нормально использовать этот массив в качестве инициализатора для указателя на символ. (Там на самом деле потенциал немного несоответствие, чтобы, является ли строка постоянной массив сопзЬ полукокса или нет, но давайте не будем беспокоиться о том, что на данный момент.)

Так что, если вы сказали

int tmpi[] = {1, 2, 3, 4, 5}; 
int *ip = tmpi; 

, что будет работать отлично, тоже.

Но вы не можете присвоить целое число к int *, потому что не имеет смысла:

int *ip2 = 123; /* WRONG */ 

Справа мы имеем целое число 123, а слева мы имеем int *, и вы не можете назначить целое число указателю.

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

+1

Стандарт намеренно не перехитрил сгенерированный массив 'const', но явно указывает, что изменения UB. В результате вы можете «char * p =« Hello »;', а 'const char a [] =" Hello "; char * p = a; 'должен генерировать предупреждение. Таким образом, вы не можете сделать его эквивалентным. – Olaf

+0

@ Олаф: Конечно. Вот почему я сказал «просто * о» точно так же ». –

+0

У него разные семантические преобразования - самая важная часть, а не только незначительная разница. – Olaf

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