C語言基礎———指針,結構體指針,函數指針

指針基礎

一 指針就是地址,地址就是指針.指針存儲的是內存的地址.
二 指針的定義
1.&表示取址運算符,&a 可以取到 a 在內存中的地址;打印地址的佔位符爲(%p),printf(“%p\n”,&a);
2. 指針變量的定義
指針變量是用來存放地址的.
指針定義的格式:
類型數據  *變量名  =  初始值;
int *p = NULL;*在這裏表示p 是一個指針變量,不是運算符.
printf(“%p\n”,p); 在打印指針的時候不需要使用取址運算符(&),因爲指針本來就是地址.
*p 指針爲空時,打印出來0x0的地址,爲空.
int  a = 10;
p = &a;
printf(“%p\n”,p);
訪問內存中的數據有兩種方法:
1.直接訪問內存中的數據.
printf(“%p\n”,a);
2. 通過指針進行訪問.
*也可以作爲取值運算符.* p 代表取到 p 指向內存中存儲的數據  printf(“%d\n”,*p);
int a = 10,b = 20;
int *p1 = &a;
int *p2 = &b;
對指針重新賦值,意味着指針重指向,也就是說指針指向一個新的地址.
p1 = p2;p1指向 c 的首地址
printf(“*p1 = %d,* p2 = %d\n”,*p1,*p2);

int a = 15,b = 20;
int *p = &a;
int *p1 = &b;
單獨的* p 表示取值,* p = 11 表示向 p 指向的內存中存入數據.
*p = 11;
printf(“%d\n”,a);

int *p3 = &a;
p3 = p1;
*p1 = 8;
printf(“a = %d,b = %d\n”,a,b);a = 11,b = 8

經典練習
int a = 6,b = 8,c = 10;
int *x = &a;
x = &c;
int *y = &b;
int *z = &c;
*z = 5;
*x = 12;
x = y;
 *y = *x + *z;
printf(“a = %d,b = %d,c = %d\n”,a,b,c);

指針中常見的問題
1.內存編號比較小的存儲單元,是由系統控制的,不允許訪問.
int *p = NULL;
*p = 100;   error
2.野指針,定義時沒有賦初始值,操作野指針是很危險的
int *p;
*p = 100;error
3.定義指針時,*怎麼寫?
int *p = NULL;推薦
int* p1 = NULL;
int *p1 = NULL;
int*p3 = NULL;
4.指針變量在內存中佔幾個字節
與數據類型無關,在32位操作系統下,指針所佔字節數是4位,在64位操作系統中,指針所佔字節數是8位.
printf(“%lu\n”,sizeof(long *));
5. 修飾指針變量數據類型的作用
a. 指針變量數據類型,決定*(取值運算符),取多少個字節的數據;
定義指針的數據類型一定要和指向數據的數據類型一致,這樣才能把數據完整的取出來
int *p = NULL;
int a = 268;
p = &a;
printf(“%d\n”,*p);
char *c = NULL;
c = &a;
printf(“%d\n”,*c);
b.指針類型,決定指針變量加1操作時,跳轉多少個字節

int *p = NULL;
printf(“%p\n”,p); //0x0
printf(“%p\n”,p+1);//0x4
printf(“%p\n”,p+2);//0x8
printf(“%p\n,p++);//打印的結果是:0x4   但是實際結果是:0x8

p+1 和  p++或++ p 的關係
相同:取到的都是下一個字節的地址
不同:++p和 p++ 造成指針的重指向
6.指針在數組中的應用
int a[5] = {1,2,3,4,5};
數組名就是數組元素的首地址

printf(“%p\n”,a);
printf(“%p\n”,&a[0]);
printf(“%p\n”,p);

printf(“%p\n”,a+1);
printf(“%p\n”,&a[1]);
printf(“%p\n”,p+1);

printf(“%p\n”,a+2);
printf(“%p\n”,&a[2]);
printf(“%p\n”,p+2);

printf(“%d\n”,*a);
printf(“%d\n”,*(a +1));
printf(“%d\n”,*(a+2));

用指針遍歷數組
for(int i= 0;i < 5;i++){
printf(“%d\n”,*(a+i));
}
定義指針變量訪問數組中的元素
int *p =NULL;
p = a;
printf(“%p\n”,a);
printf(“%p\n”,p);
printf(“%d\n”,*p);

printf(“%d\n”,*(p+1));
printf(“%d\n”,*(p+2));

