C代码的效率

时间:2018-05-03 03:59:22

标签: c hex

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

以下是代码:

#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;
}

同样,程序编译并运行没有错误,但我持怀疑态度。任何提示或更正都将不胜感激。

2 个答案:

答案 0 :(得分:4)

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

重复调用strcat(hexadecimal, hex[r]);效率低下。可以预期每个strcat()“花费”现有字符串的长度。要循环n次,strcat()一长串n字符需要O(n*n)次。更有效地跟踪字符串的结尾并附加在那里。

然而,让我们在没有字符串的情况下这样做。

rand()会生成int[0.. RAND_MAX]RAND_MAX可能与0x7FFF一样小,也可能与INT_MAX一样小。 Example

代码正在寻找生成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倍。

答案 1 :(得分:0)

是的,您的代码效率很低。这是一个了解字符和字符串在C中协同工作方式的好机会。知道这一点,我们可以编写更严格的版本。它会更快地运行,作为奖励,它可能更容易编写和阅读。

(旁注:您的hexadecimal数组太小了。我们需要'#'的空间,加上6个十六进制数字,加上终止'\0'。所以它需要{ {1}}。)

我们将使用字符而不是字符串来构建char hexadecimal[8]字符串。因此,不是hexadecimal是一个字符串数组,而是让它成为一个字符数组:

hex

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

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

或只是:

char hex[16] = "0123456789ABCDEF";

(两者之间略有不同,但对于这个程序,这种方式无关紧要。)

现在,我们不是使用char hex[] = "0123456789ABCDEF"; strcat字符串连接到我们正在构建的字符串上,而是将hex[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] = '#';

但最终这将做同样的事情:生成一个包含六个随机十六进制数字的格式良好的字符串hexadecimal[7] = '\0';

hexadecimal

而且,在我走之前,关于效率的脚注。我说“是的,你的代码效率非常低”,我声称我的重写会“更快”地运行。虽然这是真的,但它都是相对的。即使是“效率低下”的版本也会比你测量的速度快得多,两个版本之间的差异几乎难以察觉。

我,无论如何,我会写一个角色的版本,因为我是一个老派的C类型的家伙,我想是这样的。但如果有理由更喜欢printf("%s\n", hexadecimal); 版本 - 如果它更容易或更快地编写,或更容易阅读,或者更容易在第一时间做到正确 - 我们可能更喜欢它,而且只是不要担心它的“低效率”。

为了好玩,我在笔记本电脑上编译并运行了两个版本。 “低效”版本花费了0.002s的用户时间,这是通过Unix'time'命令来衡量的。 “效率更高”的版本耗时... 0.002秒。所以无论差异是什么,它都会小于一毫秒,并且会受到开销问题的影响,比如程序启动所需的时间。

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

又一个脚注。线

存在严重的潜在问题
strcat

最令人讨厌的是,r = rand() % 16; 的实现在低位中非常随机。因此,如果你只抓住低位,就像你在这里一样,你可以得到一个完全非随机的序列,每次都会经历相同的16个值序列。

解决此问题的最简单方法(假设您可以使用)是切换到更好的rand()功能:

random()

如果您这样做,则需要使用其随附的r = random() % 16; 功能:

srandom

(旁边的问题:当发现srandom((unsigned) time(&t)); 的问题时,为什么没有人只是修复它,而不是让它破坏并发明一个更新但但稍微不那么标准的功能,效果更好,并希望每个人都记得用它代替?老实说我不知道​​。)

如果您无法使用rand(),则可以使用高阶位来避开random()的问题,如下所示:

rand

另请参阅C FAQ list,问题13.1613.18