LINUX.ORG.RU

С и символьные массивы

 


0

2

Всем доброго дня. Не так давно я изучаю С изучаю самостоятельно, так же рядом нет людей которые могут помочь и подсказать что-либо, надеюсь на вашу помощь. Собственно вопрос:

Есть в С символьные массивы и функции, и вот в той самой книге K&R описано поведение и вот к примеру код:

#include <stdio.h>
#include <string.h>
void change_a(char b[]);

int main() {
    char a[] = "test\n";
    printf(a);
    
    change_a(a);
    
    printf(a);

    return 0;
}

void change_a(char b[]) {
    int i;
    for (i = 0; i < strlen(b) -1; i++)
        b[i] = '3'; 
    
}

Этот код можно запуститьи увидеть что a[] изменяется. Я понимаю как это работает, я разобрался, да. Но почему оно так работает? Мы передаем массив в функцию, которая ничего не возвращает. Мы не переприсваем новые данные этому массиву в основной функции. Итого передав массив в качестве аргумента к какой-либо функции этот самый аргумент может быть изменен? Я не дошел еще до ссылок, но это не похоже на ссылки. Может кто-то объяснить почему так? Я могу и путать конечно, но разве этой не основа основ? То что передается в качестве аргумента не должно быть изменено, если нет явного присваивания.



Последнее исправление: beastie (всего исправлений: 1)

Вам надо улучшить понимание указателей. Потому что именно его вы передаёте.

flyshoot
()

b это указатель на начало массива.

Deleted
()

Потому что массивы в функцию по значению не предаются. Передаётся указатель на массив.

Для C обе записи эквивалентны:

void change_a(char b[]);
void change_a(char *b);

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

Спасибо всем за такой быстрый ответ. Теперь стало более понятно, но нет ли каких-то бест праксисов и общепринятых правил по тому как это использовать? Ладно если функция еще имеет адекватное имя и кода мало, но я так себе представляю что в обратном случае тут же совсем концов не найти и не разобраться где что меняется? Или это обычное дело и что-то вроде специфики языка, это повсеместно и просто нужно привыкнуть?

Kronick
() автор топика
Ответ на: комментарий от beastie

У массива нет понятия «значение», ибо массив не-копируемый тип. А то, что он не передаётся - это уже следствие. Все типы(не-копируемые, у которых нет понятия «значение») в сигнатуре типа аргументов преобразуются к указателю на этот тип. Массивы не исключения - такое же поведение и у функций.

А само «не значение» - это свойство присваивания, т.е. передачи. change_a(char b[]) = преобразуется в char * b, а поведение у него идентично char * b = m;

Т.е. отсутствие передачи «по значению» - свойство массива, а не функции.

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

Ясно, погуглил и увидел value types и указатели, теперь знаю что это такое, дальше уже буду читать сам и учиться пользоваться, спасибо.

Kronick
() автор топика

Ну все, спасибо всем кто участвовал, теперь мне все понятно, закрываю тему.

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

бэст практис - если функция, принимающая указатель, не должна менять значение, на которое указатель ссылается - следует объявлять указатель как указатель на const.

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

тут же совсем концов не найти и не разобраться где что меняется?

Добавлю тоже отсебятины, что const (как указал t1nman) в сигнатуре процедуры

void change_a(const char b[]);
помогает с этим и компилятор выдаёт ошибку, если процедура пытается изменить такой массив.

Тоже мучался по этому вопросу, когда изучал C лет 12 назад, эх...

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

вброс_моде

а какой бест практис, если функция принимает указатель на указатель и так же не должна менять значение?)

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

Думаю этот пример должен снять все вопросы.

$ cat test.c 
int f(const int **a, int const **b, int * const * c, int ** const d) {
    a = 0;
    *a = 0;
    **a = 0;
    b = 0;
    *b = 0;
    **b = 0;
    c = 0;
    *c = 0;
    **c = 0;
    d = 0;
    *d = 0;
    **d = 0;
}
$ cc test.c 
test.c:4:9: error: read-only variable is not assignable
    **a = 0;
    ~~~ ^
test.c:7:9: error: read-only variable is not assignable
    **b = 0;
    ~~~ ^
test.c:9:8: error: read-only variable is not assignable
    *c = 0;
    ~~ ^
test.c:11:7: error: cannot assign to variable 'd' with const-qualified type
      'int **const'
    d = 0;
    ~ ^
