【问题标题】:How can I quickly printf 2 dimensional array of chars made of pointers to pointers without using a loop?如何在不使用循环的情况下快速打印由指向指针的指针组成的二维字符数组?
【发布时间】:2023-03-03 01:39:01
【问题描述】:

我正在制作 ASCII 游戏,我需要性能,所以决定使用 printf()。但是有一个问题,我将我的 char 数组设计为多维 char ** 数组,打印它输出的是内存垃圾而不是数据。我知道可以使用 for 循环打印它,但性能会以这种方式迅速下降。我需要像静态数组[][] 一样打印它。有什么办法吗?

我做了一些工作和不工作数组的例子。我只需要 printf() 来处理 nonWorking 数组。

编辑:在 Win 10 上使用 Visual Studio 2015,是的,我测试了性能,cout 比 printf 慢得多(但我真的不知道为什么会这样)

#include <iostream>
#include <cstdio>

int main()
{
    const int X_SIZE = 40;
    const int Y_SIZE = 20;

    char works[Y_SIZE][X_SIZE];
    char ** notWorking;

    notWorking = new char*[Y_SIZE];
    for (int i = 0; i < Y_SIZE; i++) {
        notWorking[i] = new char[X_SIZE];
    }

    for (int i = 0; i < Y_SIZE; i++) {
        for (int j = 0; j < X_SIZE; j++) {
            works[i][j] = '#';
            notWorking[i][j] = '#';
        }
        works[i][X_SIZE-1] = '\n';
        notWorking[i][X_SIZE - 1] = '\n';
    }
    works[Y_SIZE-1][X_SIZE-1] = '\0';
    notWorking[Y_SIZE-1][X_SIZE-1] = '\0';

    printf("%s\n\n", works);
    printf("%s\n\n", notWorking);

    system("PAUSE");
}

注意:我想我可以制作某种缓冲区或静态数组来复制和显示数据,但我想知道如果没有它是否可以做到。

【问题讨论】:

  • printf("%s\n%s\n%s\n%s\n [...]", notWorking[0], notWorking[1], notWorking[2], notWorking [3],[....]);会这样做(当然,假设你 NUL 终止每个分配的字符串),但我怀疑它会更快......这只是意味着你使用的是 printf() 实现内部的 for 循环,而不是你的自己的。此外,它会非常丑陋且难以维护。
  • 当您在printf 格式字符串中使用%s 时,您传递的内容将被强制转换为 const char * 指针。 working 起作用的原因是它可以成功地解释为单个字符串,而无需 UB。 Notworking无法工作并且是UB,printf无法处理这样的复杂数据结构。您需要使用循环或其他东西。
  • 顺便说一句,您实际测量过性能下降吗?因为根据我的经验,与输出 I/O 所花费的时间相比,for 循环本身所花费的时间可以忽略不计(在任何一种情况下都是相同的,因为在任何一种情况下都输出相同的字符)跨度>
  • 要记住的另一件事:printf 不是某种魔法,它在内部也使用循环。
  • 如果只有一种静态类型的打印文本的方式可以安全地允许您重载自己的流操作符,唉。不过说真的,您对使用cout 的反感可能是错误的,您确定您是unsynching 流,没有使用endl 进行不必要的刷新,并在打开优化的情况下进行编译吗?

标签: c++ arrays pointers char printf


【解决方案1】:

如果您想打印带有printf 且没有循环的二维结构,您需要将其作为连续的一维C 字符串呈现给printf。由于您的游戏需要以 2D 结构访问字符串,因此您可以将指针数组放入这个平面结构中,如下所示:

指针数组将缓冲区划分为二维结构,而缓冲区本身可以由printf 打印,因为它是一个连续的 C 字符串。

下面是相同的代码结构:

