سؤال

وأنا غريبة عن الاتفاقيات لنوع المجانسة مؤشرات / المصفوفات في C ++. هنا حالة استخدام لدي في هذه اللحظة:

<اقتباس فقرة> حساب اختباري 32 بت بسيط على النقطة الثنائية من البيانات والتعامل معها على أنها مجموعة من الأعداد الصحيحة 32-بت (نحن نعرف طولها الإجمالي من مضاعفات الرقم 4)، ثم تلخيص كل القيم وتجاهل تجاوز.

وأتوقع مثل هذه الوظيفة لتبدو مثل هذا:

uint32_t compute_checksum(const char *data, size_t size)
{
    const uint32_t *udata = /* ??? */;
    uint32_t checksum = 0;
    for (size_t i = 0; i != size / 4; ++i)
        checksum += udata[i];
    return udata;
 }

والآن السؤال لدي هو، ماذا عليك أن تنظر في "أفضل" وسيلة لتحويل data إلى udata؟

وعلى غرار C المدلى بها؟

udata = (const uint32_t *)data

وC ++ الزهر التي يفترض جميع مؤشرات قابلة للتحويل؟

udata = reinterpret_cast<const uint32_t *>(data)

وC ++ أن يلقي بين أنواع مؤشر التعسفية باستخدام void* المتوسط؟

udata = static_cast<const uint32_t *>(static_cast<const void *>(data))

والزهر من خلال اتحاد؟

union {
    const uint32_t *udata;
    const char *cdata;
};
cdata = data;
// now use udata

وأنا أدرك تماما أن هذا لن يكون حلا المحمولة 100٪، ولكن أتوقع فقط لاستخدامها على مجموعة صغيرة من منصات حيث أنا أعلم أنه يعمل (أي الذاكرة الصغيرة المحايدة بالوصول والافتراضات مترجم على التعرجات المؤشر). بماذا تنصحين؟

هل كانت مفيدة؟

المحلول

وبقدر ما يتعلق الأمر معيار C ++، litb " الصورة إجابة صحيحة تماما وأكثر المحمولة. صب const char *data إلى const uint3_t *، سواء كان ذلك عن طريق مجموعة من الممثلين على غرار C، static_cast، أو reinterpret_cast، يكسر قواعد صارمة التعرج (انظر <لأ href = "http://cellperformance.beyond3d.com/articles/2006/06/understanding -strict-aliasing.html "يختلط =" نوفولو noreferrer "> فهم الصارم التعرج ). إذا كنت ترجمة مع التحسين الكامل، هناك فرصة جيدة رمز ولن تفعل ذلك الشيء الصحيح.

والصب من خلال اتحاد (مثل my_reint litb) هو على الارجح أفضل حل، على الرغم من أنها لا تنتهك تقنيا القاعدة التي إذا كنت أكتب إلى الاتحاد من خلال عضو واحد وقراءته من خلال آخر، فإنه يؤدي إلى سلوك غير معرف. ومع ذلك، عمليا كل المجمعين تدعم هذا الأمر، وأنه يؤدي إلى النتيجة المتوقعة. إذا كنت ترغب على الاطلاق لتتوافق مع معيار 100٪، انتقل مع طريقة تحويل بعض الشيء. خلاف ذلك، أنصح الذهاب مع الصب من خلال اتحاد، والتي من المرجح أن تعطيك أداء أفضل.

نصائح أخرى

وتجاهل الكفاءة والبساطة من التعليمات البرمجية كنت تفعل:

#include <numeric>
#include <vector>
#include <cstring>

uint32_t compute_checksum(const char *data, size_t size) {
    std::vector<uint32_t> intdata(size/sizeof(uint32_t));
    std::memcpy(&intdata[0], data, size);
    return std::accumulate(intdata.begin(), intdata.end(), 0);
}

وأود أيضا الإجابة مشاركة litb، وتلك التي تحول كل حرف بدوره، إلا أنه منذ شار قد وقعت، وأعتقد أنه يحتاج إلى قناع اضافية:

checksum += ((data[i] && 0xFF) << shift[i % 4]);

