LINUX.ORG.RU

Меняю const. Стреляю себе в ногу и получается

 ,


0

1
>>> cat weirdConst.c
#include <stdio.h>

static const unsigned a0 = 0;
int main() {
	*((int *)&a0) = 102;
	
	printf("%u\n", a0);
	
	return 0;
}
>>> gcc -Wall -Wextra weirdConst.c
>>> gcc --version
gcc (GCC) 6.3.0  

0 ошибок 0 ворнингов. Iar тоже пофиг.

При исполнении правда Segmentation fault

Как бы ок. Но меня возмущает что гцц игнорирует подобное.

На мк вообще нету const секции и там константа действительно изменяется.

Это я сравнивал enum vs static const. enum (за исключением того что он всегда int типа) точно такое не позволит. И памяти тоже гарантированно не занимает (важно для маленького размера флеша/озу мк)

#define можете даже не упоминать. Не знаю почему сишники так любят численные константы делать через #define. Да еще и без префиксов библиотеки. А потом эти дефайны везде лезут в неожиданных местах. Если портирую библиотеку и вижу кучу #define то сразу sed ом превращаю в enum. Ибо нафик. Сейчас есть острое желание сделать static const uint8_t UINT8_MAX_imp = UINT8_MAX; #undef UINT8_MAX; static const UINT8_MAX = UINT8_MAX_imp; и так для всех численных макросов стандартной библиотеки.

★★★★

так ты ж явно приводишь к указателю на неконстантный тип

Harald ★★★★★
()

Преобразование типов это такая штука, что ты должен осознавать на 100%, что делаешь. Даже в безопасной джаве словишь exception, если кастанешь несовместимые типы. В си исключений нет, там есть сегфолты, но это местная специфика.

Не кастуй const в не-const, если не делаешь какую-то темную магию (а в 99% случаев её лучше не делать).

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

Так у него эта константа просто в RO секции лежит.

anonymous
()

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf

7.1.6.1/4

any attempt to modify a const object during its lifetime results in undefined behavior.

Ну ты понял.

P.S. Блин, у меня есть ощущение, что на половину вопросов про странные штуки в C/C++ можно ответить ссылкой на стандарт, где почти наверняка будет упоминаться undefined behaviour.

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

Про -Wcast-qual выше написали. Но я соглашусь, выглядит дико что в -Wall оно не входит.

UPD: в GCC был такой тикет лет 15 назад, но его завернули.

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31573

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

Я причем помню что несколько лет назад оно входило в какой-то набор, а сейчас даже в педантике похоже нет. Возможно это было в шланге.

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

В clang оно кстати и сейчас входит в -Weverything, которого в gcc нет.

ТС, собирай шлангом для этого. -Weverything -Werror спасёт тебя!

$ clang weird.c -o weird -Weverything                       
weird.c:5:11: warning: cast from 'const unsigned int *' to 'int *' drops const qualifier [-Wcast-qual]
        *((int *)&a0) = 102;
                 ^
1 warning generated.
hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от anonymous

Хотя педантик это не о варнингах же, чот загнался.

anonymous
()

Как бы ок. Но меня возмущает что гцц игнорирует подобное.

Ты ему сказал - (int *) С чего бы ему тебе не верить, если ты в этом так уверен?

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

Константу в класс и & переопределяй или наоборот, короче чтоб не умел компилятор вот это твое (int *)&

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

Самое интересное - что компилятор попытался не дать изменить значение и можно было бы обойтись без Segmentation fault. Тогда код просто вхолостую бы отрабатывал.

$gcc -S weirdConst.c -o weirdConst.s
$cat weirdConst.s
	.file	"weirdConst.c"
	.text
	.section	.rodata
	.align 4
	.type	a0, @object
	.size	a0, 4
a0:
	.zero	4
.LC0:
	.string	"%u\n"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	leaq	a0(%rip), %rax
	movl	$102, (%rax)
	movl	$0, %eax
	movl	%eax, %esi
	leaq	.LC0(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Debian 8.3.0-6) 8.3.0"
	.section	.note.GNU-stack,"",@progbits

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

