我可以将打印输出分配给变量吗?
Can I assign a printed output into a variable?
我正在弄清楚是否可以从打印输出中创建一个变量。例如
#include <stdio.h>
void main()
{
char n[4] = "2445";
int i;
for (i = 0; i <= 4; i++)
{
printf("%c", n[i] + 1);
}
}
输出是3556
我可以从输出中创建一个变量吗?
void main(void)
{
char n[4]="2445";
char result[5];
size_t i;
for(i = 0; i < sizeof(n); i++)
{
result[i] = n[i] + 1;
}
result[i] = 0;
printf("result = `%s`\n", result);
}
如果您只想输出单个字符,那么将数据放入 char
缓冲区,正如另一个答案所暗示的那样,是正确的答案。简单,快速,无非就是善良。
但是,如果您需要将不同类型格式化为字符串,并且希望将这些字符串保存在缓冲区中,那么您需要查看 sprintf
或更安全的 snprintf
。使用此函数(它是 C99 的一部分,因此现在应该随处可用),您可以将格式化的字符串写入缓冲区。
当然,如果你不小心,你可能会在缓冲区的边界之外写入,这就是为什么 snprintf
函数比 sprintf
更好,如果你重复写入缓冲区,您需要跟踪下一个数据应该写入的位置以及缓冲区的长度。前者很容易跟踪,因为 snprintf
returns 它写了多少(不包括零终止),所以你可以在每次写的时候用那个数量更新游标。
将 snprintf
与缓冲区一起使用,您的示例程序可能如下所示:
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
struct buf
{
size_t cursor, len;
char data[];
};
struct buf *new_buf(size_t len)
{
struct buf *buf = malloc(offsetof(struct buf, data) + len);
assert(buf); // handle allocation error
buf->cursor = 0;
buf->len = len;
return buf;
}
int main(void)
{
char n[] = "2445";
struct buf *buf = new_buf(sizeof n);
for (size_t i = 0; i < sizeof n; i++)
{
buf->cursor +=
snprintf(buf->data + buf->cursor, // where to write
buf->len - buf->cursor, // how much you can write (snprintf takes care of '[=10=]')
"%c", n[i] + 1); // what to write
}
printf("result = `%s`\n", buf->data);
free(buf);
return 0;
}
每次用 snprintf
写入后,buf->data + buf->cursor
指向 snprintf
放置零终端字节的位置,因此如果我们从该地址开始写入下一个数据,那么我们将添加一个下一个序列化后的数据值。如果您想要 spaces 或逗号或数据之间的任何其他内容,您可以修改格式字符串。
当然,使用该代码,如果 运行 超出缓冲区 space,就会出现问题。并不是说 snprintf
会越界写(就是 snprintf
中的 n
比 sprintf
做的更好),只是不会写所有的数据你给它。它只会写入缓冲区的末尾,不会再写了。
如果要确保获取所有数据,则必须在缓冲区填满时增大缓冲区。
我们很幸运,如果我们用零调用它作为最大长度(我们在上面使用 buf->len - buf->cursor
的地方),我们可以获得格式化字符串的大小。如果我们这样做,并且我们不需要提供缓冲区,那么我们就会得到我们需要的长度。所以,在我们写入缓冲区之前,我们可以请求必要的长度,然后我们可以扩展缓冲区以获得足够的长度 space.
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX(A, B) (((A) > (B)) ? (A) : (B))
struct buf
{
size_t cursor, len;
char data[];
};
struct buf *resize_buf(struct buf *buf, size_t len)
{
// don't allow len == 0; it will mess up doubling of lengths.
len = len ? len : 1;
struct buf *new_buffer = realloc(buf, offsetof(struct buf, data) + len);
assert(new_buffer);
// if it's a new buffer, set the cursor (otherwise it is already set)
new_buffer->cursor = buf ? new_buffer->cursor : 0;
new_buffer->len = len;
return new_buffer;
}
struct buf *new_buf(size_t len)
{
return resize_buf(0, len);
}
int main(void)
{
char n[] = "2445";
struct buf *buf = new_buf(0); // empty now, but we will resize
for (size_t i = 0; i < sizeof n; i++)
{
// I've changed the formatting string to %d, so now you get the ASCII
// codes plus one. They are no longer a single character long.
// the +1 after snprintf() is for the zero sentinel '[=11=]'
size_t needed = snprintf(0, 0, "%d ", n[i] + 1) + 1;
if (needed > buf->len - buf->cursor)
{
buf = resize_buf(buf, MAX(buf->len + needed, 2 * buf->len)); // double the length
}
buf->cursor +=
snprintf(buf->data + buf->cursor, // where to write
buf->len - buf->cursor, // how much you can write (snprintf takes care of '[=11=]')
"%d ", n[i] + 1); // what to write
}
printf("result = `%s`\n", buf->data);
free(buf);
return 0;
}
我将缓冲区增加两倍以获得线性时间增长,如果每次只以所需的大小增加缓冲区,它最终会以二次方 运行ning 时间结束。除此之外,没有什么复杂的;我们询问我们需要多少space,然后我们通过在需要时增加缓冲区来确保我们拥有它,然后我们像以前一样写入缓冲区。
不过,这并不是一个特别好的解决方案。我们绝对必须确保我们在第一次和第二次调用 snprintf
时创建的格式化字符串是相同的,否则我们真的会搞砸,缓冲区实现的细节从根本上暴露给用户。最好把它包在一个函数里。
如果我们想处理一般的格式化字符串,我们需要一个可变参数函数,如果你不习惯它们,它们可能看起来有点奇怪。但它们是这样工作的:你使用 ...
作为函数的最后一个参数,然后你可以使用 va_start()
获得指向附加参数的指针,你需要再次释放一些保存参数的数据结构va_end()
.
为了我们的目的,我们不需要对参数做任何特殊的事情,因为我们可以用它们调用 vsnprintf
。该函数等同于 snprintf
接受这些结构而不是参数。我认为 vsnprintf
来自 C99,所以你也应该拥有它,但我承认它可能直到 C11 才存在(但你真的应该也拥有它)。
我们需要调用vsnprintf
两次,我们需要设置参数并在每次调用前再次释放它们,但除此之外没有任何其他事情。
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX(A, B) (((A) > (B)) ? (A) : (B))
struct buf
{
size_t cursor, len;
char data[];
};
struct buf *resize_buf(struct buf *buf, size_t len)
{
// don't allow len == 0; it will mess up doubling of lengths.
len = len ? len : 1;
struct buf *new_buffer = realloc(buf, offsetof(struct buf, data) + len);
assert(new_buffer);
// if it's a new buffer, set the cursor (otherwise it is already set)
new_buffer->cursor = buf ? new_buffer->cursor : 0;
new_buffer->len = len;
return new_buffer;
}
struct buf *new_buf(size_t len)
{
return resize_buf(0, len);
}
struct buf *write_to_buf(struct buf *buf, const char *restrict fmt, ...)
{
va_list args;
va_start(args, fmt);
size_t needed = vsnprintf(0, 0, fmt, args);
va_end(args);
if (needed > buf->len - buf->cursor)
{
buf = resize_buf(buf, MAX(buf->len + needed, 2 * buf->len));
}
va_start(args, fmt);
buf->cursor +=
vsnprintf(buf->data + buf->cursor,
buf->len - buf->cursor,
fmt, args);
va_end(args);
return buf;
}
int main(void)
{
char n[] = "2445";
struct buf *buf = new_buf(0); // empty now, but we will resize
for (size_t i = 0; i < sizeof n; i++)
{
buf = write_to_buf(buf, "%d ", n[i] + 1);
}
printf("result = `%s`\n", buf->data);
free(buf);
return 0;
}
这应该会让您了解如何创建一个可以写入通用格式字符串的缓冲区。我并没有声称此代码是稳定的或正确的接口类型或其他任何东西,我只是很快地完成了它,但它应该是开始的地方。
我正在弄清楚是否可以从打印输出中创建一个变量。例如
#include <stdio.h>
void main()
{
char n[4] = "2445";
int i;
for (i = 0; i <= 4; i++)
{
printf("%c", n[i] + 1);
}
}
输出是3556
我可以从输出中创建一个变量吗?
void main(void)
{
char n[4]="2445";
char result[5];
size_t i;
for(i = 0; i < sizeof(n); i++)
{
result[i] = n[i] + 1;
}
result[i] = 0;
printf("result = `%s`\n", result);
}
如果您只想输出单个字符,那么将数据放入 char
缓冲区,正如另一个答案所暗示的那样,是正确的答案。简单,快速,无非就是善良。
但是,如果您需要将不同类型格式化为字符串,并且希望将这些字符串保存在缓冲区中,那么您需要查看 sprintf
或更安全的 snprintf
。使用此函数(它是 C99 的一部分,因此现在应该随处可用),您可以将格式化的字符串写入缓冲区。
当然,如果你不小心,你可能会在缓冲区的边界之外写入,这就是为什么 snprintf
函数比 sprintf
更好,如果你重复写入缓冲区,您需要跟踪下一个数据应该写入的位置以及缓冲区的长度。前者很容易跟踪,因为 snprintf
returns 它写了多少(不包括零终止),所以你可以在每次写的时候用那个数量更新游标。
将 snprintf
与缓冲区一起使用,您的示例程序可能如下所示:
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
struct buf
{
size_t cursor, len;
char data[];
};
struct buf *new_buf(size_t len)
{
struct buf *buf = malloc(offsetof(struct buf, data) + len);
assert(buf); // handle allocation error
buf->cursor = 0;
buf->len = len;
return buf;
}
int main(void)
{
char n[] = "2445";
struct buf *buf = new_buf(sizeof n);
for (size_t i = 0; i < sizeof n; i++)
{
buf->cursor +=
snprintf(buf->data + buf->cursor, // where to write
buf->len - buf->cursor, // how much you can write (snprintf takes care of '[=10=]')
"%c", n[i] + 1); // what to write
}
printf("result = `%s`\n", buf->data);
free(buf);
return 0;
}
每次用 snprintf
写入后,buf->data + buf->cursor
指向 snprintf
放置零终端字节的位置,因此如果我们从该地址开始写入下一个数据,那么我们将添加一个下一个序列化后的数据值。如果您想要 spaces 或逗号或数据之间的任何其他内容,您可以修改格式字符串。
当然,使用该代码,如果 运行 超出缓冲区 space,就会出现问题。并不是说 snprintf
会越界写(就是 snprintf
中的 n
比 sprintf
做的更好),只是不会写所有的数据你给它。它只会写入缓冲区的末尾,不会再写了。
如果要确保获取所有数据,则必须在缓冲区填满时增大缓冲区。
我们很幸运,如果我们用零调用它作为最大长度(我们在上面使用 buf->len - buf->cursor
的地方),我们可以获得格式化字符串的大小。如果我们这样做,并且我们不需要提供缓冲区,那么我们就会得到我们需要的长度。所以,在我们写入缓冲区之前,我们可以请求必要的长度,然后我们可以扩展缓冲区以获得足够的长度 space.
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX(A, B) (((A) > (B)) ? (A) : (B))
struct buf
{
size_t cursor, len;
char data[];
};
struct buf *resize_buf(struct buf *buf, size_t len)
{
// don't allow len == 0; it will mess up doubling of lengths.
len = len ? len : 1;
struct buf *new_buffer = realloc(buf, offsetof(struct buf, data) + len);
assert(new_buffer);
// if it's a new buffer, set the cursor (otherwise it is already set)
new_buffer->cursor = buf ? new_buffer->cursor : 0;
new_buffer->len = len;
return new_buffer;
}
struct buf *new_buf(size_t len)
{
return resize_buf(0, len);
}
int main(void)
{
char n[] = "2445";
struct buf *buf = new_buf(0); // empty now, but we will resize
for (size_t i = 0; i < sizeof n; i++)
{
// I've changed the formatting string to %d, so now you get the ASCII
// codes plus one. They are no longer a single character long.
// the +1 after snprintf() is for the zero sentinel '[=11=]'
size_t needed = snprintf(0, 0, "%d ", n[i] + 1) + 1;
if (needed > buf->len - buf->cursor)
{
buf = resize_buf(buf, MAX(buf->len + needed, 2 * buf->len)); // double the length
}
buf->cursor +=
snprintf(buf->data + buf->cursor, // where to write
buf->len - buf->cursor, // how much you can write (snprintf takes care of '[=11=]')
"%d ", n[i] + 1); // what to write
}
printf("result = `%s`\n", buf->data);
free(buf);
return 0;
}
我将缓冲区增加两倍以获得线性时间增长,如果每次只以所需的大小增加缓冲区,它最终会以二次方 运行ning 时间结束。除此之外,没有什么复杂的;我们询问我们需要多少space,然后我们通过在需要时增加缓冲区来确保我们拥有它,然后我们像以前一样写入缓冲区。
不过,这并不是一个特别好的解决方案。我们绝对必须确保我们在第一次和第二次调用 snprintf
时创建的格式化字符串是相同的,否则我们真的会搞砸,缓冲区实现的细节从根本上暴露给用户。最好把它包在一个函数里。
如果我们想处理一般的格式化字符串,我们需要一个可变参数函数,如果你不习惯它们,它们可能看起来有点奇怪。但它们是这样工作的:你使用 ...
作为函数的最后一个参数,然后你可以使用 va_start()
获得指向附加参数的指针,你需要再次释放一些保存参数的数据结构va_end()
.
为了我们的目的,我们不需要对参数做任何特殊的事情,因为我们可以用它们调用 vsnprintf
。该函数等同于 snprintf
接受这些结构而不是参数。我认为 vsnprintf
来自 C99,所以你也应该拥有它,但我承认它可能直到 C11 才存在(但你真的应该也拥有它)。
我们需要调用vsnprintf
两次,我们需要设置参数并在每次调用前再次释放它们,但除此之外没有任何其他事情。
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX(A, B) (((A) > (B)) ? (A) : (B))
struct buf
{
size_t cursor, len;
char data[];
};
struct buf *resize_buf(struct buf *buf, size_t len)
{
// don't allow len == 0; it will mess up doubling of lengths.
len = len ? len : 1;
struct buf *new_buffer = realloc(buf, offsetof(struct buf, data) + len);
assert(new_buffer);
// if it's a new buffer, set the cursor (otherwise it is already set)
new_buffer->cursor = buf ? new_buffer->cursor : 0;
new_buffer->len = len;
return new_buffer;
}
struct buf *new_buf(size_t len)
{
return resize_buf(0, len);
}
struct buf *write_to_buf(struct buf *buf, const char *restrict fmt, ...)
{
va_list args;
va_start(args, fmt);
size_t needed = vsnprintf(0, 0, fmt, args);
va_end(args);
if (needed > buf->len - buf->cursor)
{
buf = resize_buf(buf, MAX(buf->len + needed, 2 * buf->len));
}
va_start(args, fmt);
buf->cursor +=
vsnprintf(buf->data + buf->cursor,
buf->len - buf->cursor,
fmt, args);
va_end(args);
return buf;
}
int main(void)
{
char n[] = "2445";
struct buf *buf = new_buf(0); // empty now, but we will resize
for (size_t i = 0; i < sizeof n; i++)
{
buf = write_to_buf(buf, "%d ", n[i] + 1);
}
printf("result = `%s`\n", buf->data);
free(buf);
return 0;
}
这应该会让您了解如何创建一个可以写入通用格式字符串的缓冲区。我并没有声称此代码是稳定的或正确的接口类型或其他任何东西,我只是很快地完成了它,但它应该是开始的地方。