CPP指針(1)

指針

指針的概念

  • 地址
    操作系統根據程序中定義變量的類型,給變量分配一定的內存空間。字符型佔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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章