Проблема, о которой спрашивают о
После того, как символ |
обнаружен с директивой scanset %[^|]
, sscanf()
возобновит совпадение с символом |
.Следующая директива должна быть литералом |
, чтобы избежать совпадения.В исходном коде с %[^|]s
s
является , а не частью директивы scanset, и вместо этого sscanf()
ищет совпадение с литералом s
на входе.Кроме того, обратите внимание, что спецификаторы максимальной ширины всегда должны использоваться с директивами семейства %s
и %[]
fscanf()
, чтобы избежать переполнения буфера с помощью злонамеренных или искаженных вводов:
"%d=%39[^|]|%39[^=]=%39[^|]|%39s"
Некоторые другие серьезные проблемы
Всегда включать предупреждения при компиляции кода C;это помогло бы вам избежать нескольких серьезных проблем.Для этого кода есть много предупреждений, и большинство проблем, перечисленных ниже, приводит к неопределенному поведению.Я всегда использую по крайней мере gcc -std=c11 -Wall -Wextra -Wpedantic
, и я добавил пример вывода gcc для исходного кода в конце ответа.
В опубликованном коде отсутствует #include <string.h>
для memset()
.
Поля .title
и .extra
в lcdInput
должны быть unsigned char *
, поскольку они указывают на первые элементы unsigned char
массивов.
При инициализации customInput
&
операторы должны быть удалены.customInput.title
и customInput.extra
ожидают указателей на unsigned char
(или char
с до вышеуказанной коррекции).Например, с &customInputTitle[0]
у вас есть указатель на массив CUSTOM_INPUT_LENGTH
unsigned char
с (или char
с до вышеуказанной коррекции);это несоответствие типов, и ваш компилятор должен громко жаловаться (с включенными предупреждениями).Вместо этого просто используйте:
static lcdInput customInput = {
.title = {customInputTitle[0], customInputTitle[1]},
.extra = {customInputExtra[0], customInputExtra[1]},
.type = INPUT_NORMAL,
.editable = 1,
.forceLen = 0,
};
Здесь customInputTitle[0]
- это массив CUSTOM_INPUT_LENGTH
unsigned char
s, который будет уменьшаться до указателя на его первый элемент (unsigned char *
).В качестве альтернативы вы можете использовать &customInputTitle[0][0]
, &customInputTitle[1][0]
и т. Д.
Аналогично вам необходимо удалить амперсанды из массивов customInput
в вызове на sscanf()
.Здесь вам также нужно что-то сделать с &customInput.type
.Это тип enum
, и вы не можете ввести значение enum
.Опять же, компилятор жалуется с включенными предупреждениями.Вместо этого попробуйте:
int typeInput;
if (sscanf(payload, CUSTOM_INPUT_REGX,
&typeInput,
customInputTitle[0], customInputTitle[1],
customInputExtra[0], customInputExtra[1]) == 5) {
if (typeInput >= INPUT_NUMBER && typeInput <= INPUT_PAYAMOUNT) {
customInput.type = typeInput;
} else {
/* Handle error */
}
};
Здесь typeInput
используется для сбора ввода, значение, возвращаемое sscanf()
, проверяется, чтобы убедиться, что было назначено правильное количество значений, и значение typeInput
проверяется по диапазону значений для inputType
.Если ввод соответствует ожидаемому, typeInput
назначается на customInput.type
.
Вызовы на memset()
будут работать, но зачем запутывать вещи битовыми сдвигами?Здесь вам не нужны операторы &
, но в этом случае они в порядке.Вместо этого рассмотрим:
memset(customInputTitle, 0, sizeof customInputTitle);
memset(customInputExtra, 0, sizeof customInputExtra);
Вот исправленный код.Компилируется без предупреждений, используя gcc -std=c11 -Wall -Wextra -Wpedantic
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // missing header
typedef enum {
INPUT_NUMBER = 0,
INPUT_NORMAL = 1,
INPUT_PASSWORD = 2,
INPUT_PAYAMOUNT = 3,
} inputType;
typedef struct {
unsigned char * title[2]; // need unsigned char
unsigned char * extra[2];
inputType type;
unsigned minLen:6;
unsigned maxLen:6;
unsigned forceLen:1;
unsigned editable:1;
unsigned char data[100];
} lcdInput;
#define CUSTOM_INPUT_LENGTH 40
static unsigned char customInputTitle[2][CUSTOM_INPUT_LENGTH];
static unsigned char customInputExtra[2][CUSTOM_INPUT_LENGTH];
const char * payload = "1=Salam Khobi|FC93F8A120F491F3A8=Rial|F191FEA4";
// bad format string
#define CUSTOM_INPUT_REGX "%d=%39[^|]|%39[^=]=%39[^|]|%39s"
// & operator not needed
static lcdInput customInput = {
.title = {customInputTitle[0], customInputTitle[1]},
.extra = {customInputExtra[0], customInputExtra[1]},
.type = INPUT_NORMAL,
.editable = 1,
.forceLen = 0,
};
int main(void)
{
// could use improvements
memset(customInputTitle, 0, sizeof customInputTitle);
memset(customInputExtra, 0, sizeof customInputExtra);
// & operators not needed
int typeInput;
if (sscanf(payload, CUSTOM_INPUT_REGX,
&typeInput,
customInputTitle[0], customInputTitle[1],
customInputExtra[0], customInputExtra[1]) == 5) {
if (typeInput >= INPUT_NUMBER && typeInput <= INPUT_PAYAMOUNT) {
customInput.type = typeInput;
} else {
/* Handle error */
}
};
return 0;
}
Вывод GCC с предупреждениями
Вот предупреждения компилятора с gcc -std=c11 -Wall -Wextra -Wpedantic
из исходной программы, опубликованной в вопросе:
bad_program.c:27:19: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
.title = {&customInputTitle[0], &customInputTitle[1]},
^
bad_program.c:27:19: note: (near initialization for ‘customInput.title[0]’)
bad_program.c:27:41: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
.title = {&customInputTitle[0], &customInputTitle[1]},
^
bad_program.c:27:41: note: (near initialization for ‘customInput.title[1]’)
bad_program.c:28:19: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
.extra = {&customInputExtra[0], &customInputExtra[1]},
^
bad_program.c:28:19: note: (near initialization for ‘customInput.extra[0]’)
bad_program.c:28:41: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
.extra = {&customInputExtra[0], &customInputExtra[1]},
^
bad_program.c:28:41: note: (near initialization for ‘customInput.extra[1]’)
bad_program.c: In function ‘main’:
bad_program.c:36:5: warning: implicit declaration of function ‘memset’ [-Wimplicit-function-declaration]
memset(&customInputTitle, 0, CUSTOM_INPUT_LENGTH << 1);
^~~~~~
bad_program.c:36:5: warning: incompatible implicit declaration of built-in function ‘memset’
bad_program.c:36:5: note: include ‘<string.h>’ or provide a declaration of ‘memset’
bad_program.c:25:33: warning: format ‘%d’ expects argument of type ‘int *’, but argument 3 has type ‘inputType * {aka enum <anonymous> *}’ [-Wformat=]
#define CUSTOM_INPUT_REGX "%d=%[^|]s|%[^=]s=%[^|]s|%s"
^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
sscanf(payload, CUSTOM_INPUT_REGX,
^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%[^|’ expects argument of type ‘char *’, but argument 4 has type ‘unsigned char (*)[40]’ [-Wformat=]
#define CUSTOM_INPUT_REGX "%d=%[^|]s|%[^=]s=%[^|]s|%s"
^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
sscanf(payload, CUSTOM_INPUT_REGX,
^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%[^=’ expects argument of type ‘char *’, but argument 5 has type ‘unsigned char (*)[40]’ [-Wformat=]
#define CUSTOM_INPUT_REGX "%d=%[^|]s|%[^=]s=%[^|]s|%s"
^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
sscanf(payload, CUSTOM_INPUT_REGX,
^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%[^|’ expects argument of type ‘char *’, but argument 6 has type ‘unsigned char (*)[40]’ [-Wformat=]
#define CUSTOM_INPUT_REGX "%d=%[^|]s|%[^=]s=%[^|]s|%s"
^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
sscanf(payload, CUSTOM_INPUT_REGX,
^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%s’ expects argument of type ‘char *’, but argument 7 has type ‘unsigned char (*)[40]’ [-Wformat=]
#define CUSTOM_INPUT_REGX "%d=%[^|]s|%[^=]s=%[^|]s|%s"
^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
sscanf(payload, CUSTOM_INPUT_REGX,
^~~~~~~~~~~~~~~~~