0Pricing
Swift Academy · บทเรียน

withUnsafeBytes และการทำงานร่วมกับ C

เชื่อมต่อกับ API ของ C ที่ต้องการไบต์ดิบ

withUnsafeBytes และการทำงานร่วมกับ C เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

จากชนิดข้อมูลสู่ไบต์ดิบ

บางครั้งคุณต้องมองค่าหนึ่งไม่ใช่ในรูปของ Int หรือ struct แต่เป็นลำดับของไบต์ดิบ เพื่อคำนวณแฮช เขียนลงไฟล์ หรือส่งให้ API ของ C withUnsafeBytes มอบสิ่งนั้นให้คุณโดยตรง นั่นคือมุมมองชั่วคราวของพื้นที่จัดเก็บค่าในรูปของ UInt8

var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { rawBuffer in
    print(rawBuffer.count) // 4 bytes
}

UnsafeRawBufferPointer

คลอเชอร์จะได้รับ UnsafeRawBufferPointer ซึ่งเป็นบัฟเฟอร์ของไบต์ที่ไม่มีชนิดขององค์ประกอบ คุณใช้ดัชนีเพื่ออ่านค่า UInt8 แต่ละรายการได้ บัฟเฟอร์นี้อ่านได้อย่างเดียว ส่วนรูปแบบที่เปลี่ยนแปลงได้คือ withUnsafeMutableBytes

var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { bytes in
    for b in bytes { print(String(format: "%02x", b)) }
}
// Order depends on endianness

ลำดับไบต์มีความสำคัญ

ลำดับไบต์ที่คุณสังเกตเห็นขึ้นอยู่กับลำดับไบต์ของแพลตฟอร์ม บนเครื่องแบบ little-endian (Apple silicon, Intel) ไบต์ที่มีนัยสำคัญน้อยที่สุดจะอยู่ก่อน เมื่อทำการจัดลำดับข้อมูลสำหรับโพรโทคอล ให้แปลงอย่างชัดเจนด้วย bigEndian หรือ littleEndian

let host: UInt32 = 0x01020304
let networkOrder = host.bigEndian
var be = networkOrder
withUnsafeBytes(of: &be) { bytes in
    // Always 01 02 03 04 regardless of platform
    print(Array(bytes))
}

ไบต์ของ struct

withUnsafeBytes(of:) ทำงานกับค่าใด ๆ ก็ได้ รวมถึง struct ความยาวของบัฟเฟอร์เท่ากับ MemoryLayout<T>.size และไบต์เติมช่องว่างใด ๆ จะถูกรวมอยู่ด้วย แต่เนื้อหาของไบต์เหล่านั้นไม่ได้รับการระบุ

struct RGB { var r: UInt8; var g: UInt8; var b: UInt8 }
var color = RGB(r: 255, g: 128, b: 0)
withUnsafeBytes(of: &color) { bytes in
    print(bytes[0], bytes[1], bytes[2]) // 255 128 0
}

การโหลดค่าจากไบต์

หากต้องการตีความไบต์ดิบใหม่เป็นค่าแบบมีชนิด ให้ใช้ load(fromByteOffset:as:) กับบัฟเฟอร์ดิบ ออฟเซ็ตต้องสอดคล้องกับการจัดแนวของชนิดข้อมูล และไบต์นั้นต้องแทนค่าที่ถูกต้องของชนิดข้อมูลดังกล่าวจริง ๆ

let raw: [UInt8] = [0x04, 0x03, 0x02, 0x01]
let n = raw.withUnsafeBytes { ptr in
    ptr.load(fromByteOffset: 0, as: UInt32.self)
}
print(String(format: "0x%08x", n))

loadUnaligned สำหรับข้อมูลแบบแพ็ก

รูปแบบไบนารีมักจัดเก็บฟิลด์โดยไม่มีไบต์เติมเพื่อจัดแนว การเรียก load ที่ออฟเซ็ตซึ่งไม่ได้จัดแนวถือเป็นพฤติกรรมที่ไม่มีการกำหนด ให้ใช้ loadUnaligned(fromByteOffset:as:) แทน เพื่ออ่านจากออฟเซ็ตใด ๆ ได้อย่างปลอดภัย

let packet: [UInt8] = [0xFF, 0x04, 0x03, 0x02, 0x01]
let field = packet.withUnsafeBytes { ptr in
    ptr.loadUnaligned(fromByteOffset: 1, as: UInt32.self)
}
print(field)

การเชื่อมข้อมูล Swift เข้ากับ C

ฟังก์ชัน C จำนวนมากรับ const void * พร้อมความยาว Data และอาร์เรย์เปิดให้ใช้ withUnsafeBytes คุณจึงส่ง baseAddress และ count ได้โดยตรงโดยไม่ต้องคัดลอกข้อมูล

import Foundation
let data = Data([0x68, 0x69]) // "hi"
data.withUnsafeBytes { (raw: UnsafeRawBufferPointer) in
    if let base = raw.baseAddress {
        // c_consume(base, raw.count)
        print("ptr", base, "len", raw.count)
    }
}

