在C中同时打印多行[关闭]

uemypmqf  于 2023-10-16  发布在  其他
关注(0)|答案(4)|浏览(85)

已关闭,此问题需要details or clarity。它目前不接受回答。
**想改善这个问题吗?**通过editing this post添加详细信息并澄清问题。

6天前关闭
Improve this question
我想知道是否有一种方法可以在C中同时打印2行(或更多)。类似的东西,但在1个循环中完成:

int M(int n){return n+1;}
int F(int n){return n-1;}

int main(void)
{
    int n;
    printf("F(n):\n");
    for (n = 0; n <= 5; n++)
    {
        printf("%d %d ", n, F(n));
    }
    printf("\nM(n):\n");

    for (n = 0; n <= 5; n++)
    {
        printf("%d %d ", n, M(n));
    }
    printf("\n");
}

编辑:预期输出如下:

F(n):
0 1 1 2 2 3 3 4 4 5 5 6
M(n):
0 -1 1 0 2 1 3 2 4 3 5 4

但打印会“一栏一栏”地进行。

w8ntj3qf

w8ntj3qf1#

在标准C中,没有一种方法可以重复地将一些信息写入屏幕上的一行,将一些信息写入屏幕上的另一行,并在两行之间交替输出。
如果在生成第二行的信息之前无法轻松或方便地生成第一行的信息,则需要为这两行使用内存存储,在内存中的缓冲区(字符串)中准备数据,并在信息生成完成时才打印行。

版本1

这段代码被简单而快速地拼凑在一起,以演示如何在内存中准备两行。它包含可怕的重复代码,所以它只适合作为演示。

#include <stdio.h>
#include <stdlib.h>

static inline int M(int x) { return x % 10; }
static inline int F(int x) { return x * x; }

int main(void)
{
    char line1[1024];
    char line2[1024];
    line1[0] = '\0';
    line2[0] = '\0';
    size_t off1 = 0;
    size_t off2 = 0;
    const char *pad = "";

    for (int i = 0; i <= 30; i++)
    {
         int nbytes;
         nbytes = snprintf(line1 + off1, sizeof(line1) - off1,
                              "%s%d %d", pad, i, F(i));
         if (nbytes < 0 || (size_t)nbytes >= sizeof(line2) - off1)
         {
             fprintf(stderr, "formatting failed (%s:%d)\n", __FILE__, __LINE__);
             exit(EXIT_FAILURE);
         }
         off1 += nbytes;
         nbytes = snprintf(line2 + off2, sizeof(line2) - off2,
                              "%s%d %d", pad, i, M(i));
         if (nbytes < 0 || (size_t)nbytes >= sizeof(line2) - off2)
         {
             fprintf(stderr, "formatting failed (%s:%d)\n", __FILE__, __LINE__);
             exit(EXIT_FAILURE);
         }
         off2 += nbytes;
         pad = " ";
    }
    puts(line1);
    puts(line2);

    return 0;
}

基本思想是在循环中构建两个输出行(在数组中- aka buffers -line1line2),并仅在循环结束时打印它们。
我会在自己的代码中使用更聪明的错误报告--或者安排动态分配输出行,等等。我会将标识信息添加到输出行,并可能使用逗号空格而不仅仅是空格(pad = ", ";)来分隔数字对。这些改进对于给你基本答案来说是不必要的。
此外,两行并行代码结构迫切需要封装成一个结构类型和一个可以构建行的函数-并避免尽可能多的代码重复。
下面是一系列使用各种形式的缓冲区管理的程序,简化了主程序代码。

版本2

此版本使用具有固定大小缓冲区的缓冲区结构。如果不重新编译程序,就无法轻松地增加缓冲区大小。这对主函数有很大的清理作用。

#include <stdio.h>
#include <stdlib.h>

static inline int M(int x) { return x % 10; }
static inline int F(int x) { return x * x; }

struct OutputBuffer
{
    size_t   maxlen;
    size_t   offset;
    char     buffer[1024];
};

