Різниця між char * і const char *?


177

Яка різниця між

char* name

що вказує на постійний рядковий літерал, і

const char* name

що ти маєш на увазі під " постійним літеральним рядком" в C (не C ++)
gbulmer

1
... char * ім'я можна зробити так, щоб вказати на постійний буквальний рядок
Iceman

константа в "постійному літеральному рядку" є надмірною, оскільки всі рядкові літерали в теорії є постійними сутностями. Це вміст змінної, яку можна зробити постійною або змінюваною. Декларація "const" просто призведе до помилки часу компіляції, якщо ви спробуєте змінити вміст символу, на який вказує "ім'я"
Cupcake

Просте: ім'я "char * name" - це вказівник на char, тобто обидва можна змінити тут. ім'я "const char * name" - це вказівник на const char, тобто вказівник може змінюватися, але не char.
akD

Прочитайте ці речі справа наліво.
Jiapeng Zhang

Відповіді:


406

char*є змінним покажчиком на змінний символ / рядок.

const char*є змінним покажчиком на незмінний символ / рядок. Ви не можете змінити вміст розташування, на яке вказує цей вказівник. Крім того, компілятори зобов'язані надсилати повідомлення про помилки, коли ви намагаєтесь це зробити. З цієї ж причини конверсія з const char *в char*перестарілий.

char* constє незмінним покажчиком (він не може вказувати на будь-яке інше місце), але вміст місця, на яке він вказує, може змінюватися .

const char* constє незмінним покажчиком на незмінний символ / рядок.


4
Плутанина може бути очищена за допомогою використання змінної після вищезазначених висловлювань та шляхом посилання на цю змінну.
ankit.karwasra

3
@ ankit.karwasra, ти пропустив ще одного:char const *
Pacerier

Я вважаю, що два варіанти з змінним символом / рядком є ​​дуже небезпечними, оскільки ви можете зробити помилку сегментації помилок, і якщо ви справді розумні, ви можете зламати комп'ютер. Ось чому компілятори завжди показували попередження в тих реалізаціях, які я думаю
Даніель Н.

1
Чи не вимкнено не призведе до char *помилки сегментації під час роботи?
Дівяншу Майтані

1
Тому я використовую, constякщо хочу, щоб компілятор помилився, якщо я забув і змінив дані помилково, правда?
Бухгалтер з

43
char *name

Ви можете змінити діаграму на які nameточки, а також діаграму, на яку вона вказує.

const char* name

Ви можете змінити діаграму на які nameточки, але ви не можете змінити діаграму, на яку вона вказує.
виправлення: Ви можете змінити покажчик, але не графік, на яку nameвказує ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , див. "Приклади" ). У цьому випадку constспецифікатор стосується char, а не зірочки.

Згідно зі сторінкою MSDN та http://en.cppreference.com/w/cpp/language/declarations , constпопереднє значення *є частиною послідовності специфікаторів decl, тоді як constпісля *є частиною декларатора.
Послідовність специфікатора декларації може дотримуватися декілька деклараторів, тому const char * c1, c2декларується c1як const char *і c2як const char.

Редагувати:

З коментарів, ваше запитання, здається, задає питання про різницю між двома деклараціями, коли вказівник вказує на рядковий буквал.

У такому випадку не слід змінювати графік, на який nameвказують моменти, оскільки це може призвести до невизначеної поведінки . Строкові літерали можуть бути розподілені лише в областях пам'яті для читання (визначено реалізацію), і користувацька програма ні в якому разі не повинна змінювати її. Будь-яка спроба зробити це призводить до не визначеної поведінки.

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

Приклад онлайн-зразка:

#include <string.h>
int main()
{
    char *str1 = "string Literal";
    const char *str2 = "string Literal";
    char source[] = "Sample string";

    strcpy(str1,source);    //No warning or error, just Undefined Behavior
    strcpy(str2,source);    //Compiler issues a warning

    return 0;
}

Вихід:

cc1: попередження трактуються як помилки
prog.c: У функції 'main':
prog.c: 9: помилка: передача аргументу 1 'strcpy' відкидає кваліфікатори від цільового типу вказівника

Зауважте, компілятор попереджає про другий випадок, але не для першого.


Дякую .. я змішувався з постійним літеральним рядком, який визначається як: char * name = "String Literal"; Зміна "Строкового літералу" не визначено ..
Iceman

@ user1279782: Помилка, зачекайте! Ви тут говорите про пункти, які вказують на рядкові букви? У такому випадку ви не повинні змінювати nameтаблицю, на яку вказуються точки в будь-якому випадку. Це може призвести до UB.
Alok Save

Так, це був сенс. Тож у такому випадку ім'я char * та const char * поводяться схоже, правда?
Льодовик

4
Ця відповідь або є надзвичайно неоднозначною, або просто невірною. Я б інтерпретував "Ви не можете змінити діаграму, на яку називаєте точки, але ви можете змінити діаграму, на яку вона вказує". Як не в змозі змінити сам вказівник, але бути в змозі змінити місце пам'яті, на яке він вказує, що невірно: ideone.com/6lUY9s альтернативно для чистого C: ideone.com/x3PcTP
shroudednight

