繁体   English   中英

C代码的效率

[英]Efficiency of C Code

我创建了一个小程序,用于生成随机的十六进制颜色代码。 它可以编译并运行,没有错误,但是由于我是新手,所以我在质疑代码的效率并寻找可能的优化方法的解释。

这是代码:

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

int main(void) {
    int i, r;
    time_t t;
    char hexadecimal[7];

    char* hex[16];
    hex[0] = "0";
    hex[1] = "1";
    hex[2] = "2";
    hex[3] = "3";
    hex[4] = "4";
    hex[5] = "5";
    hex[6] = "6";
    hex[7] = "7";
    hex[8] = "8";
    hex[9] = "9";
    hex[10] = "A";
    hex[11] = "B";
    hex[12] = "C";
    hex[13] = "D";
    hex[14] = "E";
    hex[15] = "F";  

    strcpy(hexadecimal, "#");

    srand((unsigned) time(&t));

    for (i = 0; i < 6; i++) {
        r = rand() % 16;
        strcat(hexadecimal, hex[r]);
    }   
    printf("%s\n", hexadecimal);

    return 0;
}

再次,该程序可以编译并运行而没有错误,但是我对此表示怀疑。 任何提示或更正将不胜感激。

char hexadecimal[7]; 太小,无法容纳需要8个char字符串 "#123456"


重复调用strcat(hexadecimal, hex[r]); 效率低下。 每个strcat()都可以“花费”现有字符串的长度。 要循环n次并用strcat()一个长串的n字符,需要花费O(n*n)时间。 跟踪字符串的末尾并将其附加在其中的效率更高。

然而,让我们做到这一点没有一个字符串


rand()将生成一个int[0.. RAND_MAX] RAND_MAX可以小至0x7FFF或大至INT_MAX

代码正在寻找生成6 * 4位的随机数据。 因此,调用rand() 1或2次,而不是6次。

// for rand(), uint32_t, printf(), PRIX32
#include <stdlib.h>
#include <stdint.h>
#include <stdlio.h>
#include <inttypes.h>

int main(void) {
  // srand() omitted for simplicity,  let OP add back in

  uint32_t r = rand();
  if (RAND_MAX < 0xFFFFFF) {
    r += r*RAND_MAX;  // like r = r*(RAND_MAX + 1) yet avoids RAND_MAX + 1 overflow
    r += rand();
  }

  // We only want 24-bits
  r &= 0xFFFFFF;
  // If RAND_MAX is a Mersenne Number  M=2^n-1, 
  // then `r` value is no more biased than `rand()`

  printf("#%06" PRIX32 "\n", r);
}

注意:执行I / O的时间通常比此代码的其余部分多100倍的时间。

是的,您的代码效率很低。 这是学习C语言中字符和字符串协同工作方式的好机会。知道了这一点,我们可以编写一个更紧密的版本。 它的运行速度要快得多,此外,它的编写和阅读也可能会更容易一些。

(请注意:您的hexadecimal数组太小。我们需要为'#'加上6个十六进制数字的空间,再加上终止符'\\0' 。因此它必须为char hexadecimal[8] 。)

我们将使用字符而不是字符串来构建hexadecimal字符串。 因此,让我们让它不是一个字符串数组,而不是hex是一个字符串数组:

char hex[16];
hex[0] = '0';
hex[1] = '1';
/* ... */

但是,等等,字符数组是一个字符串。 所以我们可以这样写:

char hex[16] = "0123456789ABCDEF";

要不就:

char hex[] = "0123456789ABCDEF";

(两者之间有细微的差别,但是对于此程序,无论哪种方式都没有关系。)

现在,我们不再使用strcathex[r]字符串连接到我们正在构建的字符串上,而是将hex[r]字符直接放置在我们正在构建的字符串中:

for (i = 0; i < 6; i++) {
    r = rand() % 16;
    hexadecimal[i+1] = hex[r];
}

您会注意到,我分配给hexadecimal[i+1] ,而不是hexadecimal[i] 我们必须将所有内容“向右移”一位,因为字符串已经包含了开头的'#' 做到这一点的另一种方法(尽管在C语言中是不常见的)是使用基于1的循环:

for (i = 1; i <= 6; i++) {
    r = rand() % 16;
    hexadecimal[i] = hex[r];
}

另外,由于我们一次要在字符串中建立一个字符,因此,我们也可以通过返回并更改来同样地以领先'#'字符

strcpy(hexadecimal, "#");

hexadecimal[0] = '#';

我们还有另一件事要做。 由于我们一直在手工建立一个字符串,因此我们也必须手工处理null终止:

hexadecimal[7] = '\0';

但最终,这将完成相同的操作:生成格式正确的字符串hexadecimal其中包含六个随机十六进制数字:

printf("%s\n", hexadecimal);

而且,在我开始之前,先做一个关于效率的脚注。 我说:“是的,您的代码效率很低”,我声称重写将“快得多”地运行。 虽然这是事实,但这都是相对的。 即使是“低效”版本,其运行速度也要比您可以衡量的快得多,并且两个版本之间的差异几乎是不可察觉的。

我,我还是会写一次字符的版本,因为我是个老派的C家伙,我也这么认为。 但是,如果strcat某种原因更喜欢strcat版本-如果它更容易或更快捷地编写,或者更容易阅读,或者更容易就位,那么我们仍然可以更喜欢它,而不必担心它的版本“效率低下”。

只是为了好玩,我在笔记本电脑上编译并运行了这两个版本。 根据Unix的“时间”命令,“低效”版本占用了0.002秒的用户时间。 “更高效”的版本花费了0.002秒。 因此,不管有什么区别,它都小于一毫秒,并且被开销问题所淹没,例如程序完全启动所花费的时间。

因此,我向两个程序都添加了另一个循环,以便它们生成1,000,000个随机十六进制字符串。 现在有一个区别:低效率版本花费0.404s,高效率版本花费0.263s。 (考虑一下:一百万个随机字符串,在不到半秒钟的时间内。计算机非常非常快。)将其除掉,效率低下的版本每次迭代花费0.404÷1000000 = .000000404s,即404 纳秒 另一方面,高效版本仅花费263纳秒。 因此,是的,有效版本更有效-但除非您需要大量这些随机十六进制值,否则您将永远不会注意到差异。


还有一个脚注。 生产线存在严重的潜在问题

r = rand() % 16;

最令人讨厌的是,有rand()实现在低阶位不是很随机。 因此,如果像这里一样仅抓住低位,则最终可能会得到一个完全非随机的序列,每次都经过16个值的相同序列。

解决此问题的最简单方法(假设您可以使用它)是切换到更好的random()函数:

r = random() % 16;

如果这样做,则需要使用其伴随的srandom函数:

srandom((unsigned) time(&t));

(侧面问题:当发现rand()的问题时,为什么有人不只是解决它,而不是让它坏了,而是发明一个新的但功能稍差的标准函数,效果更好,希望所有人都记得使用它呢?老实说,我不知道。)

如果您不能使用random()则可以使用其高阶位来躲避rand的问题,如下所示:

r = rand() / (RAND_MAX / 16 + 1);

另请参阅C FAQ列表 ,问题13.1613.18

暂无
暂无

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

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