天天看點

C語言void指針的用法

在使用C語言進行開發的過程中,經常會遇到void*這樣一個特殊的指針,容易被新手忽視,實際上void*非常強大。下面舉幾個比較常見的例子。

1. 用作泛型,接收任意資料類型指針

void*用于指向特定位址,而無需關心這個位址上存放着什麼類型的資料。例如常見的memcpy等函數就用到void*,函數原型如下:

void *memcpy(void *des, void *src, size_t n)
           

此處的void *des和void *src可以接收任意類型的資料類型指針,既然是記憶體拷貝,入參就不應該限制傳入什麼類型的指針,邏輯上十分合理。

2. 動态記憶體申請與釋放

動态記憶體申請函數傳回值一般都是void*,如果申請成功則傳回的是申請的記憶體塊的首位址,申請失敗則傳回一個空指針NULL,NULL相當于(void *)0。C庫的malloc函數原型如下:

void *malloc(size_t size) 
           

一般申請完記憶體之後會與指針類型強制轉換一起使用,如下所示。

typedef struct 
{
    char    *name;
    int     age;
    ...
}animal, *animal_t;

animal_t dog = (animal_t )malloc(sizeof(animal));
if(!dog)
{
    printf("malloc failed\n");
}
           

3. 私有資料關聯

利用void*關聯私有資料是一種常見的程式設計技巧,這種技巧在Linux中廣泛存在。以下截取了宋寶華老師的《Linux裝置驅動開發詳解》的一小段代碼,保留了核心部分。struct file結構體中有一個void* private_data,在globalmem_open中使用struct file的private_data指針記錄對應的裝置的位址,那麼後續隻要擷取到了file結構體指針,也就可以通過file結構體指針的private_data成員來間接擷取到對應的裝置了,例如globalmem_read函數。

#define DEVICE_NUM    10

struct globalmem_dev
{
    struct cdev cdev;  
    ...
};

struct globalmem_dev *global_devp;

static int __init globalmem_init(void)
{
    int i;

    /* 配置設定裝置号等操作 */
    ... 

    /* 為global_devp申請DEVICE_NUM個globalmem_dev記憶體大小的空間 */
    global_devp = kzalloc(sizeof(struct globalmem_dev) * DEVICE_NUM, GFP_KERNEL);
    if(!global_devp )    
        return -1;

    for(i = 0; i < DEVICE_NUM; i++)
    {
        /* 将申請的DEVICE_NUM個裝置添加到字元裝置節點中 */
        globalmem_setup_cdev(global_devp + i, i);
    }
    
    return 0;
}

static int globalmem_open(struct inode *inode,struct file *filp)
{
    /* 在globalmem_setup_cdev中已經添加到字元裝置節點了 */
   
    struct globalmem_dev *dev = container_of(inode->i_dedv, 
                                    struct globalmem_dev, cdev);
    filp->private_data = dev;
}

static ssize_t globalmem_read(struct file *filp, char __user *buf,
                                    size_t size, loff_t *ppos)
{
    struct globalmem_dev *dev = filp->private_data;
}
           

不僅僅是Linux,在一些RTOS中也常常能見到void*的身影,例如線程或定時器在建立時,往往需要一個入口函數或逾時回調函數,而這些函數的入參往往就是一個void*,必要時對這些void*加以利用,能起到簡化代碼、減小耦合等作用。

繼續閱讀