LINUX.ORG.RU

Указатель на указатель в си

 


0

2

Почему для изменения значения переменной через доступ к указатель указателя необходимо использовать две звезды * * в примере ниже, а write хватает * звезды чтобы добраться до значения?

    char test = 'a';
    char *ptr_to_char = &test;
    char **ptr_to_ptr = &ptr_to_char;
    
    *ptr_to_ptr = 'd'; 
/* Incompatible integer to pointer conversion assigning to 'char *' from 'int'*/

    **ptr_to_ptr = 'b';
/* works */
    
    write(1, *ptr_to_ptr, 1);
/* also works */


Последнее исправление: dffrwpv (всего исправлений: 4)
Ответ на: комментарий от MOPKOBKA

./a.out B

Дядя, а как у тебя получилось В если изначально это разные значения:

*pa = &a, *pb = &b + 1;
Добавил ещё одну строчку перед return 0 чтобы убедится  в этом:
printf("(long)pa=%d, (long)pb=%d, pb=%d, pa=%d", (long)pa, (long)pb, pb, pa);

gcc -g -Wall .\equals.c

pa=-1488621520, pb=-1488621512, pb=-1488621512, pa=-1488621520


 gcc  --version
gcc.exe (Rev2, Built by MSYS2 project) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

А выхлоп это ведь тупо адрес ссылочных переменных pa и pb?

Ygor ★★★★★
()
Ответ на: комментарий от Ygor

Дядя, а как у тебя получилось В если изначально это разные значения:

Потому что адреса отличаются на 8 байтов, а добавляя единицу я привожу их к одному адресу.

https://gcc.godbolt.org/z/ahGnace99

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Потому что адреса отличаются на 8 байтов, а добавляя единицу я привожу их к одному адресу.

https://gcc.godbolt.org/z/ahGnace99

Не получается у меня равенства, попробую cl, clang позже.

Ygor ★★★★★
()
Ответ на: комментарий от Ygor

В Windows может выравнивание в 16 байт, попробуй вместо 1 подставить 2, -1, -2. Я только не пойму зачем тебе это, я ссылку кинул, там сразу запуск программы есть, и справа ее вывод отображается.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

-1

Показывает

A

B

А вот если написать:

a=5;

То останется только 
B

а выхлоп будет:

(long)pa=-1405063120, (long)pb=-1405063120, pb=ac407030, pa=ac407030 a=5, b=0

Чому так? Указатели же ссылаются на одну ячейку памяти?

Ygor ★★★★★
()
Последнее исправление: Ygor (всего исправлений: 2)
Ответ на: комментарий от Ygor

Чому так? Указатели же ссылаются на одну ячейку памяти?

А я же написал, в С указатели это некая абстракция, она не должна иметь те же свойства, что и указатели в x86. По стандарту указатели из программы которую я написал, указывают на разные виртуальные ячейки. Если нужно более точное объяснение, то можно читать стандарт.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Ты давно делил или умножал указатели?

Прикинь! Вот как раз сегодня этим и занимался! Двумерная таблица, тут надо как раз умножать значение указателя, что бы получить адрес нужного элемента в таблице.

Vic
()
Ответ на: комментарий от MOPKOBKA

Наблюдаемый эффект, когда печатается только «B», идет не от языка, а от оптимизатора, встроенного в конкретный компилятор. Если вы поменяете в своем примере ключ -Ofast на -O0 (плюс надо скорректировать вычисляемый адрес), тогда выведется как положено, A и B.

https://gcc.godbolt.org/z/6fsG857xa

PS. К сожалению, многие путают оптимизацию и свойства языка. В языке, адреса это обычные числа (номер байта в адресном пространстве процессора), а контроль типов осуществляется только по типу переменной, хранящей адрес (т.е. по типу переменной указателя). Все остальное - вольности оптимизаторов. В вашем случае, с применением -Ofast оптимизатор просто вырезал из бинарного кода сравнение и печать «A».

Vic
()
Последнее исправление: Vic (всего исправлений: 6)
Ответ на: комментарий от Ygor

Чому так? Указатели же ссылаются на одну ячейку памяти?

Это результат работы оптимизатора с ключом -Ofast. Чуть выше я дал пример с ключом -O0, отключающий бОльшую часть оптимизаций, пример стал выдавать «A» и «B», как и положено.

Судя по ассемблерному листингу, когда ставишь -Ofast, то первое условие вместе с печатаньем «A» просто выкидывается и не попадает в бинарный код.

Собственно все эти новомодные «си стандарты» по большей степени касаются именно работы всяких оптимизаторов.

Vic
()
Последнее исправление: Vic (всего исправлений: 1)
Ответ на: комментарий от Vic

Наблюдаемый эффект, когда печатается только «B», идет не от языка, а от оптимизатора

Какая глупость! Не надо путать людей. Оптимизатор исходит из правил языка, именно по ним он оптимизирует. Если бы оптимизатор делал это по своему усмотрению, не опираясь на язык, то можно было бы сказать что это ошибка, и уже бежать с багрепортом в gcc.

Более того, я могу найти такой багрепорт, где разработчики gcc говорят что это поведение следует стандарту, и оно наоборот правильное, а то что получается без оптимизаций, платформокомпиляторозависимое.

В языке, адреса это обычные числа

Я уже показал что это не так.

номер байта в адресном пространстве процессора

Даже на x86 это не является правдой.

Кстати, в С указатели на функции, и указатели на данные даже не кастуются, нельзя преобразовать указатель на функцию в (void*), потому что стандартом предусмотрено, что на некой платформе, к примеру, код может быть в одном сегменте, а данные в другом. И для функции может потребоваться два числа. Поверх этого еще другие правила. Ну это все ты можешь прочесть, а не выдумывать лично какие то объяснения с байтами...

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от Vic

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61502
jsm-csl@polyomino.org.uk
Если два указателя печатаются одинаково и имеют один и тот же битовый шаблон, это еще не означает, что их нужно сравнивать как равные

Так же из документов комитета

Например, если два объекта одного и того же типа имеют непересекающиеся времена жизни и занимают один и тот же адрес, указатель на один не может использоваться для доступа к другому.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от mx__

что это за проги такие что выходят за границы памяти смотря какой памяти. Внутри проги или снаружи (в рамках ОС). Снаружи некоторые проги лезут в чужую память и оттуда читают/пишут данные, например, dwarf therapist так делает, потому что автор dwarf fortress api никак не сделает и потому его программу через этот механизм модифицируют/управляют ей снаружи. Внутри да спорно, но тут скорее язык надо другой. Который безопаснее сишки, но более низкоуровневый чем java/c#. Ниша очень специфичная выходит, вроде как под растишку. Вот к растишке уже с этими претензиями как мне кажется можно выкатываться.

anonymous
()