繁体   English   中英

C格式说明符大小

[英]C Format Specifier Sizes

如何访问字符数组中的整数值?

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);`

在回答您的原始问题“ "%d"需要多少个字符?“时,可以通过将缓冲区指定为NULL并将字符数指定为0 ,然后使用"%d"格式对snprintf使用技巧字符串和变量bsnprintf将返回转换所需的位数,例如

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

它将输出"required digits: 2" “如果有足够的空间,将被写入最终字符串的字符数(不包括终止的空字节)。” )在为buffer动态分配存储空间时,这很有用。 实际上,您只需要提供完整格式的字符串 ,所有变量和snprintf都将返回所需的字符总数(向其中添加+1以表示无终止字符)

从最后的几句话中,我认为您想将90buffer读回一个int 这很简单。

对于一般情况,您不只是尝试使用buffer的第二个字符(例如, buffer[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++;

找到第一个数字后,即可使用strtol (可提供完整的错误检查)将数字序列转换为long值,例如

#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的返回值,该返回值将包含对从p开始(成功时)在buffer找到的long数字的转换。 但是,在使用返回为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);
    }

将您的示例放在一起(并包括使用snprintf验证对buffer的原始写入,可以执行以下操作):

#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

仔细检查一下,如果您还有其他问题,请告诉我。

有一个%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)

在您的示例中,假设您要查询的字符数组为output_buffer,并且在体系结构中char的大小为1个字节,则%d将占用2个字节,即int的每一位(b = 90)。 要获取该值,请使用:

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

请检查缓冲区的大小,以避免溢出

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM