[做題記錄-亂做] [AGC003E] Sequential operations on Sequence
阿新 • • 發佈:2021-10-02
1.取地址運算
- scanf("%d",&i);裡的&
- 獲得變數的地址,它的運算元必須是變數
- int i; printf("%x",&i);
注意:
- 獲取地址時,用%p來獲取
- int i; printf("%p",&i);
- 地址的大小是否與int相同取決於編譯器
#include <stdio.h> int main() { int a[10]; printf("%p\n",&a); printf("%p\n",a); printf("%p\n",&a[0]); printf("%p\n",&a[1]); return 0; } 000000000062FDF0 000000000062FDF0 000000000062FDF0 000000000062FDF4
1.1 &不能取的地址
&不能對沒有的東西取地址
比如:
- &(a+b)
- &(a++)
- &(++a)
2.指標
定義:就是儲存地址的變數
int i;
int* p=&i;//這個p指向的是個int,把i的地址交給了這個p,這個*p只會有別的變數的地址
int* p,q;//q只是個普通的b
int *p,q;
- 變數的值是記憶體的地址
- 普通變數的值是實際的值
- 指標變數的值是具有實際值的變數的地址
2.1 作為引數的指標
#include <stdio.h> void f(int *p); void g(int k); int main() { int i = 6; printf("&i=%p\n",&i); f(&i); g(i); return 0; } void f(int *p) { printf("p=%p\n",p); } void g(int k) { printf("k=%d\n",k);//得到的只是i的值 }
-
void f(int *p);
-
在被呼叫的時候得到了某個變數的地址;
-
int i=0; f(&i);
-
在函式裡面可以通過這個指標訪問外面的這個i
2.2 訪問那個地址上的變數*
是一個單目運算子,用來訪問指標的值所表示的地址上的變數*
可以做右值也可以做左值
- int k= *p;
- *p = k+1;
#include <stdio.h> void f(int *p); void g(int k); int main() { int i = 6; f(&i); g(i); return 0; } void f(int *p) { printf("p=%p\n",p); printf("*p=%d\n",*p); *p = 26;//可以通過這種方式訪問外面的變數,可以修改,訪問 } void g(int k) { printf("k=%d\n",k);//得到的只是i的值 }
2.3 左值之所以叫左值
是因為出現在賦值好左邊的不是變數,而是值,是表示式計算的結果(運算的結果):
- a[0]=2;
- *p=3
- 是特殊的值,所以叫做左值
2.4 指標的運算子&*
- 互相反作用
- *&yptr -> *(&yptr) -> *(yptr的地址) -> 得到那個地址上的變數 -> yptr
- &*yptr -> &( *yptr) -> &(y) -> 得到y的地址,也就是yptr -> yptr
3.指標的使用
3.1 指標的應用場景一
-
交換兩個變數的值(函式)
#include <stdio.h> void swap(int *pa,int *pb); int main() { int a=5; int b=6; printf("a=%d,b=%d \n",a,b); swap(&a,&b); printf("a=%d,b=%d",a,b); return 0; } void swap(int *pa,int *pb) { int t=*pa; *pa=*pb; *pb=t; } //a=5,b=6 //a=6,b=5
3.2 指標的應用場景二
- 函式返回多個值,某些值就只能通過指標返回
- 傳入的引數實際上是是需要儲存帶回的結果的變數
#include <stdio.h>
void minmax(int a[],int len,int *min,int *max);
int main(void)
{
int a[]={1,2,3,4,5,6,7,8,9,10};
int min,max;
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d \n",min,max);
return 0;
}
void minmax(int a[],int len,int *min,int *max)
{
int i;
*min =*max =a[0];
for(i=1;i<len;i++){
if(*min>a[i]){
*min=a[i];
}
if(*max<a[i]){
*max=a[i];
}
}
}
應用場景二b(運算出錯)
-
函式返回運算的狀態,結果通過指標返回
-
常用的套路是讓函式返回特殊的不屬於有效範圍內的值來表示出錯
- -1或0(在檔案操作會看到大量的例子)
-
但是當任何數值都是有效的可能結果時,就得分開返回
#include <stdio.h> /*如果除法成功,返回1;否者返回0 */ int divide(int a,int b,int *result); int main() { int a=5; int b=2; int c;//c是a/b的結果 if( divide(a,b,&c)){ printf("%d/%d=%d",a,b,c); } return 0; } int divide(int a,int b,int *result) { int ret =1; if( b==0)ret=0;//除數不能是0 else{ *result=a/b; } return ret; }
4.指標最常見的錯誤
- 定義了指標變數,還沒有指向任何變數,就開始使用
5.傳入函式的陣列成了什麼?
函式引數表中的陣列實際上是指標
- sizeof(a) == sizeof(int*)
- 但是可以用陣列的運算子[]進行運算
#include <stdio.h>
void minmax(int *a,int len,int *max,int *min);
int main(void)
{
int a[] ={1,2,3,4,5,6,7,8,9,10};
int min,max;
printf("minmax sizeof(a)=%lu\n",sizeof(a));
printf("main a=%p\n",a);
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
printf("a[0]=%d\n",a[0]);
printf("min=%d,max=%d\n",min,max);
return 0;
}
void minmax(int *a,int len,int *max,int *min)
{
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));//接收到的是首地址
printf("main a=%p\n",a);
a[0]=1000;
for ( i=1;i<len;i++){
if( a[i]< *min){
*min = a[i];
}
if( a[i]> *max){
*max = a[i];
}
}
}
minmax sizeof(a)=40
main a=000000000062FDF0
minmax sizeof(a)=8
main a=000000000062FDF0
a[0]=1000
min=10,max=0
5.1 陣列的變數是特殊的指標
-
陣列變數本身表達地址,所以不需要加
- int a[10];int *p=a;//無需用&取地址
- 但是陣列的單元表達的是變數,需要用&去地址
- a == &a[0]
-
[]運算子可以對陣列做,也可以對指標做:
- p[0] <==> a[0]
-
*運算子可以對指標做,(取出所指變數例的值),也可以對陣列做
- *a =25;
-
陣列變數是const的指標,所以不能被賦值
- int a[] <==> int * const a=...
6.指標與const
6.1 如果q是const
- 表示q的值(即i的地址)不能被改變,就是說不能再指向別人了
- int *const q =&i; //q是const
- *q=26;//ok
- q++;//ERROR
6.2 如果是const int
- 表示
- const int *p =&i;
- p = 26;//ERROR! ( *P)是const
- i = 26;
- p =&j;
在前面就代表int不能被修改,在後面表示指標不能被修改。
6.3 轉換
-
總是可以把一個非const的值轉換成const
void f(const int* x); int a=15; f(&a);//ok const int b=1; f(&b);//ok b = a+1;//Error!
-
當要傳遞的引數的型別比地址大的時候,這是常用的手段:既能用比較少的位元組數傳遞值給引數,又能避免函式對外面的變數的修改
6.4 const陣列
- const int a[]={1,2,3,4,5,6};
- 陣列變數已經全是const的指標了,這裡的const表明陣列的每個單元都是const
- 所以必須通過初始化進行賦值
保護陣列值
- 因為把陣列傳入函式時傳遞的是地址,所以那個函式內部可以修改陣列的值
- 為了保護陣列不被函式破壞
- int sum(const int a[],int length);
7.指標運算
這些算術運算可以對指標做:
- 給指標加、減一個整數(+,+=,-,-=)
- 遞增遞減(++/--)
- 兩個指標相減,可以得到兩個地址差了幾個單元(地址相減/sizeof(資料型別))
7.1 *p++
- 取出p所指的那個資料來,完事以後順便把p移到下個位置去
- *的優先順序雖然高,但是沒有++搞
- 常用於資料類的連續空間操作
- 在某些CPU上,這可以直接被翻譯成一條彙編指令
7.2 指標比較
- <,<=,==,>,>=,!= 都可以對指標做
- 比較它們在記憶體中的地址
- 陣列中的單元的地址肯定是線性遞增
7.3 0地址
-
當然你的記憶體中有0地址,但是0地址通常是個不能隨便碰的地址
-
所以你的指標不應該具有0值
-
因此可以用0地址來表示特殊的事情
- 返回的指標是無效的
- 指標沒有被真正初始化(先初始化為0)
-
NULL是一個預定定義的符號
- 有的編譯器不願意你來用0來表示0的地址
8. 指標的型別
- 無論指向什麼型別, 所有的指標的大小都是一樣的,因為都是地址
- 但是指向不同型別的指標是不能直接互相賦值的
- 這是為了避免用錯指標
9. 指標的型別轉換
- void*表示不知道指向什麼東西的指標
- 計算時與char*相同(但不通)
- 指標也可以轉換型別
- int *p = &i; void *q =(void *) p;
- 這並沒有改變p所指的變數的型別,而是讓後人用不同眼光通過p看它所指的變數
- 我不再當你是int啦,我認為你就是個void!
10. 總結
- 需要傳入較大的資料時用作引數
- 傳入陣列後對陣列做操作
- 函式返回不止一個結果
- 需要用函式來修改不止一個變數
- 動態申請記憶體時