// X_SIZE+1 is for '\n's; overall +1 is for '\0'
char buffer[Y_SIZE*(X_SIZE+1)+1];
char *array[Y_SIZE];
// Setup the buffer and the array
for (int r = 0 ; r != Y_SIZE ; r++) {
    array[r] = &buffer[r*(X_SIZE+1)];
    for (int c = 0 ; c != X_SIZE ; c++) {
        array[r][c] = '#';
    }
    array[r][X_SIZE] = '\n';
}
buffer[Y_SIZE*(X_SIZE+1)] = '\0';
printf("%s\n", buffer);

Demo.

【讨论】:

  • 是的..我可能会同意,或者我会简单地将我的设计转换为一维数组来省去所有的麻烦。
  • @Shabrido 如果您的代码在 2D 中处理它更方便,那么将所有内容都转换为 1D 是没有意义的。由于您使用的是 C++,因此您可以构建一个将一维数组也呈现为二维的类,并动态计算指针。但是,由于指针的数量会相对较少,您可以通过预先计算和存储它们来节省一些 CPU 周期。
  • 为什么不简单地分配一个真正的二维数组呢?我认为没有理由有一个“错位”的一维数组,也没有一个指针到指针的表。
  • @Lundin 指针数组保留了 OP 指针数组的灵活性,以防他想在某个时候切换到非矩形字段。拥有一个指针数组或自己计算指针可以让您像处理二维结构一样处理该字段,但允许使用三角形字段。
【解决方案2】:

您可以采取一些措施来提高性能:

  • 绝对没有理由拥有一个指针数组,每个指针都指向一个数组。这将导致堆碎片,因为您的数据最终会遍布整个堆。在相邻单元中分配内存在速度方面有很多好处,例如它可能会提高数据缓存的使用率。

    相反,分配一个真正的二维数组:

    char (*array2D) [Y] = new char [X][Y];
    
  • printfcout 都非常慢,因为它们具有大量开销和您不需要的额外功能。由于它们只是系统特定控制台功能的高级包装器,因此您应该考虑直接使用系统特定功能。例如,Windows console API。但是,它会使您的程序不可移植。

    如果这不是一个选项,您可以尝试使用puts 而不是printf,因为它的开销要少得多。

  • printf/cout 的主要性能问题是它们写入“标准输出流”的末尾,这意味着您不能在您喜欢的地方写入,但总是在屏幕底部。每次更改某些内容时都强制您不断重绘整个内容,这会很慢并且可能会导致闪烁问题。

    旧的 DOS/Turbo C 程序使用称为 gotoxy 的非标准函数解决了这个问题,它允许您移动“光标”并在您喜欢的地方打印。在现代编程中,您可以使用控制台 API 函数来执行此操作。 Example for Windows.

  • 您可以/应该将图形与程序的其余部分分开。如果您只有一个线程处理图形和主线程处理算法,图形更新将更顺畅,而无需等待程序正在执行的任何其他操作。但是,它使程序更加先进,因为您必须考虑线程安全问题。

【讨论】:

  • 谢谢..我对很多事情一无所知,我仍在学习并一直在提问。
  • @Shabrido 关于堆碎片,这是从 C 风格程序继承的广泛但不好的做法。多本蹩脚的 C 书籍宣扬使用这种堆碎片表。实际上,std::vector 可能比指针对指针的东西更快,因为它可能在内部具有分配动态数据的合理方式。当然,std::vector 会带来大量其他不相关的开销,因此由于其他原因它会变慢。
  • 老实说,我从未见过这种创建所谓“真正的二维数组”的语法
  • 感谢您抽出宝贵的时间,每次进入这个网站,我都感到豁然开朗!
  • @Shabrido 是一个数组指针。它看起来很奇怪,因为我遗漏了最里面的维度。更正式的正确方法是写成char (*array) [X][Y],但是您必须以(*array)[i][j] 而不是array[i][j] 访问数组项,这很快就会变得烦人且难以阅读。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-11
  • 2018-01-04
  • 2013-07-31
  • 2012-01-26
相关资源
最近更新 更多