C工作面试 - 铸造和比较

Itz*_*984 59 c int casting char

我遇到了一个棘手的(IMO)问题.我需要以最有效的方式比较两个MAC地址.

在那一刻我唯一想到的想法是微不足道的解决方案 - 一个for循环,比较位置,所以我做了,但面试官的目标是铸造.

MAC定义:

typedef struct macA {
   char data[6];
} MAC;
Run Code Online (Sandbox Code Playgroud)

功能是(我被要求实施的那个):

int isEqual(MAC* addr1, MAC* addr2)
{
    int i;

    for(i = 0; i<6; i++)
    {
        if(addr1->data[i] != addr2->data[i]) 
            return 0;
    }
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

但如上所述,他的目标是铸造.

意思是,以某种方式将给定的MAC地址转换为int,比较两个地址,然后返回.

但是在投射时int int_addr1 = (int)addr1;,只会投入四个字节,对吗?我应该检查其余的吗?含义位置4和5?

这两个charint是整数类型,因此铸造是合法的,但在所描述的情况,会发生什么?

小智 112

如果他真的不满意这种方法(这实际上是一个脑屁,因为你没有比较兆字节或千兆字节的数据,所以在这种情况下,人们不应该真的担心"效率"和"速度"),告诉他你相信标准库的质量和速度:

int isEqual(MAC* addr1, MAC* addr2)
{
    return memcmp(&addr1->data, &addr2->data, sizeof(addr1->data)) == 0;
}
Run Code Online (Sandbox Code Playgroud)

  • @ H2CO3,你可以写一篇重写OS i/o子系统的帖子并获得35分.击败面试官 - 一个每个人天生都讨厌的过程 - 和150! (20认同)
  • @Lee Huh是什么?"两个整数的memcmp效率低下"并非如此(参见上面的讨论).此外,"理智的代码"==代码不是不必要的"聪明",并且如果不需要则不依赖于UB.理智代码的一个特性是对标准库的依赖.现在你可以看到了? (5认同)
  • @FilipeGonçalves编号`data`是一个数组,而不是指针. (4认同)
  • @ H2CO3:虽然`data`是一个数组而不是一个指针,但是`memcmp(addr1-> data,addr2-> data,sizeof(addr1-> data))`仍然正确(由于衰减),不是吗? (4认同)
  • @haccks:对我来说,老鹰只代表"你可以随时登录,但你永远不能退出"...... (4认同)
  • @chux因为结构末尾可能有填充,其中包含未指定的填充字节.那会导致漏报. (4认同)
  • @Duck抱歉的软件开发状态! (2认同)
  • @Lee但是,再一次,猜测毫无意义.只有经过优化才能对基准代码进行优化.其余的只是BS. (2认同)
  • @haccks"但我仍然不明白在这里需要什么样的地址(如面试官期待的那样)?" - 我也不是.演员不是必要的,而且面试官可能期望的是**错误**因为它会导致不确定的行为. (2认同)
  • 我的预感是调用memcmp不会是最佳的1)函数调用开销的b/c.2)b/c你不能指望为处理一般情况编写的代码要比编写的代码更快,知道它只能比较六个字节.我很好奇并决定查看从VS2013生成的优化x64.事实证明函数调用是内联的,但是在完全滚动的循环中生成逐字节比较(即6个分支,6个单字节比较).所以除了取笑面试者之外,这段代码并不能很好地满足他的要求. (2认同)

Ker*_* SB 44

如果你的面试官要求你产生不明确的行为,我可能会在其他地方寻找工作.

正确的初始方法是将MAC地址存储uint64_t在至少内存中.然后比较将是微不足道的,并且可以有效地实现.

  • @Abyx:你不知道C,但是你是因为没有暗示那些错误的东西而投票给我的?:-S (16认同)
  • @ ltzik984不,很可能他不知道这是未定义的行为. (8认同)
  • @ rm5248:MAC地址*只是一个数字.就像你坚持为文本字符设计自己的21位整数,而不是使用理智的`char32_t`. (6认同)
  • @Abyx**它确实导致UB.**在您取消引用指向不兼容类型的指针的那一刻. (5认同)
  • @Abyx:为什么不发布你认为应该做的答案,我们可以看看它是否是UB? (4认同)

Gle*_*aum 16

牛仔时间:

typedef struct macA {
   char data[6];
} MAC;

typedef struct sometimes_works {
   long some;
   short more;
} cast_it

typedef union cowboy
{
    MAC real;
    cast_it hack;
} cowboy_cast;

int isEqual(MAC* addr1, MAC* addr2)
{
     assert(sizeof(MAC) == sizeof(cowboy_cast));  // Can't be bigger
     assert(sizeof(MAC) == sizeof(cast_it));      // Can't be smaller

     if ( ( ((cowboy_cast *)addr1)->hack.some == ((cowboy_cast *)addr2)->hack.some )
       && ( ((cowboy_cast *)addr1)->hack.more == ((cowboy_cast *)addr2)->hack.more ) )
             return (0 == 0);

     return (0 == 42);
}
Run Code Online (Sandbox Code Playgroud)

  • @RichardPlunkett No. Unions保证正确对齐.从C99开始,基于联合的类型双关语是合法的(它不再是UB),不像指向不兼容类型的指针. (3认同)

Apr*_*ori 13

有效的实现没有任何问题,因为你知道这已被确定为多次调用的热代码.在任何情况下,面试问题都可以有奇怪的限制.

逻辑AND是由于短路评估的先验分支指令,即使它不以这种方式编译,所以让我们避免它,我们不需要它.我们也不需要将返回值转换为真正的bool(truefalse,不是0任何非零值).

这是32位的快速解决方案:XOR将捕获差异,OR将记录两个部分的差异,NOT将否定条件为EQUALS,而不是UNEQUAL.LHS和RHS是独立的计算,因此超标量处理器可以并行执行此操作.

int isEqual(MAC* addr1, MAC* addr2)
{
    return ~((*(int*)addr2 ^ *(int*)addr1) | (int)(((short*)addr2)[2] ^ ((short*)addr1)[2]));
}
Run Code Online (Sandbox Code Playgroud)

编辑
上述代码的目的是表明这可以在没有分支的情况下有效地完成.评论指出这个C++将其归类为未定义的行为.虽然如此,VS处理这个问题.在不更改访问者的结构定义和函数签名的情况下,为了避免未定义的行为,必须进行额外的复制.因此,没有分支但具有额外副本的非未定义行为方式如下:

int isEqual(MAC* addr1, MAC* addr2)
{
    struct IntShort
    {
        int   i;
        short s;
    };

    union MACU
    {
        MAC addr;
        IntShort is;
    };

    MACU u1;
    MACU u2;

    u1.addr = *addr1; // extra copy
    u2.addr = *addr2; // extra copy

    return ~((u1.is.i ^ u2.is.i) | (int)(u1.is.s ^ u2.is.s)); // still no branching
}
Run Code Online (Sandbox Code Playgroud)