#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
как два разных указателя (хотя они имеют одинаковый контент), поскольку они могут указывать или не указывать один и тот же адрес. Вы не должны полагаться на оптимизацию компилятора.
C11 Standard, 6.4.5, Строковые литералы, семантика
Не указано, являются ли эти массивы различными, если они элементы имеют соответствующие значения. Если программа пытается изменить такой массив, поведение undefined.
Формат для печати должен быть %p
:
printf("%p %p", (void*)p, (void*)p1);
Смотрите этот ответ для чего.
Ваш компилятор кажется довольно умным, обнаруживая, что оба литерала одинаковы. И поскольку литералы постоянны, компилятор решил не хранить их дважды.
Кажется, стоит упомянуть, что это не обязательно должно быть так. Пожалуйста, см. Blue Moon ответьте на это.
Btw: инструкция printf()
должна выглядеть так:
printf("%p %p", (void *) p, (void *) p1);
как "%p"
используется для печати значений указателя и определяется только для указателя типа void *
. * 1
Кроме того, я бы сказал, что код пропускает инструкцию return
, но, как представляется, в C файле меняется. Другие могут прояснить это.
* 1: Для char *
здесь нет необходимости указывать void *
, но для указателей на все остальные типы.
Ваш компилятор сделал что-то, называемое "объединение строк". Вы указали, что вам нужны два указателя, оба указывающие на один и тот же строковый литерал, поэтому он сделал только одну копию литерала.
Технически: он должен был пожаловаться на вас за то, что вы не указали указатели "const"
const char* p = "abc";
Возможно, это связано с тем, что вы используете Visual Studio или используете GCC без -Wall.
Если вы хотите, чтобы они дважды хранились в памяти, попробуйте:
char s1[] = "abc";
char s2[] = "abc";
Здесь вы явно указываете, что вам нужны два массива символов c-string, а не два указателя на символы.
Caveat: String pooling - это функция компилятора/оптимизатора, а не фасет языка. Поскольку такие разные компиляторы в разных средах будут создавать различное поведение в зависимости от таких факторов, как уровень оптимизации, флагов компилятора и значения строк в разных единицах компиляции.
Как говорили другие, компилятор замечает, что они имеют одинаковое значение, и поэтому решает, чтобы они делили данные в конечном исполняемом файле. Но это становится более привлекательным: когда я компилирую следующее с помощью 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".
Я узнал об этом некоторое время назад, поэтому я, возможно, не объяснил это действительно ясно, извините.
Фактически зависит от того, какой компилятор вы используете.
В моей системе с TС++ 3.5 он печатает два разных значения для двух указателей, т.е. два разных адреса.
Ваш компилятор спроектирован, он будет проверять наличие любого значения в памяти и в зависимости от его существования он переназначает или , используя ту же ссылку ранее сохраненного значения, если указано одно и то же значение.
Так что не думайте об этом слишком сильно, поскольку зависит от того, как компилятор разбирает код.
потому что строка "abc" сама является адресом в памяти. когда u пишут "abc" снова, он сохраняет тот же адрес
Это оптимизация компилятора, но забудьте оптимизировать переносимость. Когда-то скомпилированные коды более читабельны, чем фактические коды.
используется строковый литерал,
когда complier улавливает два одинаковых строковых литерала,
он дает одинаковое расположение памяти, поэтому он показывает то же местоположение указателя./