終于到了精髓的地方了,這確實有點懵,總感覺這太麻煩了,而且寫著也不爽,還是懷念py或者java,但也沒辦法,還是要繼續學下去,
一、運算子&
- scanf("%d" , &i); 里的&
- 獲取變數的地址,它的運算元必須是變數
- 地址的大小是否與int相同取決于編譯器
#include <stdio.h>
int main(void)
{
int i = 0;
printf("0x%x\n", &i);
// 0x62fe4c
return 0;
}
&不能取的地址
&不能對沒有地址的東西取地址
- &(a+b) ?
- &(a++) ?
二、指標
- 如果能夠將獲得變數的地址傳遞個一個函式,能否通過這個地址在那個函式內訪問這個變數?
- scanf("%d" , &i);
- scanf()的原型應該是怎樣的?我們需要一個引數保存別的變數的地址,如何表達能夠保存地址的變數
指標
就是保存地址的變數 , *p
// p是一個指標,現在把i的地址交給了p
int i;
int* p = &i;
// 下面兩種形式一樣,p是一個指標,而q是一個普通的int變數
int* p , q;
int *p , q;
指標變數
- 變數的值是記憶體的地址
- 普通變數的值是實際的值
- 指標變數的值是具有實際值的變數的地址
作為引數的指標
- void f(int *p);
- 在被呼叫的時候得到了某個變數的地址
- int i = 0;f(&i);
- 在函式里面可以通過這個指標訪問到外面的的這個i
#include <stdio.h>
void f(int *p);
int main(void)
{
int i = 6;
printf("&i=%p\n", &i);
f(&i);
return 0;
}
void f(int *p)
{
printf(" p=%p\n", p);
}
// 可以看到這里獲取的地址是相同的
// &i=000000000062FE4C
// p=000000000062FE4C
訪問那個地址上的變數*
- *是一個單目運算子,用來訪問指標的值所表示的地址上的變數
- 可以是右值也可以是左值
- int k = *p;
- *p = k + 1;
*左值之所以叫左值
- 是因為出現在賦值號左邊的不是變數,而是值,是運算式計算的結果
- a[0] = 2;
- *p = 3;
- 是特殊的值,所以叫左值
#include <stdio.h>
// 宣告兩個函式
void f(int *p);
void g(int k);
int main(void)
{
int i = 6;
printf("&i=%p\n", &i);
f(&i);
// 此時i的值已經發生了變化
g(i);
return 0;
}
// 傳入的是地址
void f(int *p)
{
printf(" p=%p\n", p);
printf("*p=%d\n", *p);
*p = 66;
}
// 傳入的普通int
void g(int k){
printf("k=%d\n", k);
}
// &i=000000000062FE4C
// p=000000000062FE4C
// *p=6
// k=66
三、指標的使用
指標應用場景一
交換兩個變數的值
#include <stdio.h>
void swap(int *pa , int *pb);
int main()
{
int a = 5;
int b = 10;
swap(&a , &b);
printf("a=%d , b=%d\n", a , b);
return 0;
}
void swap(int *pa , int *pb){
int t = *pa;
*pa = *pb;
*pb = t;
}
指標應用場景二
- 函式回傳多個值,某些值就只能通過指標回傳
- 傳入的引數實際上是需要保存帶回的結果的變數
#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,12,13,14,15,34,35,66,};
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 = 0; i < len; i++)
{
if (a[i] > *max)
{
*max = a[i];
}
if (a[i] < *min)
{
*min = a[i];
}
}
}
指標應用場景二b
- 函式回傳運算的狀態,結果通過指標回傳
- 常用的套路是讓函式回傳特殊的不屬于有效范圍內的值表示出錯
- -1 或 0
- 但是當任何數值都是有效的可能結果時,就得分開回傳了
#include <stdio.h>
// 如果成功就回傳1,否則就是0
int divide(int a , int b , int *result);
int main(void)
{
int a = 5;
int b = 2;
int c;
if (divide(a,b,&c))
{
printf("%d/%d = %d\n", a, b, c);
}
return 0;
}
int divide(int a , int b , int *result)
{
int ret = 1;
if ( b== 0)
{
ret = 0;
}else{
*result = a/b;
}
return ret;
}
指標常見的錯誤
定義了指標變數,還沒有指向任何變數,就開始使用了
四、指標與陣列
傳入函式的陣列成什么了?
- 函式引數表中的陣列實際上是指標
- sizeof(a) == sizeof(int*)
- 但是可以用陣列的運算子[]進行運算
下面的四種函式原型是等價的
int sum(int *ar , int n);
int sum(int* , int);
int sum(int ar[] , int n);
int sum(int[] , int);
陣列變數是特殊的指標
陣列變數本身表達地址,所以
- int a[10]; int*p = a; // 無需用&取地址
- 但是陣列的單元表達的是變數,需要用&取地址
- a == &a[0]
[]運算子可以對陣列做,也可以對指標做
- p[0] <===> a[0]
*運算子可以對指標做,也可以對陣列做
- *a = 25
陣列變數是const的指標,所以不能被賦值
五、指標與const
- 表示一旦得到了某個變數的地址,不能再指向其他變數
// q內寫的地址不能被改變
int *const q = &i;
*q = 26; // ok
q++; // error
- 表示不能通過這個指標去修改那個變數(并不能使得那個變數成為const)
const int *p = &i;
*p = 26; // error (*p是不能變的)
i = 26; // ok
p = &j; // ok
- 看懂下面的意思?
const int * p1 = &i;
int const * p2 = &i;
int *const p3 = &i;
判斷那個被const的標志是const在*的前面還是后面
前面兩個*p不能被修改,就像第二種情況
const陣列
const int a[] = {1,2,3,4,5,6,};
- 陣列變數已經是const的指標了,這里的const表明陣列的每個單元都是const int
- 所以必須通過初始化進行賦值
保護陣列值
- 因為把陣列傳入函式時傳遞的是地址,所以那個函式內部可以修改陣列的值
- 為了保護陣列不被函式破壞,可以設定引數為const
int sum(const int a[] , int length);
六、指標運算
1+1 = 2 ?那么指標加1等于什么呢
#include <stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
printf("p =%p\n", p);
printf("p + 1 =%p\n\n", p+1);
// p =000000000062FE30
// p + 1 =000000000062FE31
// 相差了1
int ai[] = {0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
printf("q =%p\n", q);
printf("q + 1 =%p\n\n", q+1);
//q =000000000062FE00
//q + 1 =000000000062FE04
// 相差了4
return 0;
}
我們不難發現,在char中,相差了1,在int中相差了4,這是怎么回事?
sizeof(char) = 1 , sizeof(int) = 4
對指標做一個加1的動作,意味著要把它移動到下一個單元去
int a[10];
int *p = a;
*(p+1) --> a[1]
如果指標不是指向一片連續分配的空間,如陣列,那么這運算沒有意義
可以對指標的算術運算
- 給指標加、減、一個整數(+、+=、-、-=)
- 遞增遞減(++/--)
- 兩個指標相減
#include <stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4,20,6,7,8,9,};
char *p1 = &ac[5];
char *p2 = &ac[4];
printf("p1 - p2 = %d\n" , p1 - p2);
printf("*p1 - *p2 = %d\n" , *p1 - *p2);
// p1 - p2 = 1 也就是 5 -4
// *p1 - *p2 = 16 也就是 20 - 4
int ai[] = {0,1,2,3,4,5,6,7,8,18,};
int *q1 = &ai[9];
int *q2 = &ai[2];
printf("q2 - q1 = %d\n", q2 - q1);
printf("*q2 - *q1 = %d\n", *q2 - *q1);
// q2 - q1 = -7 也就是 2 - 9
// *q2 - *q1 = -16 也就是 2 - 18
return 0;
}
*p++
- 取出p所指的那個資料來,完事之后順便把p移動到下一個位置
- *的優先級雖然高,但沒有++高
- 常用于陣列類的連續空間操作
- 在某些cpu上,這可以直接被翻譯成一潭訓編指令
char ac[] = {0,1,2,3,4,20,6,7,8,9,-1};
char *p = &ac[0];
// 普通方法遍歷陣列
// int i ;
// for (i = 0; i < sizeof(ac)/sizeof(ac[0]); i++)
// {
// printf("%d\n", ac[i]);
// }
// 普通指標遍歷陣列
// for (p = ac; *p != -1; p++)
// {
// printf("%d\n", *p);
// }
// printf("---------------\n");
// 使用*p++,快
while(*p != -1){
printf("%d\n", *p++);
}
0地址
- 當然你的記憶體中有0地址,但是0地址通常是個不能隨便碰的地址
- 所以你的指標不應該具有0值
- 因此可以用0地址來表示特殊的事情
- 回傳的指標是無效的
- 指標沒有被真正的初始化(先初始化為0)
- NULL是一個預定定義的符號,表示0地址
指標的型別
- 無論指向什么型別,所有的指標的大小都是一樣的,因為都是地址
- 但是指向不同型別的指標是不能直接互相賦值的
- 只是為了避免用錯指標
用指標來做什么
- 需要傳入較大的資料時用作引數
- 傳入陣列后對陣列做操作
- 函式回傳不止一個結果
- 需要用函式修改不止一個變數
- 動態申請記憶體時
七、動態記憶體分配
輸入資料
- 如果輸入資料時,先告訴你個數,然后再輸入,要記錄每個資料
- C99可以用變數做陣列定義的大小,C99之前呢?
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int number;
int *a;
int i;
printf("請輸入數量:\n");
scanf("%d" , &number);
// 動態記憶體分配用malloc,此時a就相當于陣列
a = (int*)malloc(number*sizeof(int));
for (i = 0; i < number; i++)
{
scanf("%d" , &a[i]);
}
for (i = number - 1; i >=0; i--)
{
printf("%d\n", a[i]);
}
// 最后釋放空間
free(a);
return 0;
}
malloc函式
#include <stdlib.h>
void* malloc(size_t size);
- 向malloc申請的空間的大小是以位元組為單位的,需要什么型別就在sizeof里面寫什么型別
- 回傳的結果是void* , 需要型別轉換為自己需要的型別
- 最后釋放空間
(int*)malloc(n*sizeof(int));
沒空間了?
- 如果申請失敗則回傳0 , 或者叫做NULL
- 你的系統可以給你多大的空間?
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *p;
int cnt = 0;
while((p = malloc(100 * 1024 *1024))){
cnt ++ ;
}
printf("分配了%d00MB的空間\n", cnt);
return 0;
}
// 分配了27200MB的空間
free()
- 把申請得來的空間還給系統
- 只能還申請的空間的首地址
- 一個malloc對應一個free
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/71567.html
標籤:C
上一篇:C語言交換兩個指標所指位置的數值
下一篇:資料結構--排序