練習
10個元素的數組,通過指針變量爲元素隨機賦值[22 33]

7.數組名(數組元素的首地址)   與指向數組首地址的指針變量的區別:
a. 數組名(數組首地址) 是一個常量地址不能被修改
a = NULL;error
指針變量可以修改,可以重指向
b. 數組名(數組的首地址),用sizeof 計算得到的結果,整個數組所佔的字節數,而用 sizeof 計算指向數組元素首地址的指針變量得到是一個定值4或者8
printf(“%lu\ n”,sizeof(c));

//定義一個10個元素的數組,通過指針變量爲元素隨機賦值[22 33],冒泡排序;
8.指針在字符串中的應用
用% s 打印是從指針指向那個地址開始向後輸出
char string[] = “Canglaoshi”;
printf(“%s\n”,string);

printf(“%s\n”,string+1);
通過指針訪問裏面的元素
數組名(數組元素的首地址)  == 指針
*(string + 3) = ‘\0'
printf(“%s\ n”,string);
 通過指針訪問元素
printf(“%c\ n”,*(string +3));

通過指針計算字符串的長度
int i= 0;
*(string +i)  取到數組中對應位置的值
while(*(string+i) != ‘\0’){//string[i]
i++;
}
printf(“%d\n”,i);

指針變量一定要和指向的數據類型相同

char *p = string;
int i = 0;
while(*(p+i) != ‘\0’){
i++;
}
char string[] = "canglaoshi I love you";//將字符串的首個字母大寫,並且將空格換成下劃線
char string[] = "canglaoshi I love you";
   *string -= 32;
   char *p = string;
   if (string[0] >= 'a' && string[0] <='z') {
   string[0] -= 32;
   }
   int i = 0;
   while (*(string+i) != '\0') {
   if (*(string + i) == ' ') {
   *(string + i) = '-';
   }
   i++;
   }
   printf("%s\n",string);

 //寫一個函數實現兩變量的交換
   int a = 10;
    int b = 20;
    change(&a,&b);
    printf(
"a = %d,b = %d\n",a,b);
   
   
   
int a = 10,b = 20;
   
int sum = 0,sub = 0;
    sum =
sumAndsub(a,b,&sub);
   
printf("sum = %d,sub = %d\n",sum,sub);
   
   
float a[10] = {0};
   
for (int i = 0; i < 10; i++) {
        *(a+i) =arc4random()%
21+10;
        printf(
"%.2f ",*(a+i));
    }
   
printf("\n");
   
for (int i = 0; i < 10 - 1; i++) {
       
for (int j = 0; j < 10 - i - 1;j++) {
//            if (a[j] > a[j + 1]) {
//                float temp = 0;
//                temp = a[j];
//                a[j] = a[j + 1];
//                a[j + 1] = temp;
//            }
           
           
if (*(a + j) > *(a + 1 + j)) {
               
float temp = 0;
                temp = *(a +j);
                *(a + j) = *(a + j +
1);
                *(a + j +
1) = temp;
            }
        }
    }
    
printf("\n");
   
for (int i = 0; i < 10; i++) {
        printf(
"%.2f ",*(a+i));
    }

結構體指針
1.形參和實參的區別
a. 形參:定義的時候寫的參數,(起一個描述的作用,沒有實際的意義)
b. 實參:函數實際調用時傳進來的參數
形參和實參的傳遞是單向的.
2.結構體嵌套
在定義結構體的時候,結構體的成員變量也可以是結構體.
3.結構體指針
指向結構體的指針叫結構體指針.
數據類型  + *  + 變量名 + 初始值;
Student  stu1 = {“lisi”,’M’,29}; 
 指針變量指向結構體變量的首地址,相當於指向結構體第一個成員變量的地址
Student *p = &stu1;
結構體指針的訪問
printf(“%s”,(*p).name);
 通過指針變量訪問結構體變量的成員
printf(“%s,%c,%d\n”,p->name,p->sex,p->age);
4——>:指向操作符
只有定義的是結構體變量的首地址,纔可以使用(—>指向操作符)訪問結構體變量
printf(“%s,%c,%d\n”,p->name,p->sex,p->age);
5.float x1 = 0.1;
float x2 = 1.;
float x3 = 1.f;
float x4 =1.0f;
float x5 = 1;
6.結構體數組與指針的關係
結構體數組的數組名是一個結構體指針常量.
int a[5] = {1,2,3,4,5};
int *p = a;//數組名就是數組的首地址.
printf(“%d\n”,*p);
*stu   :   stu[0]  //代表的都是數組中第一個元素,
*(stu + 1) :stu[1]  // 代表的都是數組中的第二個元素.
*(stu + 2)   :stu[2]  //代表的是數組中的第三個元素.

