Перейти к содержанию
    

модификатор const. Как правильно использовать в Си

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

Ну а const как бы лишний раз напоминает, что за объект тот самый идентификатор.

Плюс, достаточно часто функции требуют указатель (обычный), а мы туда передаем, например, строку. Компилятор тоже будет выдавать предупреждение, однако этот момент мы умалчиваем, видимо...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 часов назад, Arlleex сказал:

Плюс, достаточно часто функции требуют указатель (обычный)

такого не бывает, либо это говнокод. Например функции (пусть будет некое API, реализация скрыта в библиотеках)

void func1(int *value);

void func2(const int *value);

если я вызову func2 и передам туда (обычный или конст) указатель, я буду 100% уверен в том, что мои данные не изменяться. Такой аргумент "const int *value" с языка с/с++ можно перевести на русский как "уважаемый программист, при использовании этого метода, будьте уверены, что ваши данные останутся неизменными". В func1 данные могут быть модифицированы. Если вы пишете свою функцию и в аргументах передаете указатель или ссылку, и этот метод не должен менять данные по * или &, то всегда ставьте const. Это убережет от ошибок реализации метода, а также будет в дальнейшем будет легче пользоваться методом.

8 часов назад, Arlleex сказал:

функции требуют указатель (обычный), а мы туда передаем, например, строку. Компилятор тоже будет выдавать предупреждение, однако этот момент мы умалчиваем, видимо...

если строка константная, ворнинга не будет, компилятор даст ошибку и не соберёт код. если строка не конст, то ни каких ворнингов не будет.

Изменено пользователем razrab83

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 часов назад, razrab83 сказал:

если строка константная, ворнинга не будет, компилятор даст ошибку и не соберёт код. если строка не конст, то ни каких ворнингов не будет.

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

Это один из недостатков, зачем-то тянущийся в си-компиляторах с древних времён. Видимо для некой мифической совместимости со старым кривым кодом.

Обсуждали этот вопрос уже тут на форуме как-то. Не так давно....

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, jcxz сказал:

Вы бы сперва попробовали, прежде чем говорить. Arlleex сказал правильно...

Скорее, наоборот - @razrab83 прав. Но частично.

Например, смотрю поведение в Keil uVision (CLang).

void func1(char *p)
{}

void func2(const char *)
{}

...

char       str1[] = "String";
const char str2[] = "String";

func1("String"); // ok
func2("String"); // ok

func1(str1);     // ok
func1(str2);     // warning: passing 'const char [4]' to parameter of type 'char *' discards qualifiers
func2(str1);     // ok
func2(str2);     // ok

 

Для меня (по крайней мере) тут все логично, кроме одного. ИМХО, func1("String") тоже должна давать warning, но этого не происходит.

Полезные ссылки: №1, №2.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

30 минут назад, Arlleex сказал:

Скорее, наоборот - @razrab83 прав. Но частично.

Ну а где-ж прав? В вашем же примере func1("String") по уму должно выдать ошибку. Т.к. "String" - константа? По факту - константа. Ибо находится в RO-секции. Но почему-то при явной передаче аргументом функции считается типом "char *".

Цитата

Для меня (по крайней мере) тут все логично, кроме одного. ИМХО, func1("String") тоже должна давать warning, но этого не происходит.

IAR на такое вообще выдаёт error, а не warning.

Error[Pe167]: argument of type "char const *" is incompatible with parameter of type "char *" D:\...\main.cpp 415 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, jcxz сказал:

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

в чем прав и какой код?

void func1(char *p){} //со слов Arlleex "достаточно часто функции требуют указатель (обычный)"

const char *str = "asd";

func(str); //тут error. со слов Arlleex " а мы туда передаем, например, строку."

 

Цитата

IAR на такое вообще выдаёт error

вот об это я и говорю.

Изменено пользователем razrab83

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

49 minutes ago, jcxz said:

Т.к. "String" - константа? По факту - константа. Ибо находится в RO-секции. Но почему-то при явной передаче аргументом функции считается типом "char *".

Ну так компилируйте с флагами -Wall -Werror -Wextra, и все предупреждения будут расцениваться как ошибки. А вообще, gcc обычно предупреждает, если пытаешься константу отправить в функцию, где перед аргументом const отсутствует. Вот наоборот — запросто.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Eddy_Em сказал:

