【问题标题】:Reading a String as an int pointer将字符串作为 int 指针读取
【发布时间】:2013-12-05 21:42:39
【问题描述】:

好的,这一切都是从我对哈希码的兴趣开始的。在阅读了 Jon Skeet 的一篇文章后,我问了this question。这让我对指针算术产生了浓厚的兴趣,我几乎没有这方面的经验。所以,在阅读了this page 之后,我开始进行实验,因为我从那里和我的其他出色的同龄人那里得到了初步的了解!

现在我正在做更多的实验,我相信我已经准确地复制了下面 string 实现中的哈希码循环(我保留对此有误的权利):

Console.WriteLine("Iterating STRING (2) as INT ({0})", sizeof(int));
Console.WriteLine();

var val = "Hello World!";
unsafe
{
    fixed (char* src = val)
    {
        var ptr = (int*)src;
        var len = val.Length;
        while (len > 2)
        {
            Console.WriteLine((char)*ptr);
            Console.WriteLine((char)ptr[1]);

            ptr += 2;
            len -= sizeof(int);
        }

        if (len > 0)
        {
            Console.WriteLine((char)*ptr);
        }
    }
}

但是,结果让我有点困惑;有点儿。结果如下:

Iterating STRING (2) as INT (4)

H
l
o
W
r
d

认为,最初,ptr[1] 的值将是与第一个字母一起读取(或压扁)的第二个字母。然而,显然不是。那是因为ptr[1] 在技术上是第一次迭代的byte 4 而第二次迭代的byte 12

【问题讨论】:

  • 如果目标只是学习指针算法 - 学习 C
  • @zerkms,目的是理解指针算法,这样我就可以更好地理解微软编写的这些函数。我不想做指针运算,我只是想理解它,这样我在阅读代码时就知道它的含义。
  • 如果我没记错我的指针。 “ptr”的类型为“int*”。由于 int 是一个 32 位值(4bytes) ptr[1] 应该指向 'src+(sizeof(int) * index)' 这将是字节 4。

标签: c#


【解决方案1】:

您的问题是您将指针转换为 int* 指针.. 它是 32 位.. 而不是像 char* 那样的 16 位。

因此,每个增量为 32 位。这是一张图片(如果你必须赞美我的作品):

抱歉,这些不可靠的箭头.. 我觉得我的鼠标电池快没电了

当您通过 char 指针读取时.. 您正在以 16 位逐个字符读取。

当您将其转换为 int 指针时.. 您正在以 32 位增量读取。这意味着,ptr[0] 既是 H 又是 e(但指向 H 的底部)。 ptr[1] 都是 l 的..

这就是为什么您实际上是在输出中跳过了一个字符。

当您在此处将其转换回 char 时:

Console.WriteLine((char)*ptr);

..只有前 16 位将由该转换产生,即每对中的第一个字符。

【讨论】:

  • 该死的,我已经达到了当天的次数上限!人们似乎很喜欢我的照片! :(
  • 不不...我很高兴人们喜欢我的照片 :)
【解决方案2】:

http://msdn.microsoft.com/en-us/library/vstudio/x9h8tsay.aspx

char 是 16 位,int 是 32 位。每次将 1 添加到 int ptr 时,都会添加 2 个 char 指针。

这就是为什么您只能看到奇数字符。

【讨论】:

    【解决方案3】:

    @Simon Whitehead 的回答是一个很好的解释。

    将值分解为它们驻留在内存中的字节将有助于您更好地理解这一点。希望下面的代码和 cmets 将帮助您了解为什么您只在 int* 索引处编写字符。

    var val = "Hello World!";
    /*
               Hello World!
    char idx = 012345678911
                         01
    
               Hello World!
    int idx =  0 1 2 3 4 5
    
    -> this is why len should be 6 below    
    
    */
    unsafe
    {
        fixed (char* src = val)
        {
            var ptr = (int*)src;
    
            //explicit definition of what val.Length / 2 would actually mean
            // -> there are actually 6 integers here but 12 chars
            var len = val.Length * sizeof(char) / sizeof(int);  
            while (len > 0)
            {
                //char pointer to the first "char" of the int
                var word = (char*) ptr;         
                Console.WriteLine(*word);
                /* types matter here.  ptr[1] is the next _integer_ 
                   not the next character like it would for a char* */
                Console.WriteLine(word[1]);   //next char of the int @ ptr
    
                ptr++; // next integer / word[2]
                len--;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      c# 字符串中的字符长度为 2 个字节,只要它们以 UTF16 编码。

      【讨论】:

        【解决方案5】:

        char 是 16 位,而 int 是 32,所以在转换后你当时读取的是 32 位。 如果您使用 short(16 位)而不是 int,您可以轻松看到它。然后你会得到你的Hello

        var ptr = (short*)src;
        

        【讨论】:

        • “所以施法后你跳过一个” --- 这句话不清楚。由于+ 1 而跳过了一个字母,而不是由于强制转换
        【解决方案6】:

        我认为这是你的问题:

        在 C# 中,字符由 2 个字节(16 位)表示。另一方面,整数是 4 个字节(32 位)。整数可以转换为最多 2^16 的字符,因为可以重新解释表示该整数的相同 16 位以表示 UTF-16 中的字符。底层位完全相同,但它们被读取为不同的值。

        但是,让您感到困惑的是尺寸差异。一个 int 是 4 个字节到 chars 2,所以通过以SizeOf(Int)(4Bytes)而不是 Char 或 Byte * 2 为单位递增(就像您的 int 指针所做的那样),您将向前移动 32 位,读取 16,然后再向前跳过 32 个字符,导致您每隔一个字符跳过一次。因此,H L O W R D.

        如果您想了解更多关于指针算法和位运算的知识,学习一些基本的 C 语言是一种很酷且非常有趣(有争议)的方式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-10
          • 1970-01-01
          • 2013-03-06
          • 1970-01-01
          相关资源
          最近更新 更多