Размеры спецификатора формата C - PullRequest
0 голосов
/ 09 января 2019

Как получить доступ к целочисленному значению в массиве символов?

char a = 'A';
int b = 90;
char * c = "A String";
snprintf(output_buffer, 1024, "%c%d%s, a,b,c);`

Сколько байтов займет спецификатор формата %d (я полагаю, 4?) В этом массиве символов и как мне получить доступ к целочисленному значению b?

Я пробовал:

int x = *((int *) &output_buffer[1]

без успеха.

short x; 
sscanf(&output_buffer[1], "%d", &x);
printf("%d\n",x);`

char *buffer; 
sscanf(&output_buffer[3], "%s", buffer);
printf("%s\n",buffer);`

Ответы [ 3 ]

0 голосов
/ 09 января 2019

Существует модификатор% n, который сохраняет фактическое количество записанных байтов в int:

int main(int argc, char *argv[])
{
    int p0;
    int p1;
    char    buf[128];

    sprintf(buf, "%c%n%d%n%s", argv[0][0], &p0, atoi(argv[1]), &p1, argv[1]);
    printf("'%s' -> %d, %d\n", buf, p0, p1);
}

Но этот модификатор считается опасным; в некоторых реализациях требуется, чтобы строка формата находилась в постоянной памяти.

Чтобы сделать последнее предложение более понятным, пример:

#include <stdio.h>

int main(void)
{
        char    fmt[] = "%n";
        int     pos;

        printf("%n", &pos);
        printf("ok\n");

        printf(fmt, &pos);
        printf("ok\n");
}

, а затем

$ gcc x.c -D_FORTIFY_SOURCE=2 -O2
$ ./a.out 
ok
*** %n in writable segment detected ***
Aborted (core dumped)
0 голосов
/ 09 января 2019

В ответ на ваш первоначальный вопрос «сколько символов займет "%d"?», Вы можете использовать трюк с snprintf, указав буфер как NULL и количество символов как 0, и затем, используя строку формата "%d" и переменную b, snprintf вернет количество цифр, которое потребуется для преобразования, например

    req = snprintf (NULL, 0, "%d", b);
    printf ("required digits: %d\n", req);

Который выдаст "required digits: 2". ( "количество символов (исключая завершающий нулевой байт), которое было бы записано в конечную строку, если бы было достаточно места." ) Что полезно при динамическом выделении памяти для buffer. Фактически, вы просто предоставляете свою полную строку формата и все переменные, и snprintf вернет общее количество необходимых символов (к которому вы добавите +1 для nul-terminating символы)

Из последних нескольких комментариев, я так понимаю, вы хотите прочитать 90 обратно в int из buffer. Это достаточно просто сделать.

Вместо того, чтобы просто пытаться преобразовать, используя 2-й символ в buffer (например, buffer[1]), для общего случая вы просто хотите начать с 1-го символа в буфере и сканировать вперед, пока не найдете первую цифру , (Вы также можете проверить '+/-', если у вас есть явные значения со знаком).

Чтобы просмотреть вперед в buffer, чтобы найти первую цифру, вы перебираете символы в buffer (либо используя индексы, например, buffer[x], либо используя указатель, например, char *p = buffer; и увеличивая p++ ) и проверьте, является ли каждый символ цифрой. Хотя вы можете просто использовать if ('0' <= *p && *p <= '9'), заголовок ctype.h предоставляет макрос isdigit(), который делает это довольно легко. Чтобы найти первую цифру, вы можете сделать что-то вроде:

#include <ctype.h>  /* for isdigit */
...
char buffer[MAXC] = "",  /* buffer to hold a, b, c */
    *p = buffer;
...
    while (*p && !isdigit(*p))  /* scan forward in buffer to 1st digit */
        p++;

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

#include <stdlib.h>
#include <errno.h>  /* for errno   */
#include <limits.h> /* for INT_MIN/INT_MAX */
...
    char *endptr;           /* end pointer to use with strtol */
    long tmp;               /* long value for return of strtol */
    ...
    errno = 0;                      /* reset errno - to check after strtol */
    tmp = strtol (p, &endptr, 0);   /* save conversion result in tmp */

( примечание: избегать atoi() - обеспечивает ноль проверка ошибок преобразования)

Теперь tmp содержит возврат strtol, который будет содержать преобразование в long цифр, найденных в buffer, начиная с p (в случае успеха). Но прежде чем вы сможете использовать значение, возвращаемое как int, вы должны проверить , что цифры были преобразованы, что при преобразовании не возникло ошибок и что значение в tmp находится в диапазоне int. Вы можете сделать все с помощью нескольких условных проверок. Если все ваши проверки выполнены, вы можете присвоить значение в tmp целому числу (с соответствующим приведением) и быть уверенным в своем конечном значении, например,

    if (p == endptr)                /* check if pointer == end pointer */
        fputs ("error: no digits converted.\n", stderr);
    else if (errno)             /* if errno set, over/underflow occurred */
        fputs ("error: invalid conversion to long.\n", stderr);
    else if (tmp < INT_MIN || INT_MAX < tmp)    /* will fit in int? */
        fputs ("error: value exceeds range of int.\n", stderr);
    else {      /* good value, assign to b_from_buf, output */
        b_from_buf = (int)tmp;
        printf ("\nint read from buffer: %d\n", b_from_buf);
    }

Соединяя ваш пример (включая проверку вашей первоначальной записи в buffer с помощью snprintf, вы могли бы сделать что-то похожее на следующее):

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>  /* for isdigit */
#include <errno.h>  /* for errno   */
#include <limits.h> /* for INT_MIN/INT_MAX */

#define MAXC 1024

int main (void) {

    char a = 'A',
        *c = "A String",
        buffer[MAXC] = "",  /* buffer to hold a, b, c */
        *p = buffer,        /* pointer to buffer */
        *endptr;            /* end pointer to use with strtol */
    int b = 90,
        b_from_buf,         /* int to read from filled buffer */
        rtn;                /* return for snprintf to validate */
    long tmp;               /* long value for return of strtol */

    rtn = snprintf (buffer, MAXC, "%c%d%s", a, b, c);
    if (rtn < 0) {  /* if < 0, error occurred */
        fputs ("error: writing to buffer.\n", stderr);
        return 1;
    }
    else if (rtn >= MAXC)   /* if > size, truncation occurred */
        fputs ("warning: buffer contains truncated string.\n", stderr);

    printf ("%s\n", buffer);    /* output buffer */

    while (*p && !isdigit(*p))  /* scan forward in buffer to 1st digit */
        p++;

    errno = 0;                      /* reset errno - to check after strtol */
    tmp = strtol (p, &endptr, 0);   /* save conversion result in tmp */
    if (p == endptr)                /* check if pointer == end pointer */
        fputs ("error: no digits converted.\n", stderr);
    else if (errno)             /* if errno set, over/underflow occurred */
        fputs ("error: invalid conversion to long.\n", stderr);
    else if (tmp < INT_MIN || INT_MAX < tmp)    /* will fit in int? */
        fputs ("error: value exceeds range of int.\n", stderr);
    else {      /* good value, assign to b_from_buf, output */
        b_from_buf = (int)tmp;
        printf ("\nint read from buffer: %d\n", b_from_buf);
    }
}

( примечание: если значение в buffer может иметь явный знак перед ним, например, '-' или '+', то вы добавляете их к тому же условному выражению с помощью isdigit())

Пример использования / Вывод

$ ./bin/snprintf_string
A90A String

int read from buffer: 90

После последнего комментария Желая 'a, b & c` Назад

У вас уже есть все, что нужно, чтобы получить a, b & c из буфера. Поскольку вы использовали strtol и endptr будут указывать на следующий символ после последней преобразованной цифры, вы можете получить a, b & c обратно из буфера, просто выводя значения, например,

    else {      /* good value, assign to b_from_buf, output */
        b_from_buf = (int)tmp;
        printf ("\n(a) 1st char in buffer        : %c\n"
                "(b) int read from buffer      : %d\n"
                "(c) remaining chars in buffer : %s\n", 
                *buffer, b_from_buf, endptr);
    }

Модифицированный пример Использование / Вывод

$ ./bin/snprintf_string
A90A String

(a) 1st char in buffer        : A
(b) int read from buffer      : 90
(c) remaining chars in buffer : A String

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 09 января 2019

В вашем примере, если предположить, что массив символов, о котором вы спрашиваете, это output_buffer, а размер char в вашей архитектуре равен 1 байту,% d займет 2 байта, по одному на каждую цифру вашего целого числа (b = 90) , Чтобы вернуть значение, используйте:

int x; 
sscanf(&output_buffer[1], "%d", &x);
char buffer[255]; 
sscanf(&output_buffer[3], "%[^\n]", buffer);

Пожалуйста, проверьте размер буфера, чтобы избежать переполнения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...