$gcc weirdConst.s -o weirdConst
$./weirdConst
0

Видно, что отрабатывается засылка значени 102 movl $102, (%rax) но затем она уничтожается movl $0, %eax 0 - это константное значение

praseodim ★★★★★
()
Последнее исправление: praseodim (всего исправлений: 2)

Вы когда себе палец в тисках хотите зажать, рассчитываете что их винт перестанет вращаться чтоб не допустить увечие? Или молоток волшебным образом остановится в воздухе, хоть вы и сами хотели попробовать ударить себя по другому пальцу?

anonymous
()

Если портирую библиотеку и вижу кучу #define то сразу sed ом превращаю в enum.

Зачем? Погодите, вы изучаете язык C и ещё не уяснили суть слов const, define и enum или так и делаете и будете делать дальше?

anonymous
()

то что абстракции текут - так со старины повелось

НО!

почто ТЫ дырявишь абстракции - скорость погружения не устраивала?

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

Он же спрашивает не что это, а какого хрена

так-то тс вообще ничего не спрашивает.

anonymous
()

#define можете даже не упоминать. Не знаю почему сишники так любят численные константы делать через #define. Да еще и без префиксов библиотеки. А потом эти дефайны везде лезут в неожиданных местах. Если портирую библиотеку и вижу кучу #define то сразу sed ом превращаю в enum. Ибо нафик. Сейчас есть острое желание сделать static const uint8_t UINT8_MAX_imp = UINT8_MAX; #undef UINT8_MAX; static const UINT8_MAX = UINT8_MAX_imp; и так для всех численных макросов стандартной библиотеки.

Какой-то ерундой занимаешься, enum будет ни чуть не лучше дефайна. Похоже наслушался каких-то страшилок про дефайны и кодишь теперь в состоянии аффекта. Я не буду утверждать, что -Wall не должна замечать такой каст, скорее даже соглашусь с тем, что замечать надо. Ну и так, к слову - каст указателя на константные данные с последующей модификаций данных полностью законный, если в этот указатель пришел адрес неконстантных данных.

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

Кстати, про -Wcast-qual читали? Там не без ложки дегтя:

Also warn when making a cast that introduces a type qualifier in an unsafe way.  For example, casting "char **" to "const char **" is unsafe, as in this example:

  /* p is char ** value.  */
  const char **q = (const char **) p;
  /* Assignment of readonly string to const char * is OK.  */
  *q = "string";
  /* Now char** pointer points to read-only memory.  */
  **p = 'b';

Ну как бы такое … . Теперь будет куча мусорных ворингов вдобавок к полезным.

UPD: хотя не, оно и так не даёт подобные касты делать, зачем такую заметку сделали - не совсем понятно.

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

Используй касты из c++. Они длинные и тебе будет больно говнокодить.

Про sed и макросы вообще абзац. Кто тебя подпустил к компилятору?

ox55ff ★★★★★
()

Стреляю себе в ногу и получается

Ну так не пиши на С. Выбери язык который не дает так стрелять. Или используй статические анализаторы

Aswed ★★★★★
()

Если портирую библиотеку и вижу кучу #define то сразу sed ом превращаю в enum. Ибо нафик.

