首页 > 同样的函数,同样的参数,不同的进程,结果不一样

同样的函数,同样的参数,不同的进程,结果不一样

#include <stdio.h>

unsigned int getHashNum(const char *str,unsigned int len,unsigned int maxPrime)
{
     register unsigned int sum = 0; 
     register unsigned int h = 0;
     register unsigned short *p = (unsigned short *)str;
     register unsigned short *s = (unsigned short *)str;
     printf("str:%s,len:%d,h:%d,maxPrime:%d\n",p,len,h,maxPrime);

     while(p - s < len) 
     {
          register unsigned short a = *(p++) * (p-s);
          printf("a:%d\n",a);
          sum += sum ^ a; 
          h += a;
     }
     printf("sum:%d\n",sum);
     return ((sum << 16) | h) % maxPrime;
}

上面那段代码在主进程和子进程中都调用,传入的参数是一样的,但是返回值不一样,谁能看出来点端倪?
主进程输出:

str:key,len:3,h:0,maxPrime:60
a:0
a:121
a:49900
sum:49934

子进程输出:

str:key,len:3,h:0,maxPrime:60
a:0
a:121
a:29896
sum:29994

我大体找到原因了,问题中的代码是我从网上找到的,确实是为了计算hash值。就像 @米克 说的那样,这里是由于指针出现了越界。函数的入参const char *str,在函数内部被转化成了unsigned short *,即这句代码register unsigned short *p = (unsigned short *)str;。c语言中char是一个字节,short是两个字节,指针str是指向char的,那么如果执行运算str+1,得到的结果应该是str的内存地址加一;但是换成表达式p+1,那么就应该是p的内存地址加二(而不是 @米克 兄说的每次移动4个位置的内存地址距离)。也就是说,在代码中while循环中,p的地址每次都是加二的,所以*p的内容第一次是k的assic码,第二次是y的assic码,第三次就越界了,如果是在同一个进程中,多次调用越界得到的内容很有可能是相同的,但是如果换了一个进程,那么越界得到的内容就极有可能是不同的。所以就出现了之前遇到的问题。
将while循环稍微修改一下,增加对p内存地址的打印:

    while(p - s < len) 
    {
        register unsigned short a =0;
        printf("value:%d[%c][%d],offset:%d\n",*p,*p,p,p-s);
        a= *(p++) * (p-s);
        printf("a:%d\n",a);
        sum += sum ^ a; 
        h += a;
    }

主进程中打出来的是这样的:

str:key,len:3,h:0,maxPrime:60
value:25963[k][17598552],offset:0
a:0
value:121[y][17598554],offset:1
a:121
value:24950[v][17598556],offset:2
a:49900
sum:49934

子进程中打印出来的是这样的:

str:key,len:3,h:0,maxPrime:60
value:25963[k][13954256],offset:0
a:0
value:121[y][13954258],offset:1
a:121
value:14948[d][13954260],offset:2
a:29896
sum:29994

可以很清晰的看到内存越界,并且p指针是每次移动两字节的。
解决方法也很简单,将p改为使用char类型指针:

unsigned int getHashNum(const char *str,unsigned int len,unsigned int maxPrime)
{
    register unsigned int sum = 0; 
    register unsigned int h = 0;
    register unsigned char *p = (unsigned char *)str;
    register unsigned char *s = (unsigned char *)str;
    printf("str:%s,len:%d,h:%d,maxPrime:%d\n",p,len,h,maxPrime);

    while(p - s < len) 
    {
        register unsigned short a =0;
        printf("value:%d[%c][%d],offset:%d\n",*p,*p,p,p-s);
        a= (unsigned short)*(p++) * (p-s);
        printf("a:%d\n",a);
        sum += sum ^ a; 
        h += a;
    }
    printf("sum:%d\n",sum);
    return ((sum << 16) | h) % maxPrime;
}

如果len>=strlen(str)而又是单数的话,就有可能出这个问题


最有可能的情况就是对 str 的操作越界了,只有这样才有可能操作到不受控的数据。
检查下 while 条件和 *(p++) * (p-s) 这个表达式的结果,我觉得像这样还好控制些

short p = 0;    // offset
short s = 0;    // start, 可以省略了

while (p < length) {
    short a = str[p++] * p
}

补充

我对 *(p++) * (p-s) 的运算顺序不是很清楚,可以尝试一下 (*(p++)) * (p-s) (不知道你是不是这个意思)。

看运行的结果,


对 str 的操作越界了,unsigned short p, p++ 实际往后对(char *)str移动了sizeof(unsigned short) = 4的距离,如果str长度为3,第一次循环就会跑出str的界, 从而对p取值就说不定了。

看出你的本意是按blocks对str求hash值(block大小是一个short),但是在实际指针操作过程有点问题。简单起见,你可以按单字节计算,将p,s指针类型改成unsigned char×。
如果想要实现按blocks计算,可以参考murmur hash方法。
···

unsigned int murmur3_32(const char *key, unsigned int len, unsigned int seed) 
{
    static const unsigned int c1 = 0xcc9e2d51;
    static const unsigned int c2 = 0x1b873593;
    static const unsigned int r1 = 15;
    static const unsigned int r2 = 13;
    static const unsigned int m = 5;
    static const unsigned int n = 0xe6546b64;

    unsigned int hash = seed;

    const int nblocks = len / 4;
    const unsigned int *blocks = (const unsigned int *) key;
    int i;
    for (i = 0; i < nblocks; i++) {
        unsigned int k = blocks[i];
        k *= c1;
        k = (k << r1) | (k >> (32 - r1));
        k *= c2;

        hash ^= k;
        hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
    }

    const char *tail = (const char *) (key + nblocks * 4);
    unsigned int k1 = 0;

    switch (len & 3) {
    case 3:
        k1 ^= tail[2] << 16;
    case 2:
        k1 ^= tail[1] << 8;
    case 1:
        k1 ^= tail[0];

        k1 *= c1;
        k1 = (k1 << r1) | (k1 >> (32 - r1));
        k1 *= c2;
        hash ^= k1;
    }

    hash ^= len;
    hash ^= (hash >> 16);
    hash *= 0x85ebca6b;
    hash ^= (hash >> 13);
    hash *= 0xc2b2ae35;
    hash ^= (hash >> 16);

    return hash;
}
【热门文章】
【热门文章】