عند نوع المجانسه هو مشكلة محتملة، وتفضل عدم اكتب التورية بدلا من محاولة القيام بذلك بأمان. إذا لم تقم بإنشاء أي مؤشرات مستعارة من أنواع متميزة في المقام الأول، ثم لم يكن لديك ما يدعو للقلق ما قد يفعله مترجم مع الأسماء المستعارة، ولا يفعل مبرمج الصيانة الذي يرى static_casts متعددة من خلال الاتحاد.

إذا كنت لا تريد تخصيص الكثير من ذاكرة إضافية، ثم:

uint32_t compute_checksum(const char *data, size_t size) {
    uint32_t total = 0;
    for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
        uint32_t thisone;
        std::memcpy(&thisone, &data[i], sizeof(uint32_t));
        total += thisone;
    }
    return total;
}

والأمثل بما فيه الكفاية سوف تتخلص من memcpy وuint32_t متغير إضافي كليا على دول مجلس التعاون الخليجي، ومجرد قراءة قيمة عددية الصغيرة المحايدة، أيا كان أنجع وسيلة للقيام بذلك على النظام الأساسي الخاص بك، مباشرة من مجموعة مصدر. كنت آمل أن نفس الشيء ينطبق على المجمعين "خطيرة" أخرى. ولكن هذا الرمز هو الآن أكبر من لlitb، لذلك ليس هناك الكثير مما يمكن قوله عن ذلك البعض من الألغام أسهل أن تتحول إلى قالب وظيفة التي ستعمل فقط كذلك مع uint64_t، ومنجم يعمل الأصلي endian نيس بدلا من اختيار قليلا -endian.

وهذا بالطبع لا المحمولة تماما. فإنه يفترض أن تمثيل تخزين sizeof (uint32_t) حرف يتوافق مع تمثيل التخزين من uin32_t في الطريقة التي نريد. وهذا يعني ضمنا في السؤال، لأنه ينص على أن المرء لا يستطيع أن "يعامل" من جهة أخرى. Endian نيس، ما إذا كان شار هو 8 بت، وعما إذا كان uint32_t يستخدم كل بت في تمثيل التخزين يمكن أن تتدخل واضح، ولكن السؤال يعني أنها لن تفعل ذلك.

وهناك بلدي خمسين سنتا - طرق مختلفة للقيام بذلك

.
#include <iostream>
#include <string>
#include <cstring>

    uint32_t compute_checksum_memcpy(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            // memcpy may be slow, unneeded allocation
            uint32_t dest; 
            memcpy(&dest,data+i,4);
            checksum += dest;
        }
        return checksum;
    }

    uint32_t compute_checksum_address_recast(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            //classic old type punning
            checksum +=  *(uint32_t*)(data+i);
        }
        return checksum;
    }

    uint32_t compute_checksum_union(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            //Syntax hell
            checksum +=  *((union{const char* c;uint32_t* i;}){.c=data+i}).i;
        }
        return checksum;
    }

    // Wrong!
    uint32_t compute_checksum_deref(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            checksum +=  *&data[i];
        }
        return checksum;
    }

    // Wrong!
    uint32_t compute_checksum_cast(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            checksum +=  *(data+i);
        }
        return checksum;
    }


int main()
{
    const char* data = "ABCDEFGH";
    std::cout << compute_checksum_memcpy(data, 8) << " OK\n";
    std::cout << compute_checksum_address_recast(data, 8) << " OK\n";
    std::cout << compute_checksum_union(data, 8) << " OK\n";
    std::cout << compute_checksum_deref(data, 8) << " Fail\n";
    std::cout << compute_checksum_cast(data, 8) << " Fail\n";
}

وأنا أعلم وكان هذا الموضوع غير نشط لفترة من الوقت، ولكن اعتقدت أن الرد بسيط عام الصب روتين لهذا النوع من الشيء:

// safely cast between types without breaking strict aliasing rules
template<typename ReturnType, typename OriginalType>
ReturnType Cast( OriginalType Variable )
{
    union
    {
        OriginalType    In;
        ReturnType      Out;
    };

    In = Variable;
    return Out;
}

// example usage
int i = 0x3f800000;
float f = Cast<float>( i );

ونأمل أن يساعد شخص ما!

وهذا يبدو وكأنه سبيل المثال قضية كتاب من عند استخدام reinterpret_cast، فإن أي شيء آخر تعطيك نفس التأثير دون الوضوح تحصل من استخدام لغة بناء لاستعمالها الرسمي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top