И можешь поймать очень интересные ошибки. Энамы принимают свои значения на этапе компиляции, следовательно их нельзя использовать в препроцессоре (#if#ifdef)

Сравни и подумай, какой вариант алгоритма будет активирован с дефайнами.

#define LIB_FEATURE_NO_OPTIMIZE 0
#define LIB_FEATURE_OPTIMIZE_SPEED 1
#define LIB_FEATURE_OPTIMIZE_MEMORY 2

#define LIB_FEATURE_MODE LIB_FEATURE_OPTIMIZE_MEMORY

...

#if LIB_FEATURE_MODE == LIB_FEATURE_NO_OPTIMIZE
// no-opt code for debugging
#elif LIB_FEATURE_MODE == LIB_FEATURE_OPTIMIZE_SPEED
// speed-optimized version
#elif LIB_FEATURE_MODE == LIB_FEATURE_OPTIMIZE_MEMORY
// mem optimized version
#endif

И с энамами

enum LIB_FEATURE_OPT_MODE {
    LIB_FEATURE_NO_OPTIMIZE = 0,
    LIB_FEATURE_OPTIMIZE_SPEED = 1,
    LIB_FEATURE_OPTIMIZE_MEMORY = 2
};

#define LIB_FEATURE_MODE LIB_FEATURE_OPTIMIZE_MEMORY

...

#if LIB_FEATURE_MODE == LIB_FEATURE_NO_OPTIMIZE
// no-opt code for debugging
#elif LIB_FEATURE_MODE == LIB_FEATURE_OPTIMIZE_SPEED
// speed-optimized version
#elif LIB_FEATURE_MODE == LIB_FEATURE_OPTIMIZE_MEMORY
// mem optimized version
#endif
Tweaker ★★★★☆
()
Последнее исправление: Tweaker (всего исправлений: 2)
Ответ на: комментарий от bga_

Ну обычный косяк сишечки. 20 лет уж как исправлено через static_cast/const_cast, но если не пользоваться, то они не помогут.

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

Вот что у меня было ИРЛ

Библиотека

#define  GPIOA_BaseAddress 0x5000
#define  GPIOA ((__IO GPIO_TypeDef*)GPIOA_BaseAddress)

Я пишу макрос

#define BGA__CONCAT(a, b) BGA__CONCAT_IMPL(a, b)
#define BGA__CONCAT_IMPL(a, b) a ## b

#define BGA__CONCAT3(a, b, c) BGA__CONCAT(BGA__CONCAT(a, b), c)
...
#define BGA__MCU_HAL_PIN_INNERJOIN_DETAILS_BASE__GEN_portX(portLetterArg) enum { \
	BGA__CONCAT3(port, portLetterArg, _odr_set) = 0,  \
	...
	BGA__CONCAT3(port, portLetterArg, _cr2_reset) = 0, \
};

BGA__MCU_HAL_PIN_INNERJOIN_DETAILS_BASE__GEN_portX(A)
BGA__MCU_HAL_PIN_INNERJOIN_DETAILS_BASE__GEN_portX(B)

Долго понять не могу что оно мне рожает ((__IO GPIO_TypeDef*)0x5000)_odr_set Нашел эти макросы GPIOA поменял на

enum MAP_FILE_Base_Addresses {
	OPT_BaseAddress = 0x4800,
	GPIOA_BaseAddress = 0x5000,
	GPIOB_BaseAddress = 0x5005,
  ...
};
...
static __IO GPIO_TypeDef* const GPIOA = ((GPIO_TypeDef*) GPIOA_BaseAddress);

Теперь ошибки нет. Не то что бы я боялся этих #define как огня. Просто если есть возможность использовать нормальные константы компилятора (которые можно запереть в namespace, что я и делаю) то зачем заниматься подстановкой теста? А #define оставить для более сложных вещей.

bga_ ★★★★
() автор топика

для всех численных макросов стандартной библиотеки.

Это тупость.

Не знаю почему сишники так любят численные константы делать через #define

Потому что это действительно на выходе константа

enum

Оно тебя не спасёт ни разу ведь ты не пишешь bla.VALUE, а просто фигачишь VALUE.

anonymous
()

Но меня возмущает что гцц игнорирует подобное.

И почему возмущает? Ты делаешь явное приведение считай говоришь «конпилятор не ори я знаю что делаю я приводу типы сам руками» ибо есть ситуации где такое валидно, например если ты собрал гцц на новой архитектуре или для нового формата исполняемого где всё меяемое или ещё что. Ты ЯВНО приводишь, тем самым затыкаешь рот компилятору про всё что касается типов в этом контексте.

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

Из поста выше

#define GPIOA_BaseAddress 0x5000 -> enum { GPIOA_BaseAddress = 0x5000, }

#define GPIOA ((__IO GPIO_TypeDef*)GPIOA_BaseAddress) -> static __IO GPIO_TypeDef* const GPIOA = ((GPIO_TypeDef*) GPIOA_BaseAddress);

Для макро функций

#define MAX(a, b) ((a) < (b) ? (b) : (a))

->

template<typename AArg, typename BArg> AArg MAX(const AArg& a, const BArg& b) {
	return (a < b) ? b : a;
}
bga_ ★★★★
() автор топика
Ответ на: комментарий от anonymous

Имхо шаблоны + `constexpr` практически полностью заменяют макро костыли. Но ждать пришлось 22 года

bga_ ★★★★
() автор топика

Если хочешь чтобы конпелятор бил по рукам, то надо идти в rust. Я слушал там по рукам сильно бьют.

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

https://cmake.org/cmake/help/latest/command/add_compile_definitions.html


set(LIB_FEATURE_MODE "NO_OPTIMIZE" CACHE STRING "Library compilation mode")
string(TOUPPER ${LIB_FEATURE_MODE} LIB_FEATURE_MODE )

if (LIB_FEATURE_MODE STREQUAL "NO_OPTIMIZE")
	add_compile_definitions(LIB_FEATURE_NO_OPTIMIZE)
elseif (LIB_FEATURE_MODE STREQUAL "OPTIMIZE_SPEED")
	add_compile_definitions(LIB_FEATURE_OPTIMIZE_SPEED)
elseif (LIB_FEATURE_MODE STREQUAL "OPTIMIZE_MEMORY")
	add_compile_definitions(LIB_FEATURE_OPTIMIZE_MEMORY)
else (LIB_FEATURE_MODE STREQUAL "NO_OPTIMIZE")
	message(FATAL_ERROR "Please specify correct compilation mode")
endif (LIB_FEATURE_MODE STREQUAL "NO_OPTIMIZE")
#if defined(LIB_FEATURE_NO_OPTIMIZE)
// no-opt code for debugging
#elif defined(LIB_FEATURE_OPTIMIZE_SPEED)
// speed-optimized version
#elif defined(LIB_FEATURE_OPTIMIZE_MEMORY)
// mem optimized version
#endif
bhfq ★★★★★
()
Ответ на: комментарий от bhfq

Это понятно. Но это был пример возможных ошибок к которым может привести «замена дефайнов энамами седом» в чужой либе.

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

Такие define я оставляю конечно. Только префикс библиотеки приписываю если его нет. Но в основной своей массе это именно численные константы портов, команд и прочие биты.

bga_ ★★★★
() автор топика

Стреляю … и получается

Это хорошо или плохо?

anonymous
()

Как бы ок. Но меня возмущает что гцц игнорирует подобное.

А чего ему волноваться? Вы же сами сказали «компилятор, считай что это int», он и ведет себя соответственно.

На мк вообще нету const секции и там константа действительно изменяется.

Что ж это за МК такие странные?

COKPOWEHEU
()

static const unsigned a0 = 0;

Изучаю тоже Си уже нсколько месяцев. Скажите зачем вы указываете static? Тип линковки? У вас один файл.

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

Я только начинаю еще. Есть чему поучиться у местных маэстро по написанию кода в стиле

static const unsigned a0 = 0

Я аплодирую стоя

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

сколько смотрю лор, количество таких придурков, как ты, не убывает

ТС - ССЗБ, но вышесказанного это не отменяет

anonymous
()

Задолбали с UB. Кто еще раз скажет про UB тот мудак.

Если ты пишешь на Си, ты должен знать че ты и зачем делаешь. И сегфолты итд, там могут случиться вообще без указания компилятора на твои ошибки, это не хаскель тебе, и даже не жаба.

Твой a0 - кладется в бинарнике компилятором в секцию RO, соответственно, когда ОС поднимает бинарник, она эту RO область помещает в страницы, которые запрещены на запись. Вот тебе и сегфолт при попытке переписать. Не люблю термин сегфолт, впрочем, он устаревший и не описывает сути явления. На винде куда более корректно все называется - ACCESS_VIOLATION.

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

Потому что Си это не жабка тебе, где все должно давать тебе по рукам. Это ассемблер такой практически. И соответственно ты там можешь ковырять память как хочешь. Без этого на нем невозможно было бы писать ядра ОС, например.

Отсюда вывод - если ты пишешь не ядра ОС, не загрузчики ОС, итп, то и не лезь в Си.

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