test.c:1:67: note: variable 'd' declared const here
int f(const int **a, int const **b, int * const * c, int ** const d) {
                                                     ~~~~~~~~~~~~~^
4 errors generated.
Legioner ★★★★★
()
Ответ на: комментарий от Kronick

Могу посоветовать не использовать такую нотацию: void change_a(char b[]). Вместо этого лучше писать void change_a(char *b). Результат такой же, а вопросов меньше.

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

Значение чего? Указателя, либо данных вложенного указателя? Точно так же. Сам вложенный указатель и его данные? const char * const *, хотя это не имеет смысла. Сам указатель? char * const *

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

Уже успел пока я мучил «свою» прокси. Молодец.

Кстати - типичное проявление куллстори:

Адепты часто спрашиваю меня - царь, почему ты пишешь указатель как человек char * ptr, а не как мы. Типичный показательный пример:

int f(const int **a, int const **b, int * const * c, int ** const d) 

Почему так.

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

функция принимает указатель на указатель на данные. я хочу защитить сами данные (не указатели) от случайного изменения. как быть?

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

функция принимает указатель на указатель на данные

Причём тут точно функция и указатели? Указатели - это интерфейс доступа - к памяти отношения не имеют.

я хочу защитить сами данные

Какие данные - подробнее. ro-данные - в дефолтной сишке никак. В онтопике mprotect и иже с ним.

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

боже ж ты мой, чтож ты такой тугой то

бэст практис - если функция, принимающая указатель, не должна менять значение, на которое указатель ссылается - следует объявлять указатель как указатель на const.

перевожу твои слова на код:

void foo(char *s);

ты говоришь

если функция, принимающая указатель, не должна менять значение, на которое указатель ссылается - следует объявлять указатель как указатель на const

т.е надо писать

void foo(const char *s);

я же задаю тебе ВСТРЕЧНЫЙ вопрос. есть функция

void foo(char **s);

ситуация ТАКАЯ ЖЕ. как быть в данном случае?

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

Ладно - я не буду тебя ловить на балабольстве - пощажу.

ситуация ТАКАЯ ЖЕ. как быть в данном случае?
void foo(char **s)

Так же. void foo(const char ** s)

Остальные случаи я так же описал: const char * const *, хотя это не имеет смысла. Сам указатель? char * const *

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

Так же. void foo(const char ** s)

и как использовать такую функцию?

char **data;
...
foo(data);

так? а что делать с варнингами? а если у меня варнинги трактуются как ошибки? какие бест практисес на этот случай?

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

да я тут царя потроллить решил) как-то плохо он троллится. тугой попался.

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

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

и как использовать такую функцию?

Что тебе помешает?

а что делать с варнингами?

Понятия не имею. Кастуй, вырубай конкретный варнинг в этом месте.

а если у меня варнинги трактуются как ошибки?

Это твоя проблема, а не моя. Что там низшие формы жизни мира сего себе «надумали» - мне не важно.

какие бест практисес на этот случай?

Понятия не имею.

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

да я тут царя потроллить решил) как-то плохо он троллится. тугой попался.

Естественно плохо - каким образом ты вообще меня можешь затралить. Я помню ещё твои убоги потуги с тредов про указатели.

Да и поймать меня не получиться. Но ты попытался.

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

И как только ты это выклал - ты проиграл, ибо а) не я говорил про const и б) я всегда говорил, что const для даунов, ибо смысла в сишке не имеет, кроме как для борьбы с конпелятором. Пруфцов на лоре тыщи - ищи.

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

Ну а дальше? Ну какрукнул ты это и что? Царь никогда не не говорил, что он разбирается в каких-то плебейских «бест практисес». И ему интересные ваши потуги со всякой хернёй назначение и причины которой вы объяснить не можете, а только обделаться, либо слиться в жалких попытках рассказать о том, что «я не могу писать код - это надо для ламерков», но мне насрать.

Я тебя пощадил, хотя ты бы у меня уже из темы выпилился за «указатель как защита памяти» и вместо благодарности ты пишешь мне херню? Отличная история.

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

функция принимает указатель на указатель на данные. я хочу защитить сами данные (не указатели) от случайного изменения. как быть?

registrant27492
()

Ого, в этом треде я наблюдаю пришествие Царя!

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