#include<stdio.h>
#include<string.h>
int main()
{
char * p = "abc";
char * p1 = "abc";
printf("%d %d", p, p1);
}
Коли я друкую значення двох покажчиків, це друкує одну і ту ж адресу. Чому?
#include<stdio.h>
#include<string.h>
int main()
{
char * p = "abc";
char * p1 = "abc";
printf("%d %d", p, p1);
}
Коли я друкую значення двох покажчиків, це друкує одну і ту ж адресу. Чому?
p
і p1
, то ви б помітили, що ці два вказівники зберігаються за двома різними адресами. Той факт, що їх значення однакове, - в даному випадку - не має значення.
p == p1
(вони не відрізняються), але &p != &p1
(вони різняться).
Відповіді:
Залежно від реалізації залежить, чи два різні рядкові літерали з однаковим вмістом розміщені в одному і тому ж місці пам'яті, або різні місця в пам'яті.
Слід завжди лікувати p
іp1
як два різні вказівники (навіть якщо вони мають однаковий вміст), оскільки вони можуть або не можуть вказувати на одну адресу. Не слід покладатися на оптимізацію компілятора.
C11 Standard, 6.4.5, String literals, семантика
Не визначено, чи відрізняються ці масиви за умови, що їх елементи мають відповідні значення. Якщо програма намагається змінити такий масив, поведінка не визначена.
Формат для друку повинен бути %p
:
printf("%p %p", (void*)p, (void*)p1);
Дивіться цю відповідь, чому.
i modify one of the pointer, will the data in the other pointed also be modified
Ви можете змінити вказівник, але не літеральний рядок. Наприклад char *p="abc"; p="xyz";
, цілком добре, тоді як char *p="abc"; p[0]='x';
викликає невизначену поведінку . Це не має нічого спільного volatile
. Незалежно від того, використовуєте ви volatile
чи ні, не слід змінювати будь-яку поведінку, яка нас тут цікавить. volatile
в основному змушує щоразу читати дані з пам'яті.
p
вказує на рядковий літерал "abc"
і p[0]='x'
намагається змінити перший символ рядкового літералу. Спроба модифікувати рядовий літерал є невизначеною поведінкою в C.
char []
в C. Тому, щоб зробити його const char*
доступним лише для читання ( як це має місце в C ++), потрібно також змінити тип . [продовження]
"Strings are no longer modifiable, and so may be placed in read-only memory"
історичний доказ того, що рядкові літерали раніше можна було модифікувати ;-)
Ваш компілятор здається досить розумним, виявляючи, що обидва літерали однакові. І оскільки літерали є постійними, компілятор вирішив не зберігати їх двічі.
Здається, варто згадати, що це не обов'язково має бути так. Будь ласка , дивіться Blue Moon "S відповідь на це .
До речі: printf()
Заява повинна виглядати так
printf("%p %p", (void *) p, (void *) p1);
як "%p"
буде використовуватися для друку значень покажчика, і він визначається лише для вказівника типу void *
. * 1
Також я б сказав, що код пропускає return
твердження, але стандарт C, схоже, перебуває в процесі зміни. Інші можуть люб’язно це пояснити.
* 1: Трансляція void *
сюди потрібна не для char *
вказівників, а для вказівників на всі інші типи.
==
використовуючи strcmpy()
функцію. Оскільки інший компілятор може не використовувати оптимізацію (це до компілятора - реалізація залежала), як Алк відповів PS: Blue Moon щойно додав про це.
Ваш компілятор зробив щось, що називається "об'єднання рядків". Ви вказали, що хочете два вказівники, обидва вказують на один і той самий літеральний рядок - отже, він створив лише одну копію літералу.
Технічно: він повинен був скаржитися на вас за те, що ви не робите покажчиків "const"
const char* p = "abc";
Можливо, це тому, що ви використовуєте Visual Studio або використовуєте GCC без -Wall.
Якщо ви прямо хочете, щоб вони двічі зберігалися в пам'яті, спробуйте:
char s1[] = "abc";
char s2[] = "abc";
Тут ви прямо заявляєте, що вам потрібні два масиви символів з рядком, а не два вказівники на символи.
Увага: об’єднання рядків - це функція компілятора / оптимізатора, а не аспект мови. Оскільки такі різні компілятори в різних середовищах вироблятимуть різну поведінку залежно від таких речей, як рівень оптимізації, прапори компілятора та чи знаходяться рядки в різних одиницях компіляції.
gcc (Debian 4.4.5-8) 4.4.5
не скаржиться (попереджає), хоча використовує -Wall -Wextra -pedantic
.
const
для рядкових літералів. Попередження вмикається опцією -Wwrite-strings
. Це, мабуть, не вмикається жодним іншим варіантом (наприклад -Wall
, -Wextra
або -pedantic
).
Як казали інші, компілятор помічає, що вони мають однакове значення, і тому вирішує, що вони надаватимуть спільний доступ до даних у остаточному виконуваному файлі. Але це стає приємніше: коли я компілюю наступне зgcc -O
#include<stdio.h>
#include<string.h>
int main()
{
char * p = "abcdef";
char * p1 = "def";
printf("%d %d", p, p1);
}
це друкує 4195780 4195783
для мене. Тобто p1
починається через 3 байти p
, тому GCC побачив загальний суфікс def
(включаючи \0
термінатор) і здійснив аналогічну оптимізацію до тієї, яку ви показали.
(Це відповідь, тому що це занадто довго, щоб бути коментарем.)
Рядкові літерали в коді зберігаються в сегменті даних, лише для читання коду. Коли ви записуєте рядовий літерал на зразок "abc", він насправді повертає "const char *", і якби у вас були всі попередження компілятора, це означало б, що ви кастуєте на той момент. Вам не дозволено змінювати ці рядки з тієї причини, на яку ви вказали у цьому питанні.
Коли ви створюєте рядковий літерал ("abc"), він зберігається в пам'яті, яка містить рядкові літерали, а потім він використовується повторно, якщо ви посилаєтесь на той самий рядковий літерал, таким чином обидва вказівники вказують на те саме місце, де " abc "рядковий літерал зберігається.
Я дізнався про це деякий час тому, тому, можливо, не пояснив би це дуже чітко, вибачте.
Це насправді залежить від того, який компілятор ви використовуєте .
У моїй системі з TC ++ 3.5 він друкує два різні значення для двох покажчиків, тобто дві різні адреси .
Ваш компілятор розроблений для того, щоб перевірити наявність будь-якого значення в пам'яті, і залежно від його існування він переприсвоїть або використає одне і те ж посилання на раніше збережене значення, якщо йдеться про те саме значення.
Тому не думайте надто над цим, оскільки це залежить від способу синтаксичного аналізу коду.
оскільки рядок "abc" сам по собі є адресою в пам'яті. коли ти знову пишеш "abc", він зберігає ту саму адресу
Це оптимізація компілятора, але забудьте про оптимізацію для перенесення. Інколи скомпільовані коди є більш читабельними, ніж фактичні коди.