static void InitBuffer(struct OutputBuffer *output)
{
    output->maxlen = sizeof(output->buffer);
    output->offset = 0;
    output->buffer[0] = '\0';
}

static void AddToLine(struct OutputBuffer *output, const char *pad, int value1, int value2)
{
    char *buffer = output->buffer + output->offset;
    size_t buflen = output->maxlen - output->offset;
    int nbytes = snprintf(buffer, buflen, "%s%d %d", pad, value1, value2);
    if (nbytes < 0 || (size_t)nbytes >= buflen)
    {
        fprintf(stderr, "formatting failed (%s:%d) - %d vs %zu\n",
                __FILE__, __LINE__, nbytes, buflen);
        exit(EXIT_FAILURE);
    }
    output->offset += nbytes;
}

int main(void)
{
    struct OutputBuffer output1;
    struct OutputBuffer output2;
    InitBuffer(&output1);
    InitBuffer(&output2);
    const char *pad = "";

    for (int i = 0; i <= 30; i++)
    {
         AddToLine(&output1, pad, i, F(i));
         AddToLine(&output2, pad, i, M(i));
         pad = ", ";
    }
    printf("F(n):\n%s\n", output1.buffer);
    printf("M(n):\n%s\n", output2.buffer);

    return 0;
}

版本3

这个版本的代码使用了一个动态分配的缓冲区,但是程序将结构分配为局部变量并调用初始化器例程。

#include <stdio.h>
#include <stdlib.h>

static inline int M(int x) { return x % 10; }
static inline int F(int x) { return x * x; }

struct OutputBuffer
{
    size_t   maxlen;
    size_t   offset;
    char    *buffer;
};

static void OutputBuffer_ctor(struct OutputBuffer *output, size_t buflen) 
{
    output->buffer = malloc(buflen);
    if (output->buffer == NULL)
    {
        fprintf(stderr, "failed to allocate %zu bytes of memory\n", buflen);
        exit(EXIT_FAILURE);
    }
    output->maxlen = buflen;
    output->offset = 0;
    output->buffer[0] = '\0';
}

static void OutputBuffer_dtor(struct OutputBuffer *output) 
{
    if (output != NULL)
    {
        free(output->buffer);
        output->buffer = NULL;
        output->offset = 0;
        output->maxlen = 0;
    }
}

static void AddToLine(struct OutputBuffer *output, const char *pad, int value1, int value2)
{
    char *buffer = output->buffer + output->offset;
    size_t buflen = output->maxlen - output->offset;
    int nbytes = snprintf(buffer, buflen, "%s%d %d", pad, value1, value2);
    if (nbytes < 0 || (size_t)nbytes >= buflen)
    {
        fprintf(stderr, "formatting failed (%s:%d) - %d vs %zu\n",
                __FILE__, __LINE__, nbytes, buflen);
        exit(EXIT_FAILURE);
    }
    output->offset += nbytes;
}

int main(void)
{
    struct OutputBuffer output1;
    struct OutputBuffer output2;
    OutputBuffer_ctor(&output1, 1024);
    OutputBuffer_ctor(&output2, 1024);
    const char *pad = "";

    for (int i = 0; i <= 30; i++)
    {
         AddToLine(&output1, pad, i, F(i));
         AddToLine(&output2, pad, i, M(i));
         pad = ", ";
    }
    printf("F(n):\n%s\n", output1.buffer);
    printf("M(n):\n%s\n", output2.buffer);

    OutputBuffer_dtor(&output1);
    OutputBuffer_dtor(&output2);

    return 0;
}

版本4

此代码动态分配整个结构以及结构中的缓冲区。可以很容易地修改它以根据需要增加缓冲区。
这个版本使用了我的错误报告例程,其代码在我的SOQ(堆栈溢出问题)存储库中,在src/libsoq子目录中作为文件stderr.cstderr.h提供。通常,我会将argv[0]传递给err_setarg0(),但main函数被定义为int main(void),因此我硬连接了名称。

