2011-12-09 22 views
4

有人可以解釋爲什麼我沒有得到變量的值,但它的內存呢?如何在C中正確使用void指針?

我需要使用void *指向「無符號短」值。

據我所知無效指針,他們的大小是未知的,他們的類型是未知的。
但是,一旦初始化它們,它們是已知的,對吧?

爲什麼我的printf語句打印錯誤的值?

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

void func(int a, void *res){ 
    res = &a; 
    printf("res = %d\n", *(int*)res); 
    int b; 
    b = * (int *) res; 
    printf("b =%d\n", b); 
} 


int main (int argc, char* argv[]) 
{ 
    //trial 1 
    int a = 30; 
    void *res = (int *)a; 
    func(a, res); 
    printf("result = %d\n", (int)res); 
    //trial 2 
    unsigned short i = 90; 
    res = &i; 
    func(i, res); 
    printf("result = %d\n", (unsigned short)res); 
    return 0; 
} 

輸出I得到:

res = 30 
b =30 
result = 30 
res = 90 
b =90 
result = 44974 
+3

printf你在說什麼? – Mansuro

+2

你期望什麼樣的價值觀,你究竟得到了什麼? –

+0

它似乎工作[這裏](http://www.ideone.com/P1UEB)指出什麼是不尋常的,它可能會讓你更好的幫助。 –

回答

0

printf("result = %d\n", (int)res);正在打印RES(指針)作爲一個數字的值。 請記住,指針是內存中的地址,所以這將打印一些隨機看起來的32位數字。

如果你想打印存儲在該地址的值,那麼你需要(int)* res - 雖然(int)是不必要的。

編輯:如果你想打印一個指針的值(即地址),那麼你應該使用%p它本質上是相同的,但格式更好,並理解如果你的平臺上int和poitner的大小不同

0

如果你有一個空指針ptr你知道點到int,才能訪問到int寫:

int i = *(int*)ptr; 

也就是說,首先將其轉換爲指針到INT與轉換運算符(int*)然後將其解引用到ge t指向的價值。

您正在將指針直接轉換爲值類型,儘管編譯器會樂於這樣做,但這可能不是您想要的。

0
void *res = (int *)a; 

a是INT但不是PTR,也許它應該是:

void *res = &a; 
+0

另外,如何初始化一個void指針與信息,如果我將這個void指針傳遞給一個函數返回void將指向一個int? – user900785

+0

@ user900785不太理解你的問題。一般來說,void *指針可以指向任何類型。不需要顯式的類型轉換。 – zchenah

0

一個空指針是已知的大小;它是一個地址的大小,所以與其他指針的大小相同。你可以在一個整數和一個指針之間自由轉換,這很危險。如果您的意思是採用變量a的地址,則需要使用(void *)&a將其地址轉換爲void *

7

有一點要記住:C不能保證int將足夠大,以容納一個指針(包括void*)。該演員陣容是而不是便攜的東西/好主意。使用%pprintf指針。

同樣,你在做一個「壞投」在這裏:void* res = (int*) a告訴編譯器:「我相信的a值是一個有效的int*,所以你應該把它看作」除非您確實知道存儲在內存地址30上的int,否則這是錯誤的。

幸運的是,您立即覆蓋res,地址爲,其他a。 (您有兩個名爲a的變量和兩個名爲res的變量,main中的變量和func中的變量,func中的變量是main中的值的副本,當您在那裏調用時)。一般而言,覆蓋值一個參數的功能是「壞形式」,但它在技術上是合法的。就個人而言,我建議所有聲明你的函數的參數爲​​時間const 99%(例如void func (const int a, const void* res)

然後,你施放resunsigned short。我不認爲有人仍然在16位地址空間CPU上運行(可能是您的Apple II),因此肯定會通過截斷來破壞res的值。

一般來說,在C中,類型轉換是危險的你在控制編譯器的類型系統,並說:「看看這裏,編譯員先生,我是程序員,我比你知道我在這裏有什麼更好的地方,所以,你只需要保持安靜,並做到這一點。從指針轉換到非指針類型幾乎是普遍錯誤的。指針類型之間的轉換更多的時候是錯誤的。

我建議檢查一下本頁面的一些「相關」鏈接,以便總體瞭解C類指針的工作原理。有時需要閱讀一些內容才能真正掌握這些內容如何結合在一起。

+0

最佳答案,很抱歉,我只能投票一次;) – Scolytus

5
(unsigned short)res 

是一個指針鑄造,解析度是一個內存地址,將其轉換成一無符號短,你得到的地址值作爲一個無符號短的,而不是十六進制值,以確保你要讓你可以打印

*(unsigned short*)res 

第一投(unsigned short*)res使得上void*指針強制轉換爲上unsigned short指針正確的值。然後,您可以使用*

1

來提取res中指向的內存地址內的值。C中使用void指針作爲一種通用指針。一個void指針變量可以用來包含任何變量類型的地址。 void指針的問題是一旦給指針指定了一個地址,關於變量類型的信息就不再可供編譯器檢查。

通常,應該避免使用void指針,因爲其地址在void指針中的變量的類型不再可供編譯器使用。另一方面,有些情況下void指針非常方便。然而,程序員應該知道地址在void指針變量中的變量的類型並正確使用它。

很多舊的C源代碼在類型指針和空指針之間都有C風格轉換。現代編譯器沒有必要這樣做,應該避免。

void指針變量的大小是已知的。未知的是其指針在void指針變量中的變量的大小。例如這裏有一些源代碼示例。

該空指針已被用於
// create several different kinds of variables 
int iValue; 
char aszString[6]; 
float fValue; 

int *pIvalue = &iValue; 
void *pVoid = 0; 

int iSize = sizeof(*pIvalue); // get size of what int pointer points to, an int 

int vSize = sizeof(*pVoid);  // compile error, size of what void pointer points to is unknown 
int vSizeVar = sizeof(pVoid); // compiles fine size of void pointer is known 

pVoid = &iValue;  // put the address of iValue into the void pointer variable 
pVoid = &aszString[0]; // put the address of char string into the void pointer variable 
pVoid = &fValue;  // put the address of float into the void pointer variable 

pIvalue = &fValue;  // compiler error, address of float into int pointer not allowed 

一種方式是通過具有幾種不同類型的被設置爲用於一個函數的參數,典型地某種調度功能的結構的。由於該函數的接口允許使用不同的指針類型,因此必須在參數列表中使用void指針。然後,指向的變量類型由附加參數或檢查指向的變量來確定。這種類型的功能使用的例子將如下所示。在這種情況下,我們在結構的各種排列的第一個成員中包含一個結構類型的指示符。只要所有與此函數一起使用的結構都具有指示結構類型的int作爲它們的第一個成員,這將起作用。

struct struct_1 { 
    int iClass; // struct type indicator. must always be first member of struct 
    int iValue; 
}; 

struct struct_2 { 
    int iClass; // struct type indicator. must always be first member of struct 
    float fValue; 
}; 

void func2 (void *pStruct) 
{ 
    struct struct_1 *pStruct_1 = pStruct; 
    struct struct_2 *pStruct_2 = pStruct; 

    switch (pStruct_1->iClass) // this works because a struct is a kind of template or pattern for a memory location 
    { 
    case 1: 
     // do things with pStruct_1 
     break; 
    case 2: 
     // do things with pStruct_2 
     break; 
    default: 
     break; 
    } 
} 

void xfunc (void) 
{ 
    struct struct_1 myStruct_1 = {1, 37}; 
    struct struct_2 myStruct_2 = {2, 755.37f}; 

    func2 (&myStruct_1); 
    func2 (&myStruct_2); 
} 

喜歡的東西上面有許多的軟件設計問題的耦合和凝聚力所以,除非你有使用這種方法很好的理由,最好是重新考慮你的設計。然而,C編程語言允許你這樣做。

有些情況下void指針是必需的。例如,分配內存的malloc()函數將返回一個包含已分配區域地址的空指針(如果分配失敗,則返回NULL)。在這種情況下,void指針允許使用一個malloc()函數,該函數可以返回任何類型變量的內存地址。以下顯示使用各種變量類型的malloc()

void yfunc (void) 
{ 
    int *pIvalue = malloc(sizeof(int)); 
    char *paszStr = malloc(sizeof(char)*32); 

    struct struct_1 *pStruct_1 = malloc (sizeof(*pStruct_1)); 
    struct struct_2 *pStruct_2Array = malloc (sizeof(*pStruct_2Array)*21); 

    pStruct_1->iClass = 1; pStruct_1->iValue = 23; 

    func2(pStruct_1); // pStruct_1 is already a pointer so address of is not used 

    { 
     int i; 
     for (i = 0; i < 21; i++) { 
      pStruct_2Array[i].iClass = 2; 
      pStruct_2Array[i].fValue = 123.33f; 
      func2 (&pStruct_2Array[i]); // address of particular array element. could also use func2 (pStruct_2Array + i) 
     } 
    } 
    free(pStruct_1); 
    free(pStruct_2Array); // free the entire array which was allocated with single malloc() 
    free(pIvalue); 
    free(paszStr); 
} 
1

如果你想要做的是通過名稱傳遞變量a,並用它的東西,你可以試試:

void func(int* src) 
{ 
    printf("%d\n", *src); 
} 

如果從庫函數得到void*,你知道它的實際類型,你應該馬上把它存儲在正確類型的變量:

int *ap = calloc(1, sizeof(int)); 

有些情況下,你必須參照接收參數的幾種情況一個void*然後施放它。我在現實世界中經常遇到的是一個線程程序。所以,你可能會寫類似:

#include <stddef.h> 
#include <stdio.h> 

#include <pthread.h> 

void* thread_proc(void* arg) 
{ 
    const int a = *(int*)arg; 
/** Alternatively, with no explicit casts: 
    * const int* const p = arg; 
    * const int a = *p; 
    */ 
    printf("Daughter thread: %d\n", a); 
    fflush(stdout); /* If more than one thread outputs, should be atomic. */ 
    return NULL; 
} 

int main(void) 
{ 
    int a = 1; 
    const pthread_t tid = pthread_create(thread_proc, &a); 
    pthread_join(tid, NULL); 

    return EXIT_SUCCESS; 
} 

如果你想驚險的生活,你可以傳遞一個uintptr_t值強制轉換爲void*並投它回來,但表示陷阱要小心。