خانه / آموزش‌ها / آموزش جامع کتابخانه ctypes در پایتون (اتصال به C/C++)

آموزش جامع کتابخانه ctypes در پایتون (اتصال به C/C++)

🐍 HomeOfPython
|
📅 1404/10/18

سطح مقدماتی (Beginner Level)

کتابخانه ctypes یکی از قدرتمندترین ابزارهای استاندارد پایتون است که به شما اجازه می‌دهد توابع موجود در کتابخانه‌های اشتراکی (Shared Libraries) یا DLLهای نوشته شده به زبان C را فراخوانی کنید. این ابزار پل ارتباطی بین سرعت C و سادگی پایتون است.

۱. مفهوم بارگذاری کتابخانه‌ها (Loading Libraries)

برای استفاده از کدهای C، ابتدا باید فایل کامپایل شده (مانند .dll در ویندوز یا .so در لینوکس) را بارگذاری کنید. ctypes روش‌های مختلفی برای این کار دارد.

مثال ۱: بارگذاری در ویندوز و لینوکس

از آنجا که مسیر فایل‌ها در سیستم‌عامل‌های مختلف متفاوت است، این کد صرفاً نحوه آدرس‌دهی را نشان می‌دهد و به فایل خارجی نیاز دارد، بنابراین استاتیک است.

python
# Static code: Requires specific OS and file existence
import ctypes
import os

# در ویندوز
if os.name == 'nt':
    my_lib = ctypes.WinDLL("C:\\Windows\\System32\\msvcrt.dll")

# در لینوکس
else:
    my_lib = ctypes.CDLL("libc.so.6")

مثال ۲: استفاده از cdll و windll

تفاوت اصلی این دو در نحوه فراخوانی توابع (Calling Convention) است. cdll از استاندارد cdecl و windll از استاندارد stdcall استفاده می‌کند.

python
# Static code: Loading logic snippet
from ctypes import cdll, windll

# بارگذاری یک کتابخانه سفارشی که خودتان نوشته‌اید
# my_custom_lib = cdll.LoadLibrary("./libmycode.so")

۲. انواع داده‌های سازگار با C (C Data Types)

پایتون و C داده‌ها را متفاوت در حافظه ذخیره می‌کنند. برای ارسال متغیرها به توابع C، باید آن‌ها را به فرمت ctypes تبدیل کنید.

لیست انواع داده‌های رایج

  • c_int: معادل int در C
  • c_float: معادل float در C
  • c_char_p: معادل char * (رشته)
  • c_bool: معادل bool

مثال ۱: تعریف متغیرهای صحیح و اعشاری

این کد کاملاً قابل اجراست و نحوه تخصیص مقدار و خواندن آن را نشان می‌دهد.

Python

مثال ۲: کار با رشته‌ها (Strings)

در ctypes باید رشته‌های پایتون را به بایت (byte string) تبدیل کنید تا به عنوان char * شناخته شوند.

Python

۳. فراخوانی توابع ساده

وقتی کتابخانه بارگذاری شد و نوع داده‌ها مشخص شد، می‌توانید تابع را صدا بزنید.

مثال ۱: فراخوانی فرضی

python
# Static code: Hypothetical function call
# فرض کنیم تابعی به نام add در فایل libtest.so داریم
# int add(int a, int b);

from ctypes import CDLL, c_int

# lib = CDLL("./libtest.so")
# result = lib.add(c_int(5), c_int(10))
# print(result)

سطح پیشرفته (Professional Level)

در پروژه‌های واقعی، ارتباط با C فراتر از ارسال چند عدد صحیح است. شما نیاز به مدیریت حافظه، ارسال ساختارها (Structs)، کار با پوینترها و مدیریت خطاها دارید.

معماری Ctypes

۱. ساختارها (Structures)

زبان C از struct برای گروه‌بندی داده‌ها استفاده می‌کند. در پایتون باید کلاسی بسازید که از ctypes.Structure ارث‌بری کند و فیلدها را در _fields_ تعریف کنید.

مثال ۱: تعریف یک ساختار Point

این مثال نحوه شبیه‌سازی struct Point { int x; int y; }; را نشان می‌دهد.

Python

مثال ۲: ساختارهای تودرتو (Nested Structures)

استفاده از یک ساختار درون ساختار دیگر.

Python

۲. پوینترها و ارجاع (Pointers & ByRef)

بسیاری از توابع C به جای مقدار، آدرس حافظه (Pointer) را دریافت می‌کنند. ctypes دو روش برای این کار دارد: pointer() (ایجاد آبجکت پوینتر جدید) و byref() (ارسال سبک آدرس).

مثال ۱: استفاده از byref (روش بهینه)

این روش سریع‌تر است و برای فراخوانی توابع استفاده می‌شود.

Python

مثال ۲: تعریف نوع پوینتر با POINTER

زمانی که می‌خواهید یک نوع داده "اشاره‌گر به صحیح" تعریف کنید.

Python

۳. آرایه‌ها (Arrays)

در ctypes، آرایه‌ها با ضرب یک نوع داده در یک عدد صحیح تعریف می‌شوند.

مثال ۱: آرایه اعداد صحیح

معادل int arr[5]; در C.

Python

۴. مدیریت نوع بازگشتی و آرگومان‌ها (argtypes & restype)

بهترین روش حرفه‌ای (Best Practice) این است که قبل از فراخوانی تابع، انواع ورودی و خروجی آن را برای مفسر پایتون مشخص کنید تا جلوی کرش کردن برنامه گرفته شود.

مثال ۱: تنظیم امضای تابع (Function Signature)

python
# Static code: Setting argtypes and restype
from ctypes import CDLL, c_double, c_int

# فرض: double power(double base, int exp);
# lib = CDLL("./math_lib.so")
# pow_func = lib.power

# تعیین نوع ورودی‌ها (لیستی از انواع)
# pow_func.argtypes = [c_double, c_int]

# تعیین نوع خروجی (تک نوع)
# pow_func.restype = c_double

# حالا اگر ورودی اشتباه بدهید، پایتون خطای TypeError می‌دهد نه Segmentation Fault
# print(pow_func(2.0, 3))

مثال ۲: کار با void *

اگر تابع C پوینتر void برمی‌گرداند یا می‌گیرد، از c_void_p استفاده کنید.

Python