#include <stdio.h>
#include <stdlib.h>
#include "stderr.h"

static inline int M(int x) { return x % 10; }
static inline int F(int x) { return x * x; }

struct OutputBuffer
{
    size_t   maxlen;
    size_t   offset;
    char    *buffer;
};

static struct OutputBuffer *OutputBuffer_ctor(size_t buflen) 
{
    struct OutputBuffer *output = malloc(sizeof(*output));
    if (output == NULL)
        err_syserr("failed to allocate %zu bytes of memory\n", sizeof(*output));
    output->buffer = malloc(buflen);
    if (output->buffer == NULL)
        err_syserr("failed to allocate %zu bytes of memory\n", buflen);
    output->maxlen = buflen;
    output->offset = 0;
    output->buffer[0] = '\0';
    return output;
}

static void OutputBuffer_dtor(struct OutputBuffer *output) 
{
    if (output != NULL)
    {
        free(output->buffer);
        free(output);
    }
}

static void AddToLine(struct OutputBuffer *output, const char *pad, int value1, int value2)
{
    char *buffer = output->buffer + output->offset;
    size_t buflen = output->maxlen - output->offset;
    int nbytes = snprintf(buffer, buflen, "%s%d %d", pad, value1, value2);
    if (nbytes < 0 || (size_t)nbytes >= buflen)
        err_error("formatting operation failed (%s:%d) - %d vs %zu\n",
                  __FILE__, __LINE__, nbytes, buflen);
    output->offset += nbytes;
}

int main(void)
{
    err_setarg0("pl47");
    struct OutputBuffer *output1 = OutputBuffer_ctor(1024);
    struct OutputBuffer *output2 = OutputBuffer_ctor(1024);
    const char *pad = "";

    for (int i = 0; i <= 30; i++)
    {
         AddToLine(output1, pad, i, F(i));
         AddToLine(output2, pad, i, M(i));
         pad = ", ";
    }
    printf("F(n):\n%s\n", output1->buffer);
    printf("M(n):\n%s\n", output2->buffer);

    OutputBuffer_dtor(output1);
    OutputBuffer_dtor(output2);

    return 0;
}

更多可能性

OutputBuffer结构类型可以通过在头文件中声明该类型为不透明类型(可能是typedef struct OutputBuffer OutputBuffer;),并沿着声明管理该类型变量的函数,从而分离为对主程序不透明的类型。会有一个单独的源文件包含函数的实现。
主程序(或使用该工具的任何其他代码)将使用头文件并写入OutputBuffer *output = OutputBuffer_ctor(1024);来定义一个变量供后续使用。将有一个向缓冲区添加字符串的函数-void OutputBuffer_add(OutputBuffer *output, const char *str);-和一个访问缓冲区字符串的函数-const char *OutputBuffer_data(OutputBuffer *output)
AddToLine()函数内部可能会将额外的数据格式化到本地缓冲区中,然后调用OutputBuffer_add()来存储它-它将是一个使用输出缓冲区包的函数。
或者(或者可能另外),包可以提供一对函数来处理可变长度参数列表的格式,类似于printf()vprintf(),可以用来向缓冲区添加信息。
不过,对于这个问题来说,这有点过头了。

总结

事实上,就这个问题而言,在我看来,第二版已经足够了。

zpf6vheq

zpf6vheq2#

标准C不支持同时在两个不同的行上打印,所以OP所要求的只能使用非标准方法或输出缓冲,这很少值得努力。
我想你的目的是避免重复相同的代码结构超过一次。
一种不同的方法是将一系列的打印放在一个函数中。OP没有提供函数F()M()的定义,因此在示例中添加了虚拟函数:

#include <stdio.h>

// Example functions for F and M
int F(int n) {return n+1;}    // Expression for F
int M(int n) {return n-1;}    // Expression for M

// Print the given series (func) from the given start value (n0) to end value (n1)
void printSeries(int n0, int n1, int (* func)(int))
{
   for(int n = n0; n <= n1; n++)
   {
      printf("%d %d ", n, func(n));
   }
   printf("\n");
}