訪問數組中結構體元素的屬性
*stu.name   :stu[0].name
(*(stu + 1)).name  :  stu[1].name

—>:指向操作符  :使用的時候一定要保證指針指向結構體變量的首地址
stu—> name   :stu[0].name
(stu + 1) —>name  :stu[1].name
(stu + 2)——>name   : stu[2].name


7.遍歷結構體數組的時候,需要向函數中傳入數組和長度.
8.指針數組(也就是指向指針的指針)
int  a = 3,b = 4,c=  5;
數組中的元素都是指針(地址)數組,就是指針數組.
int *array[3] = {&a,&b,&c};//3*8個字節
printf(“%p\n”,array[0]);
printf(“%p\n”,&a);

printf(“%d\n”,**array);//*array  取到地址  **array  取到地址裏的值
printf(“%d\ n”,*(*(arrary + 1)));
二.函數指針
函數指針的聲明方法爲:
函數類型  (標示符  指針變量名 )(形參列表);
函數類型說明函數的返回類型,”(標識符 指針變量)”中的括號不能省,若省略整體則成爲一個函數說明,說明了一個返回的數據類型是指針的函數,後面的”形參列表"表示指針變量指向的函數所帶來的參數列表.
例如:
int func(int x); 聲明一個函數
int (*f)(int x); 

函數指針

1.函數指針類型的確定步驟
1⃣把函數名換成(*)    void (*)()    int(*)(int,int)
2⃣如果有參數,把參數名去掉,只保留參數類型  void (*)()   int (*)(int,int)
3⃣格式
指針類型  *指針名  = 初始值;
char *p = NULL;
函數指針的類型
返回值類型(*函數指針變量名)(參數類型1,參數類型2.....) = 初始值;
函數後面的()是函數調用符.
注意:  函數指針變量名不能和指針重名
int sum(int a,int b){
return a+b;
}
int (*Sum)(int,int);
Sum = sum;
int y = Sum(3,5);
printf(“%d\n”,y);
同一個函數指針可以指向不同的函數,但是(前提是:函數指針的類型必須一致)
2.typedef 原始類型  新類型;
給函數指針類型起別名
typedef int (*FUN)(int,int);
typedef void (*HELLO)
typedef  返回值類型(*新類型名)(參數類型1,參數類型2.....)
3.函數回調
函數回調:就是用函數指針來調用函數
函數名就是指針,存放的函數的首地址

4.動態排序
就是不斷的改變排序的條件,根據不同的條件,調用不同的函數,進行排序.

5.枚舉
定義:一組有符號的整形常量,一一列舉所有的狀態,羅列出所有可能的結果
enum season {
spring,
summer,
autumn,
winter};
printf(“%d\n”,spring);
枚舉經常和 swith 合用
枚舉間接提高了代碼的可讀性
將人能識別的表示符,與計算機能識別的數字建立聯繫
6.宏只做簡單的替換,在預編譯的時候完成替換
宏命名規範:
1⃣純大寫
2⃣k +  大駝峯
無參宏直接進行定義,有參宏可以使用常量表達式
#define SUM(A,B)  ((A) - (B)) // 最安全的方式
有參數的宏,只做簡單的替換運算,不會判斷運算符的優先級
int p = SUM(3 + 7,5 + 6) * 3;//3 + 7 * 5 + 6

7.條件編譯
第一種
#ifdef  MY
printf(“Hello Word!!");
#else
printf(“明天考試!!");
#endif

特點:如果標示符被# define 過,就會執行代碼段1,否則就會執行代碼段2

第二種方式
 #ifndef  標示符
     代碼段1
     #else
     代碼段2
     #endif
   特點:如果標示符沒有被#define過,就會執行代碼段1;如果被定義過,,就編譯代碼段2

第三種方式
#if  常量表達式
代碼段1
#else
代碼段2
#endif

#if MY
printf(“你好藍歐!!");
#else 
printf(“你好中國!!");
#endif

特點:如果常量表達式非0,就執行代碼段1,否則執行代碼段2


8.const 常量修飾符
被 const修飾的常量是不能重新賦值的
































發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章