2013-07-14 62 views
1

我正在閱讀一本關於數據結構的書,並且難以理解指針的概念。讓我說,我沒有很多與C經驗前言本但在這裏不用....瞭解指針的用途

如果我做了以下內容:

int num = 5; 
int *ptrNum; 

ptrNum = # 

這是我的理解是,指針爲32位int保留足夠的內存以及實際指針所需的內存,儘管它的值僅僅是變量的內存地址。

如果保留相同數量的內存,這樣做的目的是什麼?爲什麼我會使用指針而不是變量num?我完全脫離基地嗎?

+1

指針不保留任何東西,他們只是持有數量沒有什麼比另一個值的地址了。 – James

+4

用一個人爲的例子來解釋整個情況並不能解釋。許多事情根本不可能不使用指針,並考慮到這一點;如果這不是一個整數,而是一個龐大的數組呢?你想複製整個數組來傳遞它嗎?如果一個函數需要改變它的輸入呢? –

+0

我的書特別指出,它們佔用的內存空間等於它指向的數據類型。 –

回答

12

您可以在值無效的情況下使用指針。在你的例子中,你是正確的;沒有好處。該archtetypal邊界線有用的例子是交換功能:

void swap_int(int *i1, int *i2) 
{ 
    int t1 = *i1; 
    *i1 = *i2; 
    *i2 = t1; 
} 

調用順序:

int main(void) 
{ 
    int v1 = 0; 
    int v2 = 31; 
    printf("v1 = %d; v2 = %d\n", v1, v2); 
    swap_int(&v1, &v2); 
    printf("v1 = %d; v2 = %d\n", v1, v2); 
    return 0; 
} 

如果你寫了沒有使用指針 - 這樣的:

void swap_int(int i1, int i2) 
{ 
    int t1 = i1; 
    i1 = i2; 
    i2 = t1; 
} 

int main(void) 
{ 
    int v1 = 0; 
    int v2 = 31; 
    printf("v1 = %d; v2 = %d\n", v1, v2); 
    swap_int(v1, v2); 
    printf("v1 = %d; v2 = %d\n", v1, v2); 
    return 0; 
} 

那麼你只需交換函數中的兩個局部變量而不影響調用函數中的值。使用指針可以影響調用函數中的變量。

參見:

  • scanf()的功能 - 家庭
  • strcpy()

我的理解的是,指針的儲備足夠的內存爲32位的int以及實際指針所需的內存,儘管它的值只是內存地址變量。

你似乎是描述什麼是彷彿:

int *p1; 

做相同的工作爲:

int _Anonymous; 
int *p1 = &_Anonymous; 

,沒有關係。這是C.創建p1爲指針分配足夠的空間。如第一次寫入,它不初始化它,因此它指向一個不確定的位置(或沒有位置)。它(指針)在使用之前需要初始化。因此:

int i1 = 37; 
int *p1 = &i1; 

p1分配僅保留用於指針足夠的空間(通常,對於32位編譯32位,對於一個64位編譯64位);你必須分配它指向的空間,並且你必須初始化指針。初始化指針的另一種方法是使用動態分配的內存:

int *p2 = malloc(1000 * sizeof(*p2)); 

if (p2 != 0) 
{ 
    ...use p2 as an array of 1000 integers... 
    free(p2); 
} 

您已覆蓋的結構嗎?如果不是,涵蓋結構的例子(如樹或鏈表)將無濟於事。然而,一旦你已經覆蓋的結構也一樣,你就可以使用樹或鏈接的列表:倚重三分球

struct list 
{ 
    int data; 
    struct list *next; 
}; 

struct tree 
{ 
    int data; 
    struct tree *l_child; 
    struct tree *r_child; 
}; 

這種結構依靠正確連接條目。

+0

Whoaaa這是一個巨大的答案,先生,+1會證明你太少:) – 0decimal0

+0

一個簡單而明確的解釋。 – haccks

+0

對於int arr [1000],你可以添加一個case,當一個帶有void fn(int * receivedArray)的函數比void void fn(int [] receivedArray)更優化時。 FN(ARR);' –

2

如何將一個元素添加到動態列表?通過每次創建一個新的數組?

您只需添加指向下一個元素的指針,並將前一個單元格的下一個指針鏈接到它。

如果沒有指針,則會受限於數組的順序和變量的對齊。 使用指針,您可以選擇分配區域中的任何地址,以使您具有任何對齊方式,您可以使用指向和來自您分配的任何區域的列表元素。

所以,指針給你更多的自由,而每個指針只需要32或64位空間。

2

其他一些答案的重點是獲取變量的地址並將其存儲在指針中。這只是指針的一個用途。指針的一個完全不同的用途是指向動態分配的存儲以及構建存儲。

例如,假設您想讀取文件並在內存中進行處理。但是,你不知道文件有多大。你可以把任意的上限,在你的代碼:

#define MAX_FILE_SIZE (640 * 1024) /* 640K should be large enough for anyone */ 

char data[ MAX_FILE_SIZE ]; 

這浪費內存較小的文件,而不是更大的文件足夠大。更好的方法是實際分配你需要的東西。例如:

FILE *f = fopen("myfile", "rb"); 
off_t len; 
char *data; 

fseek(f, 0, SEEK_END); /* go to the end of the file */ 
len = ftell(f);   /* get the actual file size */ 
fseek(f, 0, SEEK_SET); /* rewind to the beginning */ 

data = malloc(len); /* Allocate just as much as you need */ 