int main()
{
   printSeries(0, 30, F);
   printSeries(0, 30, M);
   return 0;
}
oalqel3c

oalqel3c3#

你可以写简单的缓冲输出函数。它将打印到内存中的虚拟屏幕,然后在准备好时输出。

typedef struct screen_t
{
    size_t xsize, ysize;
    size_t lastXtouched, lastYtouched;
    int xpos, ypos;
    char *screen;
    int status;
}screen_t;

char *GETYLINE(screen_t *s, size_t line)
{
    return &s -> screen[(s -> xsize + 1) * line];
}

screen_t *xyprintf(screen_t *screen, int xpos, int ypos, const char *format, ...)
{
    va_list va;
    va_start(va, format);

    if(screen && screen -> screen)
    {
        if(xpos == INT_MIN) ypos = screen -> xpos;
        if(ypos == INT_MIN) ypos = screen -> ypos;
        if(xpos >= 0 && xpos < screen -> xsize && ypos >= 0 && ypos < screen -> ysize)
        {
            int chars_printed;
            chars_printed = vsnprintf(GETYLINE(screen, screen -> ysize), screen -> xsize - xpos + 1, format, va);
            memcpy(GETYLINE(screen, ypos) + xpos, GETYLINE(screen, screen -> ysize), chars_printed);
            screen -> xpos = xpos + chars_printed;
        }
    }
    va_end(va);

    return screen;
}

screen_t *initScreen(size_t xsize, size_t ysize)
{
    screen_t *screen = calloc(sizeof(*screen), 1);
    if(screen)
    {
        screen -> screen = malloc((ysize + 1) * (xsize + 1));
        if(screen -> screen)
        {
            screen -> xsize = xsize;
            screen -> ysize = ysize;

            for(size_t y = 0; y < ysize; y++)
            {
                memset(GETYLINE(screen, y), ' ', xsize);
                GETYLINE(screen, y)[xsize] = 0;
            }
        }
        else
        {
            free(screen);
            screen = NULL;
        }
    }
    return screen;
}

void updateScreen(screen_t *screen)
{
    if(screen)
    {
        for(size_t y = 0; y < screen -> ysize; y++)
        {
            puts(GETYLINE(screen, y));
        }
    }
}

int main(void)
{
    screen_t *screen = initScreen(80, 10);
    int c = 0;

    if(screen)
    {
        for(int x = 0; x < 10; x++)
        {
            for(int y = 20; y < 30; y++)
            {
                xyprintf(screen, x * 6, (y - 20), "%d", c++);
            }
        }
        updateScreen(screen);
        free(screen -> screen);
        free(screen);
    }
}

https://godbolt.org/z/eMdGjnsr8

krugob8w

krugob8w4#

如果你能容忍2个循环,你可以通过嵌套它们和使用函数指针数组来实现你的目标。这是一种 * 非常 * 低维护/复制的方式,可以扩展到更多输出:

#include <stdio.h>

int F(int n) {return n+1;}
int M(int n) {return n-1;}
int Q(int n) {return n+42;}  // spontaneous additional function

int main( void ) {
    struct {
        char *title;
        int (*f)(int);
    } fns[] = {
        { "Q(n):", Q }, // added to this "table"
        { "M(n):", M },
        { "F(n):", F },
    };
    size_t nf = sizeof fns / sizeof fns[0];

    // code below remains unchanged. "data driven"
    while( nf-- ) {
        puts( fns[nf].title ); // used in reverse sequence
        for( int n = 0; n <= 5; n++ )
            printf( "%d %d ", n, fns[nf].f(n) );
        putchar( '\n' );
    }
}

输出量:

F(n):
0 1 1 2 2 3 3 4 4 5 5 6
M(n):
0 -1 1 0 2 1 3 2 4 3 5 4
Q(n):
0 42 1 43 2 44 3 45 4 46 5 47

相关问题