Різниця між 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.