另一個主要使用指針是組織數據,在列表中,或樹木,或其他有趣的結構說。 (你的數據結構書會涉及到其中的許多內容。)如果你想重新組織你的數據,移動指針往往比複製數據要便宜得多。例如,假設您有這些列表:

struct mystruct 
{ 
    int x[1000]; 
    int y[1000]; 
}; 

這是大量的數據。如果你只是存儲在一個數組,然後整理這些數據可能是非常昂貴的:

struct mystruct array[1000]; 

嘗試對qsort ...這將是非常緩慢的。

您可以通過改爲存儲指向元素的指針並對指針進行排序來加速。即。

struct mystruct *array[1000]; 
int i; 
struct mystruct *temp; 

/* be sure to allocate the storage, though: */ 
temp = malloc(1000 * sizeof(struct mystruct)); 

for (i = 0; i < 1000; i++) 
    array[i] = temp + i; 

現在,如果你有這些結構排序,你會可以用三分球array[]而不是整個結構。

我不會進入您的書中更好涵蓋的發燒友數據結構。但是,我想我可能會給你一些指針的其他用途。

2

指針服務3個主要用途在C:

  • 假傳按引用語義;
  • 跟蹤動態分配的內存;
  • 構建動態數據結構。

假傳遞引用語義:在C中,所有函數參數都是按值傳遞的。考慮下面的代碼片段:

void foo(int a, int b) 
{ 
    a = 1; 
    b = 2; 
} 

void bar(void) 
{ 
    int x=0, y=1; 
    foo(x, y); 
    printf("x = %d, y = %d\n", x, y); 
} 

形式參數afoob與實際參數xbary,所以ab任何更改內存不同對象xy都沒有體現出來。輸出將是「x = 0,y = 1」。如果你想foo改變的xy值,則需要指針傳遞給這些變量來代替:

void foo(int *a, int *b) 
{ 
    *a = 1; 
    *b = 2; 
} 

void bar(void) 
{ 
    int x = 0, y = 1; 
    foo(&x, &y); 
    printf("x = %d, y = %d\n", x, y); 
} 

這一次,正式參數ab指針的變量xy ;寫入表達式*a*b int foo相當於在bar中寫入xy。因此,輸出是「x = 1,y = 2」。

這是如何scanf()和其他庫函數的分數工作;他們使用指針參考我們想要操作的實際內存。

跟蹤動態分配的內存:庫函數malloccalloc,並realloc使我們能夠在運行時內存分配,並且所有三個指針返回到分配的內存(如C89,所有的三個回程void *)。例如,如果我們想在運行時分配的int數組:

int *p = NULL; 
size_t numItems; 

// get numItems; 

p = malloc(sizeof *p * numItems); 
if (p) 
{ 
    // do stuff with p[0] through p[numItems - 1]; 
} 

free(p); 

指針變量p包含的內存足夠大,新分配的塊的地址舉行numItems整數。我們可以通過解除引用p使用*運算符或[]下標運算符(*(p+i) == p[i])來訪問該內存。

那麼,爲什麼不只是聲明一個大小爲numItems的數組並且完成它呢?畢竟,作爲C99的,你可以使用一個可變長度的數組,其中大小不必直到運行時被稱爲:

// get numItems 

int p[numItems]; 

三個原因:第一,是沒有得到普遍支持VLA的,並作爲2011年標準,VLA支持現在是可選的;其次,聲明後我們不能改變數組的大小,而我們可以使用realloc來調整我們分配的內存塊的大小;最後,沃拉斯被限制都在可以使用它們以及如何大的他們可以是 - 如果你需要在運行時分配的記憶很多,最好通過malloc/calloc/realloc比VLAS做到這一點。

上指針運算快速注意:對於任何指針T *p,表達p+1將評估爲T類型的下一個元件的地址,這是不necessariy地址值+ 1,例如:

T  sizeof T  Original value of p p + 1 
-  --------  ------------------- ----- 
char   1     0x8000 0x8001 
int    4     0x8000 0x8004 
double   8     0x8000 0x8008 

構建動態數據結構:有些時候,我們希望將數據存儲在這樣一種方式,可以很容易地插入新元素到一個列表,或快速搜索的值,或強制訪問的特定順序倍。有許多用於這些目的的不同數據結構,幾乎所有情況下都使用指針。例如,我們可以使用一個二叉搜索樹組織以這樣的方式我們的數據搜索特定值是相當快的。樹中的每個節點有兩個孩子,分別指向到樹的下一個元素:

struct node { 
    T key; 
    Q data; 
    struct node *left; 
    struct node *right; 
}; 

leftright成員指向樹中的其他節點,或NULL如果沒有孩子。通常情況下,left child指向其值是某種當前節點的值「小於」的一個節點,而right child指向其值是某種當前節點「大於」的節點。我們可以搜索樹的值,像這樣:

int find(struct node *root, T key, Q *data) 
{ 
    int result = 0; 

    if (root == NULL)   // we've reached the bottom of the tree 
    {        // without finding anything 
    result = 0;     
    } 
    else if (root->key == key) // we've found the element we're looking for 
    { 
    *data = root->data; 
    result = 1; 
    } 
    else if (root->key < key) 
    { 
    // The input key is less than the current node's key, 
    // so we search the left subtree 
    result = find(root->left, key, data); 
    } 
    else 
    { 
    // The input key is greater than the current node's key, 
    // so we search the right subtree 
    result = find(root->right, key, data); 
    } 

    return result; 
} 

假設樹是平衡的(即,在左子樹的元素個數等於在右子樹的元素個數),則檢查元件的數目爲大約登錄 N,其中N是在樹中元素的總數量。