[英]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
使用技巧字符串和变量b
, snprintf
将返回转换所需的位数,例如
req = snprintf (NULL, 0, "%d", b);
printf ("required digits: %d\n", req);
它将输出"required digits: 2"
。 ( “如果有足够的空间,将被写入最终字符串的字符数(不包括终止的空字节)。” )在为buffer
动态分配存储空间时,这很有用。 实际上,您只需要提供完整格式的字符串 ,所有变量和snprintf
都将返回所需的字符总数(向其中添加+1
以表示无终止字符)
从最后的几句话中,我认为您想将90
从buffer
读回一个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.