В файл application.properties
добавьте
server.port=1089
, вы также можете настроить порт программно
@Configuration
public class ServletConfig {
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return (container -> {
container.setPort(1089);
});
}
}
или в качестве параметра командной строки:
-Dserver.port=1089
Обработка ошибок является одной из редких ситуаций когда goto
не так плохо.
Но если бы я должен был поддержать тот код, то я был бы очень расстроен это goto
скрыты макросами.
Так в этом случае goto
хорошо для меня, но не макросов.
Этот код:
void func()
{
char* p1 = malloc(16);
if( !p1 )
goto cleanup;
char* p2 = malloc(16);
if( !p2 )
goto cleanup;
cleanup:
if( p1 )
free(p1);
if( p2 )
free(p2);
}
может быть по закону записан как:
void func()
{
char* p1 = malloc(16);
char* p2 = malloc(16);
free(p1);
free(p2);
}
успешно выполняются ли выделения памяти.
Это работает, потому что свободный () ничего не делает, если передано Нулевой указатель. Можно использовать ту же идиому при разработке собственных API, чтобы выделить и освободить другие ресурсы:
// return handle to new Foo resource, or 0 if allocation failed
FOO_HANDLE AllocFoo();
// release Foo indicated by handle, - do nothing if handle is 0
void ReleaseFoo( FOO_HANDLE h );
Разработка API как это может значительно упростить управление ресурсами.
Используя goto
перейти к последовательности обработчика/очистки/выхода распространенных ошибок прекрасно абсолютно.
Очистка с goto
общая идиома C и используется в Linux kernel*.
** Возможно, мнение Linus не является лучшим примером хорошего аргумента, но это действительно показывает goto
будучи используемым в относительно крупномасштабном проекте.*
Если первый malloc приводит Вас к сбою затем очистка и p1 и p2. Из-за goto, p2 не инициализируется и может указать на что-либо. Я работал, это быстро с gcc для проверки и пытающийся к свободному (p2) действительно вызовет отказ seg.
В Вашем последнем примере переменные ограничены по объему в фигурных скобках (т.е. они только существуют в блоке FAIL_SECTION_BEGIN).
Принятие кода работает без фигурных скобок, необходимо было бы все еще инициализировать все указатели на ПУСТОЙ УКАЗАТЕЛЬ перед FAIL_SECTION_BEGIN для предотвращения сбоя seg.
У меня ничего нет против goto и макросов, но я предпочитаю идею Neil Butterworth..
void func(void)
{
void *p1 = malloc(16);
void *p2 = malloc(16);
void *p3 = malloc(16);
if (!p1 || !p2 || !p3) goto cleanup;
/* ... */
cleanup:
if (p1) free(p1);
if (p2) free(p2);
if (p3) free(p3);
}
Или если это является более соответствующим..
void func(void)
{
void *p1 = NULL;
void *p2 = NULL;
void *p3 = NULL;
p1 = malloc(16);
if (!p1) goto cleanup;
p2 = malloc(16);
if (!p2) goto cleanup;
p3 = malloc(16);
if (!p3) goto cleanup;
/* ... */
cleanup:
if (p1) free(p1);
if (p2) free(p2);
if (p3) free(p3);
}
Термин "Структурное программирование", которое все мы знаем как anti-goto вещь, первоначально запущенную и разработанную как набор кодирования шаблонов с goto's (или JMP's). Те шаблоны назвали while
и if
шаблоны, среди других.
Так, при использовании goto's используйте их структурированным способом. Это ограничивает повреждение. И они макрос кажутся разумным подходом.
Первый пример выглядит намного более читаемым мне, чем macroised версия. И mouviciel сказал это намного лучше, чем я
Исходный код извлек бы выгоду из использования нескольких операторов возврата - нет никакой потребности скачкообразно двинуться вокруг ошибочного возврата, очищают код. Плюс, Вам обычно нужно выделенное пространство, освобожденное по обычному возврату также - иначе Вы пропускаете память. И можно переписать пример без goto
если Вы осторожны. Это - случай, где можно полезно объявить переменные прежде в других отношениях необходимый:
void func()
{
char *p1 = 0;
char *p2 = 0;
char *p3 = 0;
if ((p1 = malloc(16)) != 0 &&
(p2 = malloc(16)) != 0 &&
(p3 = malloc(16)) != 0)
{
// Use p1, p2, p3 ...
}
free(p1);
free(p2);
free(p3);
}
Когда существуют нетривиальные объемы работы после каждой операции выделения, затем можно использовать маркировку перед первый из free()
операции и a goto
в порядке - обработка ошибок является главной причиной для использования goto
в эти дни, и что-либо очень еще несколько сомнительно.
Я забочусь о некотором коде, который действительно имеет макросы со встроенными операторами перехода. Это сбивает с толку на первом обнаружении для наблюдения маркировки, которая является 'не имеющей ссылки' видимым кодом, все же который не может быть удален. Я предпочитаю избегать таких методов. Макросы в порядке, когда я не должен знать то, что они делают - они просто делают это. Макросы не так в порядке, когда необходимо знать то, до чего они расширяются использовать их точно. Если они не скрывают информацию от меня, они - больше неприятности, чем справка.
Иллюстрация - имена, замаскированные для защиты виновного:
#define rerrcheck if (currval != &localval && globvar->currtub && \
globvar->currtub->te_flags & TE_ABORT) \
{ if (globvar->currtub->te_state) \
globvar->currtub->te_state->ts_flags |= TS_FAILED;\
else \
delete_tub_name(globvar->currtub->te_name); \
goto failure; \
}
#define rgetunsigned(b) {if (_iincnt>=2) \
{_iinptr+=2;_iincnt-=2;b = ldunsigned(_iinptr-2);} \
else {b = _igetunsigned(); rerrcheck}}
Существует несколько дюжин вариантов на rgetunsigned()
это несколько подобно - различные размеры и различные функции загрузчика.
Одно место, где они используются, содержит этот цикл - в большем блоке кода в единственном случае большого переключателя с некоторыми маленькими и некоторых больших блоках кода (не особенно хорошо структурированный):
for (i = 0 ; i < no_of_rows; i++)
{
row_t *tmprow = &val->v_coll.cl_typeinfo->clt_rows[i];
rgetint(tmprow->seqno);
rgetint(tmprow->level_no);
rgetint(tmprow->parent_no);
rgetint(tmprow->fieldnmlen);
rgetpbuf(tmprow->fieldname, IDENTSIZE);
rgetint(tmprow->field_no);
rgetint(tmprow->type);
rgetint(tmprow->length);
rgetlong(tmprow->xid);
rgetint(tmprow->flags);
rgetint(tmprow->xtype_nm_len);
rgetpbuf(tmprow->xtype_name, IDENTSIZE);
rgetint(tmprow->xtype_owner_len);
rgetpbuf(tmprow->xtype_owner_name, IDENTSIZE);
rgetpbuf(tmprow->xtype_owner_name,
tmprow->xtype_owner_len);
rgetint(tmprow->alignment);
rgetlong(tmprow->sourcetype);
}
Не очевидно, что код там пропитан операторами перехода! И ясно, полное толкование грехов кода, из которого это прибывает, весь день брало бы - они - многие и варьировались.
#define malloc_or_die(size) if(malloc(size) == NULL) exit(1)
Это не похоже на то, что вы действительно можете восстановиться после сбоя malloc, если у вас нет программного обеспечения, достойного написания системы транзакций, если вы добавите код отката в malloc_or_die.
Для реального примера Правильное использование goto, проверьте код отправки, который использует вычисленное goto.