2017-06-14 48 views
6

正在關注yesterday's question,我試了一些更多的指針。具體類型爲int(*)[N]爲什麼我不能在這個指針上打印這個值?

這裏的指針是一些代碼,我寫道:

#include <stdio.h> 

int main(void) 
{ 
    int a[5] = {1, 2, 3, 4, 5}; 

    int (*p) [5] = &a; 
    int *q = a; 

    printf("\t\t\t\t\tp\t\tq\n\n"); 
    printf("Original Pointers: \t%20d%20d", p, q); 
    printf("\n\n"); 
    printf("Incremented Pointers:\t%20d%20d", p+1, q+1); 
    printf("\n\n"); 
    printf("Pointer values:   %20d%20d", *p, *q); 
    printf("\n\n"); 

    return 0; 
} 

這裏是它的輸出:

        p     q 

Original Pointers:    132021776   132021776 

Incremented Pointers:   132021796   132021780 

Pointer values:    132021776     1 
  • 指針p,通過跳躍20時遞增。這是因爲它是int(*)[5]類型的指針,因此在數組中跳到sizeof(int) * number of columns

  • 兩個pq使用與p間接運算符時的值相同(但不同類型的),然而,我不會在數組的第一個單元格中獲得的價值,而不是我得到的值p本身打印出來。爲什麼是這樣?當我使用int *p = &a時,它引發了一個警告(由於不同類型的指針,但後來當我使用間接運算符與p,我可以得到它打印數組中的第一個單元格的值)。這是因爲當我給你&ap,其轉換的&arr的類型int *類型(這是int (*) [5]),然後分配給p

+1

你的代碼是未定義行爲;你必須使用'%p'指定符打印地址,並將參數轉換爲'void *'。 –

+0

注意:不要使用'%d'來打印指針,'int'對於指針來說可能太小(並且它在典型的64位系統上是*) –

+0

對於簡單的分配約束違規,gcc給出了很差的診斷。確保使用'gcc -std = c11 -pedantic-errors'進行編譯,以使其正常運行。 – Lundin

回答

4

指針p在增加時跳躍20。這是因爲它是int(*)[5]類型的指針,因此按數組中的sizeof(int)*列數跳轉?

是。使用具有p間接運算符時

p和q兩者具有相同的值(但不同類型的),然而,我並不在所述陣列的所述第一小區中獲得的價值,而不是我得到的值OF打印出來。爲什麼是這樣 ?

p指向一個數組,因此使用*p,您可以訪問此數組。在沒有索引的情況下評估一個數組會得到一個指向其第一個元素的指針。您的*p在此處評估爲類型int *

附註中,*通常被稱爲解除引用運算符。使用不同的術語可能會讓其他人感到困惑。

當我使用int * p = &一個,它拋出一個警告(因爲不同類型的指針,但後來當我使用與對間接運算符,我可以得到它打印的價值陣列中的第一個單元。這是因爲當我分配& a至p,將其轉換的& ARR類型(其爲int(*)[5])與int類型*,然後分配至p?

我有一個答案在這裏誤解這個像int *q = (int *)p這將創建一個別名指針,並可能undefined行爲,所以留下這個小提示以防萬一有人會想到這個的想法。但是你在這個問題上提出的建議只是一個無效的任務。 Lundin's answer有一個完整的解釋。


有在你的代碼更多的問題,您的指針印刷應該像:

printf("Original Pointers: \t%20p%20p", (void *)p, (void *)q); 

指針可以比int更大。需要投射到void *是因爲printf()是一個可變參數函數。如果聲明爲void *,轉換將是隱含的,但事實並非如此,您必須自己做。

+0

我正在從K.N.King的書(C編程:現代方法)學習C,它調用*'間接'操作符。 – Nathu

+0

那麼,它不會在C標準有明確的名稱,並描述爲「*一元\ *運算符表示間接。*「,所以*有一些使用該名稱的理由,但我仍然認爲它是一個壞主意,因爲它執行的操作通常稱爲*指針解除引用*,因此大多數C程序員將它稱爲* dereference * –

+0

@NathuramGoatse仍然我會改寫我的提示,因爲*間接*不是*錯誤* –

3

你的代碼會導致undefined behavior,所以沒有輸出能驗證。

首先,一些通用信息

按照標準,使用不匹配類型的參數與格式說明導致UB。要使用printf()打印指針,必須使用%p格式說明符並將相應的參數轉換爲void *

相關,引用C11,章§7.21.6.1/ P8

p

的參數應是指向void。 [...]

和P9,

[....]如果任何參數是 類型不正確的相應的轉換說明書中,行爲是 未定義。

由於printf()是一個可變參數函數,並沒有默認參數提升開始,中投以void *是必要的。


現在,來到更直接的問題。

§1。指針p,增加時跳轉20。 [...]

你說得對,檢查數據類型。類型爲int (*) [5]的指針將根據您的平臺增加/減少指向類型sizeof(int [5])。指針算術榮譽datd類型

§2。兩者pq當使用間接運算符與`P [....]

請注意,類型存在具有相同的值(但不同類型),和愛好。 p的類型是int (*) [5],因此*p的類型是int [5]。而已。所有你應該有一個數組作爲解引用的產物。 (但是閱讀.....)現在

,同時使陣列型作爲函數參數,它衰減到指針的第一個元素的陣列,因此是類似於int *,指針。所以,最終你會打印一個指針值。

§3。當我使用int *p = &a,它拋出一個警告[...]

等待。停止。這是違反約束的。嚴格來說,這是無效的C代碼。類型int *int (*) [5]不兼容,並且不存在使其成爲有效表達式的轉換(隱式或顯式)。不要這樣做,使用適當的類型。

3

除了答案由菲利克斯:

當我使用int *p = &a,它拋出一個警告

這是因爲這段代碼是無效C.它是一個所謂的違反約束(大致意味着嚴重的語言違規)。所以編譯器需要提供診斷信息。更好的編譯器會給出錯誤,而不是警告。

沒有指針轉換髮生。除非其中一個操作數是void*,否則指針轉換不會隱含在C中。


它無效的原因C是因爲該表達式不是有效的簡單賦值形式。有效形式列在C11 6.5.16.1:

6.5.16.1簡單賦值
約束

下列情況之一的應持有:

- 左操作具有原子,合格,不合格或算術類型,右邊有 算術類型;

不是這裏的情況,兩個操作數都是指針。

- 左操作數具有與右側類型兼容的結構或聯合類型的原子,限定或非限定版本;

不是這裏的情況。

- 左操作數具有原子,合格,或不合格的指針類型;以及(考慮類型左操作數將具有左值變換後)兩個操作數都指向兼容的類型的合格或不合格的版本,類型指向左側的所有限定符都是右側指向的類型的限定符;

左操作數是(非限定)指針類型。但是右操作數不是兼容類型。所以這是不符合條件的。

- 左操作數具有原子,合格,或不合格的指針類型;以及(考慮 左操作數將具有左值轉換後的類型)一個操作數是一個指針 到的對象類型,而另一個是一個指針 空隙的合格或不合格的版本和類型指向左邊有指向 由右側的類型的所有的限定符;

不,這裏沒有void指針。

- 左操作數是原子,限定或非限定指針,右是空指針常量;或

沒有空指針常量無論是。

- 左操作數具有原子型,限定型或非限定型_Bool,右邊是一個指針。

這裏也沒有布爾。

+0

在違反約束的情況下,行爲是未定義的(因爲代碼儘管如此)? – Nathu

+0

@NathuramGoatse是的,這樣的程序完全沒有被C標準定義,因爲它不再是C程序,而是其他的東西。 – Lundin

相關問題