การรับตัวชี้จาก C

เมื่อคอลแบ็กของ C ส่ง const void * พร้อมความยาวมาให้คุณ ให้ห่อหุ้มข้อมูลนั้นใน UnsafeRawBufferPointer เพื่อวนซ้ำอย่างปลอดภัย Swift จะนำเข้าตัวชี้ C เป็น UnsafeRawPointer?

func handleC(_ ptr: UnsafeRawPointer, _ len: Int) {
    let buffer = UnsafeRawBufferPointer(start: ptr, count: len)
    let checksum = buffer.reduce(0) { $0 &+ Int($1) }
    print("checksum:", checksum)
}

bindMemory และการตีความใหม่

หากต้องการปฏิบัติต่อพื้นที่ดิบเสมือนเป็นองค์ประกอบที่มีชนิด bindMemory(to:capacity:) จะบอกคอมไพเลอร์ว่าควรตีความพื้นที่นั้นอย่างไร การใช้สิ่งนี้ผิดวิธี เช่น ผูกหน่วยความจำเดียวกันเข้ากับชนิดที่เข้ากันไม่ได้สองชนิด จะละเมิดกฎการใช้นามแฝงอย่างเข้มงวดของ Swift และถือเป็นพฤติกรรมที่ไม่มีการกำหนด

let raw = UnsafeMutableRawPointer.allocate(
    byteCount: 8, alignment: 8)
defer { raw.deallocate() }
let typed = raw.bindMemory(to: Int.self, capacity: 1)
typed.pointee = 1234
print(typed.pointee)

assumingMemoryBound เทียบกับ bindMemory

bindMemory จะเปลี่ยนวิธีตีความหน่วยความจำอย่างถาวร ส่วน assumingMemoryBound(to:) จะยืนยันว่าหน่วยความจำนั้น ผูกกับชนิดดังกล่าวอยู่แล้ว โดยไม่เปลี่ยนแปลงสิ่งใด ให้ใช้แบบหลังเฉพาะเมื่อคุณมั่นใจในการผูกที่มีอยู่ เช่น หน่วยความจำที่คุณทราบว่าจัดเก็บชนิดข้อมูลที่กำหนด

func sumInts(_ raw: UnsafeRawPointer, _ count: Int) -> Int {
    let p = raw.assumingMemoryBound(to: Int.self)
    var total = 0
    for i in 0..<count { total += p[i] }
    return total
}

รายการตรวจสอบความปลอดภัย

การทำงานร่วมกันระหว่างไบต์ดิบกับ C เป็นจุดที่มักซ่อนสาเหตุของการหยุดทำงานไว้มากที่สุด โปรดจำไว้ว่า:

  • อย่าให้ตัวชี้ของคลอเชอร์หลุดออกไปใช้นอกคลอเชอร์
  • เคารพการจัดแนว หรือใช้รูปแบบ Unaligned
  • จัดการ endianness อย่างชัดเจนเมื่อจัดลำดับข้อมูล
  • อย่าผูกหน่วยความจำใหม่เข้ากับชนิดที่ขัดแย้งกัน
  • ตรวจสอบกรณีที่ baseAddress เป็น nil สำหรับบัฟเฟอร์ว่าง
// Convert any value to a hex string safely
import Foundation
func hex<T>(_ v: T) -> String {
    var value = v
    return withUnsafeBytes(of: &value) { bytes in
        bytes.map { String(format: "%02x", $0) }.joined()
    }
}

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจของคุณเกี่ยวกับการอ่านข้อมูลไบนารีแบบแพ็ก

สรุป

คุณได้เรียนรู้การเชื่อมค่าของ Swift และ C ผ่านไบต์ดิบ:

  • withUnsafeBytes(of:) แสดงค่าใด ๆ ในรูปของ UInt8
  • load / loadUnaligned ตีความไบต์ใหม่เป็นค่าแบบมีชนิด โดยคำนึงถึงการจัดแนว
  • Data และอาร์เรย์เปิดให้ใช้ withUnsafeBytes เพื่อส่งตัวชี้ + ความยาวให้ C
  • bindMemory และ assumingMemoryBound ควบคุมวิธีระบุชนิดให้หน่วยความจำดิบ
  • ลำดับไบต์ การจัดแนว และการที่ตัวชี้หลุดออกไปใช้นอกขอบเขต คืออันตรายหลัก

คำถามที่พบบ่อย

บทเรียน “withUnsafeBytes และการทำงานร่วมกับ C” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “withUnsafeBytes และการทำงานร่วมกับ C” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “withUnsafeBytes และการทำงานร่วมกับ C”

เชื่อมต่อกับ API ของ C ที่ต้องการไบต์ดิบ คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “withUnsafeBytes และการทำงานร่วมกับ C” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม

ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. MemoryLayout และการจัดแนว
  2. UnsafePointer และ UnsafeMutablePointer
  3. ตัวชี้บัฟเฟอร์ที่ไม่ปลอดภัย
  4. withUnsafeBytes และการทำงานร่วมกับ C
← กลับไปที่ Swift Academy