1
@shroudednight: Вам потрібно дізнатися трохи більше про не визначені форми поведінки, і потрібно розрізняти: дозволено і не слід робити. :)
Alok Зберегти

16
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)

constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error

constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok

// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";

lcharp[0] = 'X';      // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error

// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X';          // compile error
((char*)astr)[0] = 'X'; // ok

1
Жоден ваш покажчик не вказує на "постійні літеральні рядки" відповідно до питання.
caf

Варто зазначити, що зміна char *значення призводить до помилки сегментації, оскільки ми намагаємось змінити рядковий літерал (який присутній у пам'яті лише для читання)
Divyanshu Maithani,

10

В жодному випадку ви не можете змінювати літеральний рядок, незалежно від того, вказується як char *або const char *.

Однак різниця полягає в тому, що якщо вказівник є, const char *тоді компілятор повинен дати діагностику, якщо ви намагаєтесь змінити вказане значення, але якщо покажчик - char *це не так.


1
"Ні в якому разі не можна змінювати літеральний рядок, незалежно від того, чи ... [він] оголошений як char * або const char *" Я погоджуюся, що програміст не повинен намагатися, але ви говорите, що кожен компілятор C, для кожного Платформа відхилить код, впорядкує збиття коду під час виконання чи щось інше? Я вважаю, один файл може мати визначення та ініціалізацію, а інший файл може містити extern ... nameта мати *name = 'X';. У «належній операційній системі» це може вийти з ладу, але на вбудованих системах я б очікував, що це зробить щось, що стосується платформи / компілятора.
габлмер

@gbulmer: Ви не можете змінити літеральний рядок у правильній програмі C. Неправильна програма С, яка намагається спричинити, може бути ні тут, ні там.
caf

@gbulmer: Одне корисне визначення - це програма, яка не порушує жодних обмежень, визначених мовою C. Іншими словами, програма, яка модифікує літеральний рядок, є невірною так само, як та, яка перенаправляє нульовий покажчик або виконує ділення на 0, невірна.
caf

caf - я думав, що це може бути саме ти. Тоді "Ні в якому разі не можна змінювати буквений рядок", здається, надмірно заявляючи про це. Було б точно сказати: "В обох випадках обмеження, визначені мовою стандарту С, були порушені, незалежно .... Неможливо компілятор або система часу роботи ідентифікувати порушення стандарту у всіх випадках". Я припускаю, що стандарт займає позицію, що ефект не визначений?
габлмер

1
Коли стандарт не може нічого стверджувати в будь-якому випадку, я думаю, що визначення поведінки як "невизначеної" здається точно правильною межею і корисною. Щоб стверджувати відношення, "правильна програма C" " не може перенести нульовий покажчик" звучить рівнозначно доказуванню проблеми зупинки. Але я не проти Я б цього не робив і розраховував піти з нею "
безшовно

4

СЛУЧАЙ 1:

char *str = "Hello";
str[0] = 'M'  //Warning may be issued by compiler, and will cause segmentation fault upon running the programme

Наведене вище встановлює str на вказівку на буквальне значення "Hello", яке жорстко закодирується у бінарному зображенні програми, яке позначено як пам'ять лише для читання, означає, що будь-яка зміна цього прямого рядка є незаконною, і це може призвести до помилок сегментації.

СЛУЧАЙ 2:

const char *str = "Hello";
str[0] = 'M'  //Compile time error

СЛУЧАЙ 3:

char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".

2

Перше ви можете насправді змінити, якщо хочете, друге ви не можете. Прочитайте про constправильність (є кілька приємних посібників про різницю). Є також місце, char const * nameде ви не можете його повторно вказати.


Що саме може змінитися?
Антті Хаапала

2

Питання в тому, в чому різниця між

char *name

що вказує на постійний рядковий літерал, і

const char *cname

Тобто дано

char *name = "foo";

і

const char *cname = "foo";

Між двома різницями немає великої різниці, і обидва можна вважати правильними. Через довгу спадщину коду С, рядкові літерали мали тип « char[]ні» const char[], і існує багато старих кодів, які також приймаються char *замість const char *, навіть коли вони не змінюють аргументи.

Принципова відмінність двох в цілому полягає в тому, що *cnameабо cname[n]оцінюватимуться до значень типу const char, тоді як *nameабо name[n]оцінюються до значень типу char, які є змінними значеннями . Відповідний компілятор необхідний для створення діагностичного повідомлення, якщо мета призначення не є зміненим значенням ; воно не повинно створювати попередження про присвоєння значень типу char:

name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostics message

У будь-якому випадку від компілятора не потрібно зупиняти компіляцію; достатньо, щоб він видав попередження про присвоєння cname[0]. Отримана програма не є коректною програмою. Поведінка конструкції не визначена . Він може вийти з ладу або ще гірше, він не може вийти з ладу і може змінити рядковий літерал в пам'яті.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.