LINUX.ORG.RU

ANSI С: передача указателей в функцию


0

0

Друзья,

помогите разобраться. Изучаю книгу Кернигана/Ричи "Язык программирования С" (второе издание), сейчас на главе посвященной указателям. В разделе 5.2 как раз идет обсуждение, почему нужно передавать функции указатели, чтобы данная ф-ция могла менять объекты, расположенные по этим указателям.

Но в следующем разделе 5.2 приводится вариант ф-ции strlen:

/* strlen: возвращает длину строки */ int strlen(char *s) { int n; for (n = 0; *s != '\0'; s++) n++; return n; }

Цитата: "Так как переменная s - указатель, к ней применима операция ++; s++ не оказывает никакого влияния на строку символов функции, которая обратилась к strlen. Просто увеличивается на 1 некоторая копия указателя, находящаяся в личном пользовании функции strlen."

Как-то это не вяжется с ранее сказанным, ведь передавая указатель, получаешь возможность изменять содержимое этого указателя.

Либо я не так понял авторов...

anonymous

Указатель - адрес некоего объекта в памяти. Делая ++ ты лишь ползёшь по адресному пространству. Т.е. если char * s - указатель на строку (на первый символ строки), то s++ -> адрес второго символа. Для доступа к самому объекту его надо ещё и разыменовать.

Anoxemian ★★★★★
()

>int strlen(char *s) { int n; for (n = 0; *s != '\0'; s++) n++; return n; }

AFAIK strlen() в с89 имеет параметр с типом const char* а не char*. Данные, на которые указывает const char*, изменить действительно нельзя.

Absurd ★★★
()

Хоть я и не программер..

Противоречия тут нет. Для начала разберись с тем, что указатель на объект по сути своей есть некое целое число, обозначающее адрес на этот объект в памяти. При передаче функции указателя на некоторый объект (то есть некоторого целого числа) передается локальная копия адреса этого объекта (локальная копия данного целого числа). Функция в своей работе оперирует с этой локальной копией адреса. Оригинальный адрес при этом остается прежним. Тем не менее в теле функции можно изменять содержимое памяти по данному указателю, разыменовав его. Просто конкретно в _данном_ примере содержимое строки не меняется - идет только работа с копией адреса.

Как-то так..

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

Ну ты чего-то тут загнул! Всё гораздо проще!

Передается адрес строки, но менять содережимое этого адреса вовсе обязательно! И + как уже сказали, в прошлом стандарте был модификатор const.

+ передавать по ссылке экономичней! :)

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

> передавать по ссылке экономичней!

большие массивы (строки) -- экономичней. А небольшие структуры может быть не экономичней -- потому что косвенный доступ и возможно худшая оптимизация.

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

Мне, кстати, в своё время помогла понять указатели такая штука:

char mem[1024]; // это вся память
int ptr; // а это указатель

и когда я окончательно осознал, что указатель это по сути простое целое число, означающее сдвиг в "массиве байтов" памяти, всё стало на свои места.

PS а потом я осознал, что это не всегда простое целое число, но это уже другая история :)

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

> PS а потом я осознал, что это не всегда простое целое число, но это уже другая история :)

а когда не простое целое число ?

anonymous
()

>int strlen(char *s) { int n; for (n = 0; *s != '\0'; s++) n++; return n; }

при вызове функции создаётся копия указателя в стековом фрейме, с ней можешь делать что угодно... ещё можно этот указатель разименовать и обратиться к содержимому памяти. s++ изменяют локальную копию переменной char *s, для каждого вызова функции создаётся новая локальная копия в стеке

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

> при вызове функции создаётся копия указателя в стековом фрейме, с ней можешь делать что угодно... ещё можно этот указатель разименовать и обратиться к содержимому памяти. s++ изменяют локальную копию переменной char *s, для каждого вызова функции создаётся новая локальная копия в стеке

совершенно необязательно, что в стеке.

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

>совершенно необязательно, что в стеке.

А где еще? Возьмем Linux, win32, Solaris, BSD. Компиляторы - gcc, msvc, sun. Какоая из этих систем аллоцирует локальные переменные не в стеке?

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

>а когда не простое целое число ?

Вроде бы, в 16-битном режиме на x86 указатель был парой чисел: сегмент и смещение

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

> а когда не простое целое число ?

возможно он имел в виду что нюансы есть. Типа того что если к указателю на объект прибавить 1 то указатель сместится не на 1 байт, а на размер объекта.

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

Ну всякие экзотические сегмент + смещение, указатель на метод в С++ вообще что-то хитрое.

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

>Возьми что-нибудь отличное от x86, епт.

То есть арм и спарк?

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

> ...регистров много, специально заточены на передачу аргументов

Дык, там это тот же стек, только на регистрах. Оно так и обзывается -- RSE, "The Register Stack Engine".

Die-Hard ★★★★★
()
Ответ на: комментарий от Absurd

>Какоая из этих систем аллоцирует локальные переменные не в стеке?

Помоему речь шла о передаче аргументов ф-ции а не о локальных переменных этой ф-ции. Почитай про fastcall в linux и удивись - все аргументы вызовов ядра передаются в регистрах.

koTuk
()
Ответ на: комментарий от lv

>Ещё с микроконтроллерах так в некоторых (atmega к примеру)

Можно ли поподробней пример про сегментацию в atmega :)

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

Всем привет,

я автор вопроса. Большое спасибо, разобрался!

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.