指針
指針的概念
-
地址
操作系統根據程序中定義變量的類型,給變量分配一定的內存空間。字符型佔1個字節,整型數佔4個字節…。內存區的每個字節都有編號,稱之爲地址。 -
變量的指針
變量的指針就是變量的地址,當變量定義後,其指針(地址)是一常量。 -
&
取地址運算符 -
*
- 用途①
用於聲明指針變量的時候。 - 用途②
用於間接訪問變量。
- 用途①
-
指針變量
指針變量是一個變量,專門用來存放另一變量的地址(指針)。在編譯時也分配一定字節的存儲單元,未賦初值時,該存儲單元內的值是隨機的。- 一個指針變量只能指向同一類型的變量。
- 指針變量只能存放地址,不要將非地址數據賦給指針變量
int i;
int *pi;
pi = &i;
……
//or
int *pi = &i;
// *pi 等價於 i,訪問*pi 就相當於訪問 i,修改*pi就相當於修改i;
int *p = 100; //非法!!!
- 一個極其危險的操作
int *p1;
*p1 = 100;
//不知道p1控制哪塊內存,就去修改值,極其危險!!!
- 用指針交換兩個整數
void mySwap(int *a,int *b)
{
int t = *a;
*a = *b;
*b = t;
}
int main()
{
int x = 0, y = 1;
mySwap(&x, &y);
cout<<x<<endl;
cout<<y<<endl;
}
指針運算符的優先級
*
與++
一起運算
①
int a = 3,*p;
p = &a ;
cout<<"指針 "<<p<<endl;
int b = (*p)++;
cout<<"指針 "<<p<<endl;
cout<<"a "<<a<<endl;
cout<<"b "<<b<<endl;
相當於 int b = a++;
指針 0x7ffeefbff62c
指針 0x7ffeefbff62c
a 4
b 3
②
int a = 3,*p;
p = &a ;
cout<<"指針 "<<p<<endl;
int b = *p++;
cout<<"指針 "<<p<<endl;
cout<<"a "<<a<<endl;
cout<<"b "<<b<<endl;
++ 後置,是指針++,即指針的類型對應的存儲空間
(int * p ;p++;p右移四個字節)
指針 0x7ffeefbff62c
指針 0x7ffeefbff630
a 3
b 3
有運算結果發現,是先*p,後賦值運算,最後p=p+1;
③
int a = 3,*p;
p = &a ;
cout<<"指針 "<<p<<endl;
int b = *(p++);
cout<<"指針 "<<p<<endl;
cout<<"a "<<a<<endl;
cout<<"b "<<b<<endl;
}
指針 0x7ffeefbff62c
指針 0x7ffeefbff630
a 3
b 3
注意下面兩種寫法等價!!!
int b = *(p++);
int b = *p++;
但是
int a = 3,*p;
p = &a ;
cout<<"指針 "<<p<<endl;
p++;
int b = *p;
cout<<"指針 "<<p<<endl;
cout<<"a "<<a<<endl;
cout<<"b "<<b<<endl;
指針 0x7ffeefbff62c
指針 0x7ffeefbff630
a 3
b -272632248
也就是說下面兩種寫法不等價!!!
// * 號仍然與之前p結合
①
int b = *(p++);
②
p++;
int b = *p;
④
int a = 3,*p;
p = &a ;
cout<<"指針 "<<p<<endl;
int b = ++*p;
cout<<"指針 "<<p<<endl;
cout<<"a "<<a<<endl;
cout<<"b "<<b<<endl;
指針 0x7ffeefbff62c
指針 0x7ffeefbff62c
a 4
b 4
這種情況滿足右結合,也就是說下面的三種寫法等價。
int a,*p
p = &a ;
int b = ++*p;
int b = ++(*p);
int b = ++a;
int a = 3,*p;
p = &a ;
cout<<"指針 "<<p<<endl;
int b = ++(*p);
cout<<"指針 "<<p<<endl;
cout<<"a "<<a<<endl;
cout<<"b "<<b<<endl;
指針 0x7ffeefbff62c
指針 0x7ffeefbff62c
a 4
b 4
⑤
int a[] = {3,4},*p;
p = &a[0] ;
cout<<"指針 "<<p<<endl;
int b = *++p;
cout<<"指針 "<<p<<endl;
cout<<"a[0] "<<a[0]<<endl;
cout<<"a[1] "<<a[1]<<endl;
cout<<"b "<<b<<endl;
指針 0x7ffeefbff620
指針 0x7ffeefbff624
a[0] 3
a[1] 4
b 4
一樣是右結合,下面四種寫法等價
①
int b = *++p;
②
int b = *(++p);
③
++p;
int b = *p;
④
p++;
int b = *p;
int a[] = {3,4},*p;
p = &a[0] ;
cout<<"指針 "<<p<<endl;
// p++;
++p;
int b = *p;
cout<<"指針 "<<p<<endl;
cout<<"a[0] "<<a[0]<<endl;
cout<<"a[1] "<<a[1]<<endl;
cout<<"b "<<b<<endl;
指針 0x7ffeefbff620
指針 0x7ffeefbff624
a[0] 3
a[1] 4
b 4
- 指針變量作爲函數參數:
-
函數的參數可以是指針類型,它的作用是將一個變量的地址傳送到另一個函數中。
-
指針變量作爲函數參數與變量作函數參數不同:
變量作函數參數傳遞的是具體值
指針作函數參數傳遞的是地址。 -
函數調用不能改變實參指針變量的值,但可以改變實參指針變量所指向變量的值。
-
void twoTimes(int *b)
{
// b是p的傀儡,但b確實擁有a的地址
(*b)*=2;
b++;
}
int main()
{
int a = 10;
int *p = &a;
cout<<"p "<<p<<endl;
twoTimes(p);
cout<<"p "<<p<<endl;
cout<<"a "<<a<<endl;
return 0;
}
p沒變,p指向的a變了。
p 0x7ffeefbff628
p 0x7ffeefbff628
a 20
- 虛實結合中採取單向的“值傳遞”方式,只能從實參向形參傳數據,形參值的改變無法回傳給實參。不論是指針變量,還是普通變量。
void mySwap(int *a,int *b)
{
int *t=a;a=b;b=t;
}
int main()
{
int a=0,b=1;
int *pa = &a,*pb = &b;
cout<<pa<<endl;
cout<<pb<<endl;
mySwap(pa, pb);
cout<<pa<<endl;
cout<<pb<<endl;
return 0;
}
0x7ffeefbff628
0x7ffeefbff624
0x7ffeefbff628
0x7ffeefbff624
數組與指針
- 概念
數組與變量一樣,在內存中佔據內存單元,有地址,一樣可以用指針來表示。C++規定:
數組名就是數組的起始地址;
數組的指針就是數組的起始地址;
數組元素的指針就是數組元素的地址。
int a[10]
//a 、&a[0]就是數組a的指針
- p+1
C++規定,p+1指向數組的下一個元素,而不是下一個字節
int a[10];
int *p =a;
*p = 1;
*++p = 2;
cout<<a[0]<<endl;
cout<<a[1]<<endl;
1
2
int a[10];
int *p =a;
*p = 1;
*++p = 2;
// *++a = 2; 錯誤!!!
cout<<a[0]<<endl;
cout<<a[1]<<endl;
-
p+i 或 a+i 均表示 a[i] 的地址 &a[i]
-
引用一個數組的元素有四種方式
(1)下標法,如a[i]或p[i]形式;
(2)指針法,如*(a+i)或*(p+i) -
用指向數組的指針變量輸出數組的全部元素
int main() {
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
for (int *p = a; p < (a + 10); p++)
cout << *p << " ";
cout << endl;
return 0;
}
int main(void)
{ int a[ ]={1,2,3};
int s, i, *p;
s=1; p=a;
for (i=0; i<3; i++)
s*=*(p+i);
// s*=*(p+i); s = s*(a[i]);
cout<<s<<endl;
}
- 指針變量作函數參數,接收數組地址
在函數調用時,形參數組並沒有另外開闢新的存儲單元,而是以實參數組的首地址作爲形參數組的首地址。這樣形參數組的元素值發生了變化,也就使實參數組的元素值發生了變化。
實參與形參的結合有以下4種形式:
實 參 形 參
數組名 數組名
數組名 指針變量
指針變量 數組名
指針變量 指針變量
這四種形式都是可行的,區別在於:
形參數組名是指針變量時,它並不是一個固定的地址值,它的值可以改變。
void f(int a[])
int main()
{
int a[10];
……
f(a);
}
- 將數組中的n個數按相反順序存放。
void inv(int *x,int n)
{
int *p,t,*i,*j,m = (n-1)/2;
p = x+m;
i = x;
j = x+n-1;
for(;i<p;i++,j--)
{
t=*i;*i=*j;*j=t;
}
}
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,0};
inv(a, 10);
for(int x:a)
{
cout<<x<<endl;
}
return 0;
}
- 將字符串中奇數位上的小寫字母轉化爲大寫字母。
void change(char* pstr)
{
while(1)
{
if(*pstr>='a'&& *pstr<='z')
{
*pstr -= 32;
}
else if(*pstr=='\0')
{
return;
}
pstr++;
if(*pstr=='\0')
{
return;
}
pstr++;
}
}
int main()
{
char str[100];
cin>>str;
change(str);
cout<<str<<endl;
return 0;
}
字符串與指針
① 用字符數組實現字符串
char str[100] = "china";
char str2[100] = {'c','h','i','n','a','\0'};
cout<<str<<endl;
cout<<str2<<endl;
str是數組名,代表數組的首地址,是常量
故而不能改變。只能在聲明時賦值。
char str[10];
str = "ok"; //常量賦值,違法
strcpy(str, "ok");
②用字符串變量(string)存放字符串
cin.getline(char *buf,int bufSize)
//從輸入流中讀取bufSize-1個字符到緩衝區buf,或讀到碰到‘\n’爲止(哪個先到算哪個) 。
cin.getline(char *buf,int bufSize,char delim)
//從輸入流中讀取bufSize-1個字符到緩衝區buf,或讀到碰到delim字符爲止(哪個先到算哪個) 。
注意:如果輸入流中‘\n’或delim之前的字符個數達到或超過了bufSize個,雖然本次讀入已經完成,但是之後的讀入會收到影響,導致下次讀入錯誤字符。
std::getline(std::cin,string str)
//demo:
string str;
getline(cin,str);
cout<<str<<endl;
總結:
cin.getline(char* buf,int bufSize,char delim)
適合知道讀入大小,固定char數組,速度快
std::getline(cin,string str) 不需要指定大小,速度慢、編譯生成的文件大
一個注意點:
當同時使用cin>>, getline()時,注意在cin>>輸入流完成之後、getline()之前,需要將回車符作爲輸入流, 以清除緩存。
錯誤示範:
int main()
{
int x;
string str;
cin>>x;
getline(cin,str);
cout<<x<<endl;
cout<<str<<endl;
return 0;
}
//從鍵盤讀取10
10
還沒有來得及輸入字符串,就結束了運行,並且輸出多了一行空格,
原因就是getline()函數讀取了10後面的空格。
解決辦法就是加一行
int main()
{
int x;
string str;
cin>>x;
getline(cin,str);//用於吸收'\n'
getline(cin,str);
cout<<x<<endl;
cout<<str<<endl;
return 0;
}
③ 用字符指針表示字符串
將內存中字符串常量的首地址賦給一個指針變量
char *str = "china";
cout<<str<<endl;
//危險操作,指針未賦值就作指向運算, danger!
char *string1;
cin.getline(string1,20);
- 實現
strcpy()
版本一
void myStrcopy(char *b,char *a)
{
int i;
for(i=0;*(a+i)!='\0';i++)
{
*(b+i)=*(a+i);
}
*(b+i) = '\0';
}
int main()
{
char a[] = "china";
char b[100];
myStrcopy(b, a);
cout<<a<<endl;
cout<<b<<endl;
return 0;
}
版本2
void myStrcopy(char *b,char *a)
{
char *pa=a,*pb=b;
for(;*pa!='\0';pa++,pb++)
{
*pb = *pa;
}
*pb = '\0';
}
版本3
void myStrcopy(char *b,char *a)
{
char *pa=a,*pb=b;
for(;*pa!='\0';)
{
*pb++= *pa++;
}
*pb = '\0';
}
- 小插曲
int x = 10;
while(--x){
}
cout<<x<<endl;
//輸出0
while(x--){
}
cout<<x<<endl;
//輸出-1;
版本4
void myStrcopy(char *b,char *a)
{
char *pa=a,*pb=b;
while(*pb++=*pa++);
}
-
字符指針變量與字符數組的區別
字符數組和字符指針變量都可以實現字符串的存儲和運算,區別在於:- 字符數組名是常量,定義時必須指明佔用的空間大小。
- 字符指針變量是變量,裏面存儲的是字符型地址,可以整體賦值,但字符串必須以‘\0’結尾。
demo:
char amessage[] = "hello";
char *pmessage = "hello";
① amessage是一個僅僅足以存放初始化字符串以及空字符‘\0’的一維數組。數組中的單個字符可以進行修改,amessage始終指向同一個存儲位置。
amessage是這個字符數組的地址,也就是這個數組的指針,它相當於是個指向變量的常指針。
②pmessage是一個指針,其初值指向一個字符串常量,之後它可以被修改以指向其它地址,但不能通過它修改字符串的內容。
它相當於是指向常變量的指針
1.字符串直接量(string literal)
cout<<“hello”<<endl;將“hello”字符串輸出,沒有使用字符串變量。
C++中"hello"的類型是const char[6]
C中"hello"類型是char[6]
2 字符串直接量可以賦值給指針變量,但是與字符串直接量相關聯的內存空間是隻讀的,是常量字符數組。
在運行時出錯!!!
p[1] = 'a';
那麼如何消除上面的警告?const char*p = "hello";
這樣就可以了。
將字符串直接量賦值給字符數組的初始值。編譯器會將字符串直接量複製到數組在棧內存中,可以進行相應的修改。
char stackArray[] = "hello";
stackArray[1] = 'a';
但不允許整體修改!
- 實現字符串比較函數 int strcmp(char *s, char *t)。
int myStrmp(char *a,char *b)
{
int res=0;
char *pa = a;
char *pb = b;
while(1)
{
if(!*pa && !*pb){
return 0;
}
else if(!*pa || !*pb)
{
return *pa-*pb;
}
else if(*pa != *pb)
{
return *pa-*pb;
}
else{
pa++;
pb++;
}
}
return res;
}
- C++中cout輸出字符型指針地址值的方法
const char* a = "abc";
cout<<a<<endl;
cout<<*a<<endl;
abc
a
以上兩種都沒有輸出地址。
const char* a = "abc";
printf("%s\n",a);
printf("%p\n",a);
abc
0x100001f58
這是c裏面的做法,達成我們的目標。
如何用cout
輸出指針呢?
C++標準庫中I/O類對<<操作符重載,在遇到字符型指針時會將其當作字符串名來處理,輸出指針所指的字符串。
要輸出地址,得用到強制類型轉換static_cast來實現,把字符串指針轉換成無類型指針。
const char* a = "abc";
cout<<a<<endl;
cout<<static_cast<const void*>(a)<<endl;
abc
0x100001f54