Ну так компилируйте с флагами -Wall -Werror -Wextra, и все предупреждения будут расцениваться как ошибки.

Причём тут это??? Вы не поняли предмета обсуждения.

1 час назад, razrab83 сказал:

func(str); //тут error. со слов Arlleex " а мы туда передаем, например, строку."

А теперь попробуйте func("str");

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

25 minutes ago, jcxz said:

А теперь попробуйте func("str");

cat 1.c
#include <stdio.h>

void p(char *s){
	printf("str: %s\n", s);
}

int main(){
	const char *x = "hello";
	char *y = "test";
	p(x);
	p(y);
	p("String");
	return 0;
}

И действительно:

gcc 1.c 
1.c: В функции <<main>>:
1.c:10:4: предупреждение: передача аргумента 1 <<p>> отменяет квалификатор <<const>> указуемого типа [-Wdiscarded-qualifiers]
   10 |  p(x);
      |    ^
1.c:3:14: замечание: ожидался тип <<char *>>, но аргумент имеет тип <<const char *>>
    3 | void p(char *s){
      |        ~~~~~~^

Т.е. строка, хоть и является фактически const char*, рассматривается gcc как char (эквивалентно p(y))!

И нужно явно писать

p((const char*)"String");

чтобы получить предупреждение.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 часов назад, jcxz сказал:

А теперь попробуйте func("str");

Цитата

"C:\testIar\main.c",8  Warning[Pe2464]: conversion from a string literal to "char *" is deprecated

это иар из коробки в идэ выдал.

с++ тоже дает ворнинг на такой код

Цитата

main.cpp:22:14: warning: ISO C++ forbids converting a string constant to ‘char*’

gcc молча съел. но gcc с ключом -Wwrite-strings выдал

Цитата

../Src/main.c:33:8: warning: passing argument 1 of 'func1' discards 'const' qualifier from pointer target type
  func1("asdf");
        ^~~~~~
../Src/main.c:24:5: note: expected 'char *' but argument is of type 'const char *'
 int func1( char *a)

 

ps

да, си, в отличии от с++, может пропустить "asdf" (но не const char *str = "asdf"). Лишний раз задаюсь риторическим вопросом - зачем нужен си? С++ более строг. Если даже не любишь(не знаешь) ооп, не нужны классы, шаблоны, stl и прочие плюшки... пиши код аля си стайл с любимым goto, но собирай его сипипишным компилятором.   

Изменено пользователем razrab83

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

C хорош тем, что даёт полную свободу выстрелить себе в ногу!

За это я так люблю этот ЯП. Остальные же в той или иной степени огораживают, что есть крайне плохо!!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, razrab83 сказал:

это иар из коробки в идэ выдал.

Какая версия и с какими ключами?

Мой 7.80.4 не выдаёт ничего.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 hours ago, Eddy_Em said:

Т.е. строка, хоть и является фактически const char*, рассматривается gcc как char (эквивалентно p(y))!

Нет, она фактически не является const char *, она является изменяемым массивом символов (mutable char array), т.е. аналогом char str[] = "String"
И между char* и char[] огромная разница.
 

Например, вот это вызовет Segfault:

char * p = "inmutable";
p[1] = 'm';


А вот это прекрасно исполнится:

char s[] = "chang  me";
s[5] = 'e';

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 минут назад, one_eight_seven сказал:

Например, вот это вызовет Segfault:


char * p = "inmutable";
p[1] = 'm';

 

Вот это-то и плохо. По уму тут компилятор должен выдать warning/error. На первую строчку.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, jcxz said:

Вот это-то и плохо. По уму тут компилятор должен выдать warning/error.

Не согласен. Это не работа компилятора. Это работа статического анализатора кода, lint или чего-то в этом роде.

В первом случае указатель прямо указывает на string literal. Во втором случае создаётся массив на стеке, который инициализируется из string literal'а, и указатель s не указывает на оригинальный литерал (который неизменяемый).
 

Но в C, действительно много плохого. Например, вот эти вот неявные приведения типов. Я согласен, что нет ничего плохого, в том, чтобы повысить строгость типизации и повысить тем самым безопасность типов. Например, ключами компиляции.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...