Присвоєння рядків масивам символів


79

Я трохи здивований наступним.

Приклад 1:

Приклад 2:

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

Відповіді:


66

При ініціалізації масиву C дозволяє заповнювати його значеннями. Так

в основному те саме, що

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

полягає у призначенні значення покажчика abcdна, sале ви не можете змінити, sоскільки тоді нічого не буде вказувати на масив.
Це може і працює, якщо sє char*- покажчиком, який може вказувати на що завгодно.

Якщо ви хочете скопіювати рядок, просто використовуйте strcpy.


7
Хороша відповідь, за винятком того, що ви більше ніколи не повинні використовувати простий strcpy. Використовуйте strncpy або strlcpy.
dwc

4
Крім того, s має бути const char *, а не char *.
aib

1
s[0] = 'x'; s[1] = 'y'; s[2] = 'z'; s[3] = 'm';працює, якщо потрібно замінити рядкові символи по одному навіть після ініціалізації.
RBT

@RBT, можливо, це відбувається на вашій платформі з прапорами компіляції, але часто ці рядки визначаються в пам'яті лише для читання. написання до нього призведе до помилки або помилки порушення доступу
shoosh

Чому? Виконання char s[100]; s = "abcd";помилок має цілковитий сенс, оскільки s ефективно розглядається як покажчик const. Отже, якщо ми спробуємо призначити "abcd" у другому рядку, ми намагаємось змінити значення вказівника, яке заборонено. Але якщо вказівник const вказує на розташування пам'яті, то вміст розташування пам'яті, звичайно, повинен змінюватися. Скажімо, sвказує на місце пам’яті 1000, тоді s містить 1000, а місце пам’яті # 1000 містить a. Я не можу змінити s, щоб зберегти нове місце в пам'яті, наприклад 2000, але я можу зберегти новий символ, наприклад, z в місці пам'яті 1000.
RBT

53

У C. немає такого поняття, як "рядок". У C рядки є одновимірним масивом char, що закінчується нульовим символом \0. Оскільки ви не можете призначити масиви в C, ви також не можете призначити рядки. Буквальне "привіт" - це синтаксичний цукор дляconst char x[] = {'h','e','l','l','o','\0'};

Правильним буде:

або ще краще:


2
Не рекомендований підхід. Остерігайтеся дивацтв strncpy: stackoverflow.com/a/1258577/2974922
нуклон

Думаю, такий підхід можна було б легко рекомендувати
Вольт

14

Ініціалізація та присвоєння - це дві різні операції, які тут використовують один і той же оператор ("=").


3

У наведеному вами прикладі s фактично ініціалізується в рядку 1, а не в рядку 2. Хоча ви не присвоїли йому значення явно в цей момент, компілятор це зробив. У рядку 2 ви виконуєте операцію присвоєння, і ви не можете призначити один масив символів іншому масиву символів, як цей. Вам доведеться використовувати strcpy () або якийсь цикл, щоб призначити кожен елемент масиву.


Навіщо вам 100 байт для привітання? Не char s[6]вистачає?
mLstudent33

1
@ mLstudent33, це був приклад, наданий OP.
Метт Девіс,

2

Розширити відповідь Спарра

Ініціалізація та присвоєння - це дві різні операції, які тут використовують один і той же оператор ("=").

Подумайте про це так:

Уявіть, що є 2 функції, викликані InitializeObjectта AssignObject. Коли компілятор бачить thing = value, він дивиться на контекст і викликає такий, InitializeObjectякщо ви створюєте новий thing. Якщо ви цього не зробите, він натомість телефонуєAssignObject .

Зазвичай це добре , як InitializeObjectі AssignObjectзазвичай поводяться таким же чином. За винятком випадків, коли йдеться про масиви символів (і кілька інших крайових випадків), і в цьому випадку вони поводяться по-різному. Навіщо це робити? Ну, це ціла інша публікація, що включає стек проти купи, і так далі, і так далі.

PS: Окрім того, якщо подумати про це таким чином, це також допоможе вам зрозуміти конструктори копіювання та інші подібні речі, якщо ви коли-небудь наважитесь на C ++


0

Зверніть увагу, що ви все ще можете зробити:


Набагато приємніше і простіше використовувати strncpy (), хоча я впевнений, що strncpy () робить саме це внутрішньо.
Chris Lutz

Звичайно. Але це наближається до 's = "hello";' Насправді це слід було реалізувати на мові C, бачачи, як можна призначити структури.
aib,

Я маю на увазі, що членська копія шляхом призначення в структурах, але не в масивах, не має сенсу.
aib

0

Я знаю, що на це вже відповіли, але я хотів поділитися відповіддю, яку я дав тому, хто задав дуже подібне питання в групі C / C ++ у Facebook.


Масиви не мають функцій оператора присвоєння *. Це означає, що ви не можете просто призначити масив символів рядковому літералу. Чому? Оскільки сам масив не має жодного оператора присвоєння. (* Це покажчик const, який неможливо змінити.)

масиви - це просто область суміжної виділеної пам'яті, а ім'я масиву насправді є покажчиком на перший елемент масиву. (Цитата з https://www.quora.com/Can-we-copy-an-array-using-an- assignment-operator )

Щоб скопіювати рядковий літерал (наприклад, "Hello world"або "abcd") у ваш масив char, вам потрібно вручну скопіювати всі елементи символу рядкового літералу в масив.

char s[100]; Це ініціалізує порожній масив довжиною 100.

Тепер, щоб скопіювати ваш рядковий літерал на цей масив, використовуйте strcpy

strcpy(s, "abcd");Це скопіює вміст із рядкового літералу "abcd"та скопіює в s[100]масив.

Ось чудовий приклад того, що він робить:

Очевидно, слід використовувати strcpyзамість цього користувальницького копіювального рядка, але це хороший приклад, який пояснює, як strcpyпринципово працює.

Сподіваюся, це допомагає!


-1

Ви можете використовувати це:

Де yylval - це char *. strdup from виконує роботу.


strdup from <string.h> виконує роботу :)
Єкатанділбург,

strdup робить дублікат і повертає покажчик на дублікат. У цьому випадку для масивів char ви повернулися туди, з чого почали, не зробивши жодної роботи
JoshKisb

-3

Що б я використав


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