C语言--关于指针两种初始化赋值操作
首先,什么叫做指針的初始化??
int * p = NULL;在定義指針變量p的同時把p的值設置為0x00000000;而不是把*p的值設置為0x00000000。這個過程叫做初始化。
探討: int * p = & a; 和 int * p = &(int )0x0012ff60; 的含義和區別
(1)我們來看第一段代碼:
#include <stdio.h>
int main(void)
{
? ? int a = 0;
? ? int *p = &a;
? ? printf("The value is: %d/n", *p);
? ? return 0;
}
我們怎樣理解變量a呢?
一個變量具有一個變量名,對它賦值后就有一個變量值,變量名和變量值是兩個不同的概念:變量名對應于內存單元的地址,表示變量在內存中的位置,而變量值則是放在內存單元中的數據,也就是內存單元的內容。變量名對應于地址,變量值對應于內容,應以區別。?
? 例如定義一個整形變量int x,編譯器就會分配兩個存儲單元給x。如果給變量賦值,令x=30,這個值就會放入對應的存儲單元中。雖然這個地址是由編譯器分配的,但我們是無法事先確定的,但可以用取地址運算符&取出變量x的地址,例如取x變量的地址用&x。
我們怎樣理解&取地址運算符呢?
? 對于c語言中的&運算符,百度百科是這樣定義的:(&p)則是這樣一種運算,返回一個指針,該指針的值是當時聲明p 時開辟的地址,指針的類型是p的類型對應的指針類型。該指針是由編譯器分配,而不是由程序指定的,但指針值可以用&p取出的。
(2)我們來看第二段代碼
#include "stdio.h"
int main(void)?
{?
? ?// int a = 0; // &a = 0x0012ff60?
? ? int *p = (int*)0x0012ff60;?
? ? printf("The value is: %d/n", *p);?
? ? return 0;?
}
為什么不能直接用int *p = 0x0012ff60來完成初始化操作呢?
我們知道:賦值符號“=”左右兩邊數據類型應相同,在這里等號左右兩邊應同為指針類型。而0x0012ff60作為一個整形數據是不能直接賦值給指針類型的。
那么我們這么寫可以嗎?int *p = &0x01000;
這顯然不行。因為對于一個數值常量,它是沒有地址的。而變量之所以有地址就是因為要有一個存儲單元對變量進行標識(當然,變量也可以直接映射到某個寄存器)。
我們將0x0012ff60強制類型轉換(int*),這時它也變成一個指針類型,可完成賦值操作。
//*******************************************
c語言中通過指針將數值賦值到制定內存地址
1.一種直觀的方法
假設現在需要往內存0x12ff7c地址上存入一個整型數0x100。我們怎么才能做到呢?
我們知道可以通過一個指針向其指向的內存地址寫入數據,那么這里的內存地址0x12ff7c其本質不就是一個指針嘛。所以我們可以用下面的方法:
1 int *p = (int *)0x12ff7c; 2 *p = 0x100;需要注意的是將地址0x12ff7c賦值給指針變量p的時候必須強制轉換。
?
1.1 為什么在此處,我們敢往0x12ff7c這個地址賦值呢?
至于這里為什么選擇內存地址0x12ff7c,而不選擇別的地址,比如0xff00等。這僅僅是為了方便在Visual?C++ 6.0上測試而已。如果你選擇0xff00,也許在執行*p = 0x100;這條語句的時候,編譯器會報告一個內存訪問的錯誤,因為地址0xff00處的內存你可能并沒有權力去訪問。既然這樣,我們怎么知道一個內存地址是可以合法的被訪問呢?也就是說你怎么知道地址0x12ff7c處的內存是可以被訪問的呢?其實這很簡單,我們可以先定義一個變量i,比如:
int i = 0;變量i所處的內存肯定是可以被訪問的。然后在編譯器的watch窗口上觀察&i的值不就知道其內存地址了么?這里我得到的地址是0x12ff7c,僅此而已(不同的編譯器可能每次給變量i分配的內存地址不一樣,而剛好Visual C++ 6.0每次都一樣)。你完全可以給任意一個可以被合法訪問的地址賦值。得到這個地址后再把“int i = 0;”這句代碼刪除。一切“罪證”銷毀得一干二凈,簡直是做得天衣無縫。
?
2.另一個方法
?
除了這樣就沒有別的辦法了嗎?未必。我們甚至可以直接這么寫代碼:
*(int *)0x12ff7c = 0x100;//*****************************************
1、指針的初始化
?
指針初始化時,“=”的右操作數必須為內存中數據的地址,不能夠是變量,也不能夠直接用整型地址值(可是int*p=0;除外,該語句表示指針為空)。此時,*p僅僅是表示定義的是個指針變量,并沒有間接取值的意思。
比如:
int a = 25;
int *ptr = &a;
int b[10];
int *point = b; ??
int *p = &b[0];
?
假設:int ?*p;
? ? *p = 7;
則編譯器(vs2008)會提示The variable 'p' is being used without being initialized.即使用了未初始化的變量p。
由于p是指向7所在的地址,*p = 7給p所指向的內存賦值,p沒有賦值,所以p所指向的內存位置是隨機的,沒有初始化的。
int k;
int *p;
p = &k; ?//給p賦值
*p = 7; //給p所指向的內存賦值,即k= 7
?
2、指針的賦值
int *p;
int a;
int b[1];
p = &a;?
p = b;?
指針的賦值,“=”的左操作數能夠是*p,也能夠是p。
當“=”的左操作數是*p時,改變的是p所指向的地址存放的數據;
當“=”的左操作數是p時,改變的是p所指向的地址。
數組的變量名b表示該數組的首地址,因此p=b;也是正確的
?
同類型的指針賦值:
?
int val1 = 18,val2 = 19;
int *p1,*p2;
p1 = &val1;
p2 = &val2;
p1 = p2; ? //注意啦,p1指向了val2,而沒有指向val1
?
備注:字符串與指針的初始化和賦值
初始化:
char *cp = "abcdefg"; //這個初始化過程,是將指針cp指向字符串的首地址,而并非傳遞字符串的值。由于,在C語言里面,沒有總體處理一個字符串的機制
賦值:
cp = "abcdefg";
*cp=”abcdefg” ;//錯誤!字符串常量傳遞的是它的首地址,不能夠通過*cp改動該字符串的值,由于該字符串為常量,而它僅僅是簡單的將指針指向該字符串常量
?
3、指針常量
?
在C語言中沒有一種內建(built-in)的方法去表示指針常量,所以當我們使用它的時候通常先寫成整型常量的形式,然后再通過強制類型轉換把它轉換成對應的類型,如:int * , double * , char *等。?所以后面所看到的的做法是不行的:?int *p = 0x12345678 ;?正確的方式應為:int *p = (int *) 0x12345678; 要注意指針中僅僅能存放地址,不能將一個非0值整型常量表達式或者其它非地址類型的數據賦給一個指針,原因就在此。在大多數計算機中,內存地址確實是以無符號整型數來表示的,并且多以16進制表示,但我們在C語言中不能用整型數去表示地址,僅僅能用指針常量來表示,由于它是被用來賦給一個指針的。
對于這個賦值問題還能夠換一個角度去理解,在C語言中,使用賦值操作符時,賦值操作符左邊和右邊的表達式類型應該是同樣的,假設不是,賦值操作符將試圖把右邊表達式的值轉換為左邊的類型。所以假設寫出int *p = 0x12345678 ;?這條語句編譯器會報錯:'=' : cannot convert from ' const int ' to ' int * '?,由于賦值操作符左邊和右邊的表達式的類型應該同樣,而0x12345678是int型常量,p是一個指向int型的指針,兩者類型不同,所以正確的方式是:int *p = (int *) 0x12345678 ;?
?
4、指針初始化補充
?
ANSI C定義了零指針常量的概念:一個具有0值的整形常量表達式,或者此類表達式被強制轉換為void *類型,則稱為空指針常量,它能夠用來初始化或賦給不論什么類型的指針。也就是說,我們能夠將0、0L、'/0'、2–2、0*5以及(void *)0賦給一個不論什么類型的指針,此后這個指針就成為一個空指針,由系統保證空指針不指向不論什么對象或函數。
ANSI C還定義了一個宏NULL,用來表示空指針常量。大多數C語言的實現中NULL是採用后面這樣的方式定義的:#define??NULL??((void *)0)。
對指針進行初始化時經常使用的有下面幾種方式:
? 1.採用NULL或空指針常量,如:int *p = NULL;或?char *p = 2-2;?或float *p = 0;
? 2.取一個對象的地址然后賦給一個指針,如:int i = 3;??int *ip = &i;
? 3.將一個指針常量賦給一個指針,如:long *p = (long *)0xfffffff0;
? 4.將一個T類型數組的名字賦給一個同樣類型的指針,如:char ary[100]; char *cp = ary;
? 5.將一個指針的地址賦給一個指針,如:int i = 3;??int *ip = &i;int **pp = &ip;
? 6.將一個字符串常量賦給一個字符指針,如:char *cp = “abcdefg”;
對指針進行初始化或賦值的實質是將一個地址或同類型(或相兼容的類型)的指針賦給它,而無論這個地址是怎么取得的。要注意的是:對于一個不確定要指向何種類型的指針,在定義它之后最好把它初始化為NULL,并在解引用這個指針時對它進行檢驗,防止解引用空指針。另外,為程序中不論什么新創建的變量提供一個合法的初始值是一個好習慣,它能夠幫你避免一些不必要的麻煩。
?
5、void *型指針
?
ANSI C定義了一種void *型指針,表示定義一個指針,但不指定它指向何種類型的數據。void *型指針作為一種通用的指針,能夠和其他不論什么類型的指針(函數指針除外)相互轉化而不須要類型強制轉換,但不能對它進行解引用及下標操作。C語言中的malloc函數的返回值就是一個void *型指針,我們能夠把它直接賦給一個其他類型的指針,但從安全的編程風格角度以及兼容性上講,不妨將返回的指針強制轉換為所需的類型,另外,malloc在無法滿足請求時會通過返回一個空指針來作為“內存分配失敗”的信號,所以要注意返回值指針的判空。
?
6、指向指針的指針
?
在指針初始化的第5種方式中提到了用一個指針的地址來初始化一個指針。回顧一下上一講的內容:指針是一種變量,它也有自己的地址,所以它本身也是可用指針指向的對象。我們能夠將指針的地址存放在還有一個指針中,如:
int i = 5000;
int *pi = &i;
int **ppi = π
此時的ppi即是一個指向指針的指針,下圖表示了這些對象:
?? ? ? ? ? ? ? ? ? ? ?? ??
i的地址為108,pi的內容就是i的地址,而pi的地址為104,ppi的內容即是pi的地址。對ppi解引用照常會得到ppi所指的對象,所獲得的對象是指向int型變量的指針pi。想要真正地訪問到i.,必須對ppi進行兩次解引用,如以下代碼所看到的:
printf("%d", i );
printf("%d", *pi );
printf("%d", **ppi );
以上三條語句的輸出均為5000。
?
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的C语言--关于指针两种初始化赋值操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决:Chrome 插件安装时提示 程序
- 下一篇: 注解 @ModelAttribute 运