c语言指针专题

 0.问题引入

         int a = 5;   
        a = 1024; //把1024存放到变量a的地址中去
        b = a; // 取变量a的值,赋值给b
          ===>在c语言中,任何一个变量,都有两层含义
                (1)代表变量的存储单元的地址:变量的地址 ===> lvalue(也称变量的左值)
                (2)代表该变量的值: ===> rvalue(也称变量的右值)
                
             对于变量的访问,只有两种情况
             read: 从变量的地址中取值(读)
             write : 把一个数值,写到变量的地址中去(写)
             
        问题:
              如果我们知道了一个变量的地址,是不是我们就可以通过该变量的地址去访问这个变量
                    可以的
                如何通过一个变量的地址去访问这个变量呢?
                   ====> 指针

1.对象的访问方式

(1)直接访问:通过对象名去访问
            直接访问有一个缺陷?
                 直接访问受对象的作用域的影响(限制)
                //   局部变量不是永久的,函数返回的时候,局部变量就会被删除
                
       (2)间接访问:通过对象的地址去访问,指针访问
            只要你知道了对象的地址,就可以去任何地方访问它,不受作用域的影响
        

2.什么是指针?指针的概念是什么?

存储单元的地址:
                  分配给每一个变量的内存单元都有一个编号,这个编号就是我们说的存储单元的地址,
                  并且是按字节来编址。
        

上图是用指针指向一维数组首地址


        在c语言中,指针的概念与地址的概念差不多,你可以认为指针就是一个地址,一个变量的地址我们也称为变量的指针。
    & 取地址符:
                                                                        单目运算符, ”取xxx对象的地址“
                
        通过一个对象的指针去访问它,首先要解决 对象的指针(地址)的存储问题
        这个时候需要定义一个变量来保存它的地址,这个变量我们称为”指针变量“

3.指针变量

什么是指针变量?
                指针变量也是一个变量,也是用来保存数据的,只不过指针变量是用来保存其他对象的地址的。
        如何定义一个指针变量?

        首先我们来看一下普通变量的定义方式:
         
                               变量的类型    变量名;
                        在定义指针变量的时候,为了区分在指针变量的前面加一个*,来表示他是一个指针变量
                        
            ====》
                 指针变量的定义:
                        指向对象的类型 * 对象名
                        
                        ”指向对象的类型“:
                                指针变量指向的是对象的类型,不是指针的类型。
                                ”指向“:
                                         假如一个变量p保存了变量a的地址,
                                         那么,我们就说p指向a
                                例子:
                                        int a  = 1024;
                                         p = &a ;   // 把a的地址赋值给p
                                                    // p保存了对象a的地址
                                                    // p 指向a
                                                    // p ---> 指针变量
                                                 这个p该如何定义?
                                                      指向对象的类型 * 指针变量名
                                                       typeof(a) * p
                                                       ===> int * p;
                                            练习:
                                                        char c[10];
                                                        char * p;
                                                        p = &c[9];
                                  分析过程    
                                           p保存了c[9]的地址;
                                           ====> p指向c[9] 
                                                p是一个指针变量

4.与指针相关的运算符

            & :取地址符
            * :指向运算符
                   二者都属于 单目运算符,优先级最高
                    用法:
                            * 地址   <=> 地址对应的那个变量(对象)
                        int a;
                        * a  // ERROR
                        
                    例子:
                           int a = 1024;
                           *(&a)  ===>  *对象a的地址 
                                  ===>  地址对应的那个对象                                                              
                            
                            int b = *(&a);  <==> b = a
                            
                        so
                            *(&a) <==> *&a <==> a
                            * & ===>可以直接约掉
                            
                            NOTE:
                                 & *a <==> a  ???? 不可以
                                 & * 不能约
                        
                        char ch ;
                        * (&ch) = 'A'; // *(&ch) ==>ch ,在此处代表的是变量ch的左值,”变量的地址“
                        char c = *&ch; // *(&ch) 在此处代表的是变量ch的右值,”变量的值“
                                                        
                代码分析:
                            int a = 5;
                            假如我们要定义一个变量p,来保存a的地址,
                            int *p;
                            p = &a;                                                  
                                      // 把变量a 的地址赋值给p
                                        p是一个指针变量
                                        那么,它也有左值,右值
                                        p的右值:p的存储单元中的内容  ===> &a
                                        p的左值:p本身存储单元的地址  ===> &p
                                        
                                        * p :在此处p代表 p的右值,p的值
                                            * p <==>*(&a) <==> a;
                                            a = 1024; <==> *(&a) ==>1024 ==> *p =1024
                                            
                        请大家写一个程序,来证明p的右值就是&a?                    
                            

#include <stdio.h>
void main()
{
    int a=1024;
    int *p=&a;
    printf("%d\n",*p);//a的值
    printf("%p\n",&a);//a的地址
    printf("%p\n",p);//a的地址
    printf("%p\n",&p);//p的地址
}//指针与所指变量的关系

5.指针变量作为函数的参数

练习:  如下函数该这么设计?该怎么调用
                   

 void func(int *x,int *y)
                    {
                      int temp;
                      temp = *x; // ===> temp = *(&a)
                      *x  = *y; // *(&a) = *(&b) 
                      *y = temp; // *(&b) = temp                       
                       
                    }
                    void func1(int *x,int *y) /*不可以的,因为此时该函数的功能仅仅是改变指针的指向,并没有改变实际的值,因为 因为指针的交换并不影响实际变量的值。*/
 
                    {
                      int *temp;
                      temp = x;
                      x= y;
                      y = temp;
                    }                    
                    int main()
                    {
                        int a = 5;
                        int b = 6;
                        
                        int *p = &a;
                        int *q = &b;
                        
                        func(p,q); //调用这个函数的目的是:为了交换变量a和b的值
                        
                        printf("a==%d\n",a);//6
                        printf("b==%d\n",b);//5
                        
                    }                     void func(int *x,int *y)
                    {
                      int temp;
                      temp = *x; // ===> temp = *(&a)
                      *x  = *y; // *(&a) = *(&b) 
                      *y = temp; // *(&b) = temp                       
                       
                    }
                    void func1(int *x,int *y) /*不可以的,因为此时该函数的功能仅仅是改变指针的指向,并没有改变实际的值,因为 因为指针的交换并不影响实际变量的值。*/
 
                    {
                      int *temp;
                      temp = x;
                      x= y;
                      y = temp;
                    }                    
                    int main()
                    {
                        int a = 5;
                        int b = 6;
                        
                        int *p = &a;
                        int *q = &b;
                        
                        func(p,q); //调用这个函数的目的是:为了交换变量a和b的值
                        
                        printf("a==%d\n",a);//6
                        printf("b==%d\n",b);//5
                        
                    }                    


        传的还是”值“,传的还是”实参的值“,
        ”实参的值可能是某个对象的地址“

                例子:
                       

 void func(int *a,int *b)
                        {
                            int *t; /*定义了一个指针t,但是t没有赋值,不知道这个t保存了
                                      哪个对象的地址;不代表它里面没有值
                                      如果是一个指针变量的话,不知道它指向的对象是谁
                                      *t  ==>t指向的那一个对象
                                           如果我们去操作*t ,分为两种情况
                                           read :
                                                 int m = *t; //有可能t指向的那一个对象不可读。
                                                  // 可能导致内存的非法访问 =>段错误
                                           write:
                                                   *t = 1024;//有可能t指向的那一个对象不可写
                                                  // 可能导致内存的非法访问 =>段错误*/
                                                   
                                                 
                                           
                            *t = *a; //可能导致”段错误“
                            *a =*b;
                            *b = *t; //可能导致”段错误“
                        }                        


            像这个例子中的t这样的指针,我们称之为”野指针“。


           6. 野指针:
                   

                指向一个未知单元(未知对象)的指针,称之为”野指针“

                野指针的成因:

                (1)指针未初始化

int main()
{
    int *p;//未初始化指针
    *p=10;
    return 0;
}

                (2)指针的越界访问

                        

int main()
{
    int arr[10]={0};
    int *p=arr;
    for(int i=0;i<=10;i++)
    {
    *(p++);/*i从0-10一共有十一次循环,而数组arr只有十个元素,在进行第十一次循环时就会出现数组越界问题*/
            /*在进行前十次循环时,p还不是野指针,因为此时并未出现数组越界,一旦第十一次循环开始,就会出现数组越界问题,p就会变成野指针,在Linux下运行的表现未"段错误"*/
    }
}

        
                   使用野指针,可能造成内存的非法访问 ===> 段错误
                   int *p;
                // *p = 1024;  属于操作野指针
                // int b = *p ; 属于操作野指针
                   int a;
                    p = &a; // 不属于操作野指针

        7.空指针

            空指针:
                    NULL 
                   在计算机中,地址为0的存储空间是不存在的
                   如果一个指针的值为0(NULL),表示这个指针指向了
                   空(NULL),像这中指向0(NULL)的指针,我们称之为
                   空指针
                   int *p = NULL;
                       p它不是野指针
                       
                段错误原因:内存的非法访问
                         (1)数组越界,可能导致段错误
                              int a[10];
                              a[100] = 1024;
                         (2)非法使用指针(指针乱指)(野指针)
 

6.数组与指针

 数组元素与普通变量是一样的,也有自己的地址。
        数组元素也有左值和右值,并且数组元素的地址相邻的。
      ===>数组名可以代表首元素的地址(首地址)。
         例子:
                int a[5];
                      a是一个数组名, 数组名a当作指针来看: a <===> &a[0]

                        //一维数组名就是这个数组的首地址
                      我要定义一个指针变量p,来保存a[0]的地址
                      该如何定义p?
                      
                       int *p = &a[0] //OK
                             如果把数组名a当作指针来看, a ===> &a[0];
                             <===> p = a; // OK
                             a[0] = 1024;
                             <===> *p = 1024;
                             <===> *a = 1024;
                             
                    p是指向a[0],那么能不能通过p去访问a[1]? 
                              可以的
                              
                        *p   ===>*&a[0] ==>a[0]
                        a[0]和a[1]的地址是相邻的
                               p + 4 ==&a[1] ????不对的

                        /*因为p+1的意思是指针p向前移动一个它所指向单元的长度,而p指向int型数组,一个单位长度是4个字节*/
                        p + 1 ===> &a[1]
                              通过指针p来访问a[1];
    
    指针做加减的问题: int a[5]
            p + i  (p是一个指针,i是一个整数)
                    不是简单的加减值,而是加减i个指向单元的长度
            p+1  ===> 往后面挪了一个指向单元的长度
                   p = > &a[0]
                   p+1 => 往后面挪了一个int单元的长度
                        (p+1) = >&a[1]
            
            例子:
                 有p = &a[0],把数值100赋值给a[0],有多少种表示方法?
                   int a[10];
                   int *p = &a[0];
                   
                   a[0] = 100;
                   *p = 100;
                   *&a[0]  = 100;
                   *a = 100;
                   **&p = 100;
                   **&a = 100;
                   p[0] = 100;
                   *(p+0) = 100;
                   *(a+0)= 100;
                   *(&a[1]-1) = 100;
                   .....
                   
            练习: 
                    int a[10] = {1,2,3,4,5,6,7,8,9,10};
                    
                    int *p = &a[2];

#include <stdio.h>
void main()
{
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    int *p=&a[2];
    int i;
    for(i=-2;i<8;i++)
    {
        printf("a==%d\n",*(p+i));
    }
}


                    通过指针p去访问每一个元素的值,将其依次输出。

7.多维数组与指针

(1)在C语言中所有数组都是一维数组
            (2)数组名可以当作指向第0个元素类型的指针常量,
                 并且数值上为第0个元素的首地址
                 
                假如有:
                        int a[3][4]
                           //int [4] a[3]

//二维数组a[3][4]可以看成三个拥有四个元素的一维数组
                           a[0] _ _ _ _
                           a[1] _ _ _ _
                           a[2] _ _ _ _ 
                           
                    表达式      表达式的含义
                       a        (1)当作指针
                                  &a[0]
                        (2)代表整个数组
                    
                                    a[0]      a[0]又是一个一维数组名
                                              (1)代表为a[0]的整个数组
                                              (2)当作指针
                                                &a[0][0]
                                             
                                     &a[0][0]     元素a[0][0]的地址                                             
               -------------------------------------------------                 
                     a+1           数组名a当作指针
                                       ==> &a[0] + 1
                                       ==> &a[1]
                                        取整个一维数组a[1]的地址
                    &a[1]          取整个一维数组a[1]的地址

                     &a              数组名a只能代表整个数组
                                      &a:取整个二维数组的地址

                     &a+1           数组名a代表整个数组
                                    &a+1 : 往后面挪了整个二维数组a的长度(12个int)
                ------------------------------------------------
                    a[1]+2         a[1]是一个一维数组名,只能当作指针
                                   ===>    &a[1][0] + 2
                                   ===> &a[1][2] :元素a[1][2]的地址
                    
                    *(a+1)+2     *(&a[0]+1)+2
                                 ==> *(&a[1])+2
                                 ==> a[1] + 2
                                   ==> &a[1][0] + 2
                                 ==> &a[1][2] :元素a[1][2]的地址
                ------------------------------------------------
                    *(a[1]+2)      a[1]是一个一维数组名,只能当作指针
                                  ==> *(&a[1][0] + 2)
                                 ==> *(&a[1][2])
                                 ==>a[1][2]:元素a[1][2]
                    
                    *(*(a+1)+2)  ==> *(*(&a[1])+2)
                                 ==>*(a[1]+2)
                                 ==>*(&a[1][0]+2)
                                 ==>*(&a[1][2])
                                 ==>a[1][2]:元素a[1][2]
                    
                    指针常量:
                             指针本身不能够改变,但是指向的空间里面的内容是可以改变的
                             如: 数组名作为指针
                             int m,n;
                             int *const a = &m; // a就是一个指针常量
                             *a = 1024;// ok
                             a = &n ; // ERROR
                             
                    常量指针:
                            是一个指向常量的指针,指向的对象是常量,那个对象是不能改变的
                            但是指针是可以改变的(也就是说可以保存其他的地址)
                            如:  字符串指针
                            
                            int m = 1024,n;
                            const int *b = &m;  ===> int const *a;
                            *a = 250;//ERROR 
                            a = &n ; // ok

8.指针数组与数组指针

 (1) 指针数组
                    指针数组是一个数组,只不过它里面的每一个元素都是相同类型的指针罢了!!!
                        定义数组:
                                  数组元素的类型   数组名[元素个数]
                                  
                            例子:
                                  int * p[4]; //指针数组
                                         //定义了一个数组,数组名为p,里面含有4个元素
                                           每一个元素类型都是 int *.
            (2) 数组指针
                    数组指针是一个指针,只不过这个指针是用来指向一个数组的
                    (这个指针保存的地址是一个数组的地址罢辽!!!)
                           例子:
                                 int a[4];
                                 您能不能定义一个指针p来保存数组a的地址呢?
                                       指针定义:
                                                 指向对象的类型 * p;
                                                 typeof(a) *p;
                                                 int [4] *p;
                                                 =>int (*p) [4];// p是一个指针,它指向的对象是一个int[4]类型的数组
                                                 
            
                    4. 有int b[3][4]; 假如要定义一个指针变量p,
                        来保存b[0][0]的地址,该如何定义? 
                                //int[4] b[3]
                                //b[0] _ _ _ _
                                //b[1] _ _ _ _
                                //b[2] _ _ _ _
                                
                            typeof(b[0][0]) *p;
                             ====> int *p;
                             p = &b[0][0];
                                 //typeof(p) ==> int *
                             p = b[0]; // ok
 

9.字符串指针

字符串是什么?
            字符串就是一串字符,在c语言中是没有字符串类型的。
            C语言的字符串是通过 char *(字符型指针)来实现的。
            C语言的字符串,是用”“(双引号)引起来的一串字符来表示。
            并且字符串后面默认会加一个’\0',表示字符串结束的标志。
                   如;
                        ”abcde“  ==>5
                        "1"     ==> 1
                        '123'   不是字符串,也不是字符啊
                        '\012'  不是字符串 ,是字符                        
                        "\012"    ==>是字符串,有一个字符  
                        ""         ==>是字符串,空串    
                
            只需要保存字符串的首地址(首字符的地址)就可以了    
            从首地址开始找到第一个’\0',前面的这些字符就是字符串里面的字符
                C语言中的字符串(如:”sssssss“)保存在一个叫做.rodata(只读数据)的内存区域中。
  
            如;
                "12345"
                在程序运行时,系统会为这个字符串在.rodata中分配6个字节大小的空间给它
                
                ”12345“的值,就是首字符的地址。
                typeof(”12345“)
                    ====》typeof(&’1‘)
                    ====》typeof('1') *
                    ====> const char *

            例子:
                    char *p = "12345";
                        p保存的是字符串首字符的地址,&’1‘
                        那么咱们是不是可以通过指针p来访问’1‘呢
                             可以
                          因为: p = &’1‘;
                          char m = *p ; <===> m = '1'
                          *p = 'A';//ERROR  因为p指向的对象是一个常量,不可以改变的
                              字符串就相当于一个常量指针
                     
                    问题:
                          字符’1‘和’2‘的地址是不是连续的呢?
                                是连续的
                            既然是连续的,那么咱们也可以通过指针p来访问’2‘
                            
                                p = &’1‘;
                                p+1 ===>往后面挪了一个char类型的单元长度   《===》 &’2‘
                                *(p+1) ==> *(&'2') ===> '2'
                                printf("%c\n",*p); // 1
                                printf("%c\n",*(p+1));// 2
                                
                                p+= 1; // OBJK  
                                ==>
                                     p = p+1;
                                     p = &'2';
                                *(p+1) = 'B';//ERROR
        
        字符数组:
                char s[5] ={'a','b','c','d','e'};
                    sizeof(s) == 5
                    char ch = *(s+1); <==> ch = s[1]
                    *(s+1) = 'B' ; // <===> s[1] = 'B'
                    s+= 1; //ERROR ==》 s = s+1 因为s是一个指针常量(数组名作为指针)为指针常量

                char s[] = {'a','b','c','d','e'};
                     sizeof(s) == 5;
                     可以省略元素个数
                char s[] = {"abcde"};
                      <===>  char s[] = {'a','b','c','d','e','\0'};
                             sizeof(s) = 6;
                            *(s+1) = ’B‘ ; // 可以,因为s是一个字符数组,数组区域是可读可写的
                            printf(”%s\n“,(s+1)); //Bcde    
                            
                            %s -> char *
                            把后面的哪个地址(指针)当作是一个字符串的首地址,一个一个字符的输出,
                            直到遇到'\0'结束,’\0' 不打印    

        
                    1.分析如下程序的输出结果
                      
                        p = "abcde";
                        printf("%s\n",p);//abcde

                        char s[] = "12345";
                        s[1] = ’B‘;
                        printf("%s\n",s);//1B345

                    2.写一个函数,用来求一个字符串的长度(包含了多少个有效字符)
                        (1)确定函数名
                            My_Strlen : 用来求一个字符串的长度
                        (2)确定参数列表(形参列表)
                            const char *s
                        (3)确定返回值的类型
                            返回值:
                                    有效字符的个数(int)
                        (4)代码的实现

                                           
                             

 int My_Strlen(const char *s)
 {
    int count = 0;
    while(*s) // *s !='\0'
    {
      count ++;
      s++;
    }
    return count;
  }    
int main()
{
    char q="123\0a0345";                                  
    scanf("%s",q);
    // int a ;
   // a = My_Strlen();
   // printf(a)
   printf("%d\n",My_Strlen((const char *)q));
}

10.几个标准库里常用的字符串处理函数


             #include <string.h>

             int strlen(const char *s);
             
             @s : 要计算长度的那一个字符串的首地址
             const char * 类型: 表示在程序运行的过程中,指针指向的字符串不能被修改
             返回值:  返回字符串的有效字符个数(不包括‘\0’);
             
             例子:
                    strlen("abcde") == 5
                    char s[4] = {'1','0'};
                        sizeof(s) == 4
                        strlen(s) == 2
                    strlen("abcd\nabc") == 8
                    strlen("123\123abc\0abc") == 7
                    strlen("123\01a3abc\0abc") == 9
                    strlen("123\000abc") ==3
                    strlen("123\0x123") == 3
                    strlen("123\x123456gad") == 7

(2)strcpy/strncpy
        字符串拷贝函数
          NAME
       strcpy, strncpy - copy a string

SYNOPSIS
       #include <string.h>

       char *strcpy(char *dest, const char *src);
         strcpy: 用来把src指向的字符串,拷贝到dest指向的空间中去,直到遇到'\0'结束。
         
         @dest: 目的地(dest必须是一个可写的空间)
         @src : 从哪里来
         返回值:
                 返回拷贝之后,目的地字符串的首地址
                 
            例子:
                  char s[6];
                  strcpy(s,"12345");
                  printf("%s\n",s); //12345
                  
                  char *p = "abcde";
                  char *q = "12345";
                  strcpy(p,q); //ERROR
                  
            strcpy 有一个小小的BUG!!!
            因为他没有考虑到数组越界的问题,有可能会导致内存的非法访问
            
         char *strncpy(char *dest, const char *src, size_t n);
            
            strncpy: 为了解决strcpy的这个bug的情况,功能是类似的,只不过
                      它顶多拷贝n个字符到 dest
                      
                      到底拷贝多少字符?(<=n)
                      (1)遇到了\0拷贝结束的,此时\0也会被拷贝
                      (2)已经拷贝了n个字符(后面的\0不会自动拷贝,除非最后一个字符是\0)
                      
                例子:
                      char s[10];
                      strncpy(s,"1234567890",10); 
                      
                      strncpy(s,"123",10);
                      
                

(3)strcmp/strncmp
            字符串的比较函数
            那么字符串该如何比较勒?
                        一个一个字符进行PK对应的ASCII的值
                                if  c1 >c2
                                        返回 >0
                                if  c1 < c2
                                        返回 < 0
                                if  c1 == c2
                                        则继续比较下一个字符,
                                        如果全部相等则返回0

                            strcmp("123","ABC"); <0
                            strcmp("123","123\0ABC"); ==0
                            strcmp("1234","123"); >0
                           
                            char *s = "GONG";
                            strncmp(s,"GONGJIANWEN",4) ==0

                 #include <string.h>

                   int strcmp(const char *s1, const char *s2);

                   int strncmp(const char *s1, const char *s2, size_t n);
                   
                            strncmp它的功能与strcmp类似,
                            只不过它只比较s1,s2前面的n个字符
                            
(4)strcat/strncat
            NAME
       strcat, strncat - concatenate(连接) two strings

SYNOPSIS
       #include <string.h>

       char *strcat(char *dest, const char *src);

       char *strncat(char *dest, const char *src, size_t n);
                    
                     strcat: 用来把src指向的字符串,拷贝到dest指向的字符串的末尾
                         
                     @dest :指向目标字符串(一段可写的空间)
                             dest指向的存储空间必须要足够的大,why?(越界)
                     @src  :指向原始字符串(表示将要被拷贝的那一个字符串)
                     返回值:
                             如果成功,返回连接后的字符串的首地址dest
                             如果失败,返回NULL

                例子:
                      char s1[12] = {"ABCD"};
                      strcat(s1,"12345");
                      printf("%s\n",s1);//ABCD12345
                        
                        当然这个函数也有一个小小的BUG,你懂的
                        so strncat 就是用来修复strcat的这个bug的    
        
                    strncat :
                              把src指向的字符串拷贝到dest指定的字符串的末尾
                              但是最多拷贝n个字符
                            (1)遇到\0拷贝结束,此时\0也会被拷贝 
                                    char s1[12] = {"ABCD"};
                                    strncat(s1,"12345",8);//ABCD12345
                            (2)如果要是没有遇到\0,但是以及拷贝了n个字符了,也结束
                                    char s1[12]={"ABCD"};
                                      strncat(s1,"123456789",8); //ABCD12345678

            练习:
                    分析如下程序的输出结果
                            char s[12];
                            strcat(s,"12345");
                            printf("%s\n",s); //随机的值
                            
                    写一个函数,把一个十进制的数字字符串,转成一个整数
                                "12345"
                                        =>12345
                                "-12345"
                                        =>-12345
                                "+12345"
                                        =>12345                                
                        
                            /*
                                my_atoi : 将一个十进制的数组字符串,转换成一个整数
                                @s :指向十进制数组字符串的  const char *s
                                返回值:  整数值
                            */
                            

int my_atoi(const char *s)
                            {
                                 int minux = 0; //符号   0 --负数   1 -- 正数
                                 int d = 0; // 保存当前位的数值
                                 int num = 0; //整数的值
                                 if(*s == '-')
                                 {
                                    minus = 0;
                                    s++;
                                 }
                                 else if(*s == '+')
                                 {
                                    minus = 1;
                                    s++;
                                 }
                                 else
                                 {
                                    minus = 1;
                                 }
                                 while(*s) // *s!='\0'
                                 {
                                    d = *s-'0'; 
                                    num = num *10 +d;
                                    s++;
                                 }
                                 if(minus==0)
                                 {
                                    num = num *(-1);
                                 }
                                 return num;
                            }
                            int main()
                            {
                               char s[256];
                               scanf("%s",s);
                               printf("%d\n",my_atoi((const char *)s);
                               //printf("%d\n",atoi());
                            }    

                其实呢,atoi这个函数是标准库中的一个函数,大家是可以直接去用的,
 NAME
atoi, atol, atoll - convert a string to an integer

SYNOPSIS
       #include <stdlib.h>

       int atoi(const char *nptr);
       long atol(const char *nptr);
       long long atoll(const char *nptr);

        在c语言中,不仅是变量,数组有地址,其实我们的函数也是有地址的。
        只要是有地址的东西,那么我们就可以定义一个指针变量去保存这个地址
        并且可以通过这个这个指针去访问指向的对象。
        
             函数地址 ----> 函数指针

11.函数指针


                                  int sum(int a,int b);
                                  int (int , int)===>这个是一个类型,是用来描述一个类似与sum函数的!
                               
                                  int * abc(int a,float b)
                                  {}
                                  描述abc的类型:
                                                函数的返回值类型 (函数的参数类型列表)
                                                    int * (int,float)
                                                    ====> 是一个返回int*型,带一个int和float的函数类型

                                                定义一个指针变量q,用来保存abc的地址:
                                                        指向对象的类型 *q;
                                                        typeof(abc) *q;
                                                        ===>int * (int ,float) *q;
                                                        ===>int * *q(int , float);
                                                        ===>int * (*q)(int,float);

                                  需要定义一个指针变量p,来保存函数sum的地址,该如何定义呢?
                                            int (*p)(int,int);
                                            指向对象的类型 *p;
                                            typeof(sum)* p;
                                            ===>int (int,int)* p;
                                            ===>int (*p)(int ,int);
                                
                        函数指针的定义方法;
                                    指向函数的返回值类型 (*指针变量名)(指向函数的形参类型列表);
                                例子;
                                   1.  char ** func(void)
                                       {}
                                      请大家定义一个函数指针变量p,来保存函数func的地址
                                        char ** (*p)(void);
                                        
                                   2.请各位大佬定义一个指针变量q,来保存如下函数的地址
                                       数组名a作为函数的参数,是当作指针来看的
                                       => &a[0]   typeof(&a[0]) ==> int *
                                       //int arr_sum(int * a,int n)
                                     int arr_sum(int a[],int n);
                                     {}
                                       => int (*q)(int *,int);
                                       
        (2)该怎么将函数的地址赋值给函数指针变量呢?
                  p -> 函数指针
                  p = 函数的地址
                函数的地址怎么去获取呢?
                    &对象名 ==> 取对象的地址

                函数的地址:
                        &函数名
                        or
                        函数名:在c语言中,函数名本身就代表函数的首地址
                    例子:
                            int sum_array(int *a,int n)
                            {}
                            //定义一个函数指针p
                            int (*p)(int *,int);
                            //将函数sum_array的地址赋值给p;
                            p = &sum_array;
                            //or
                            p = sum_array;
                                        //此时p指向函数sum_array

         (3)怎么通过函数指针取访问这一个函数呢?
                    函数调用:
                              函数名(实参列表);
                    a,
                         p = sum_array;
                         sum_array(a,5);
                        <==> p(a,5);

                    b,
                              p = &sum_array;
                         *p = *&sum_array;
                         sum_array(a,5);
                         ===>(*p)(a,5);
                         
                结论:    通过函数指针去调用指向的函数,有两种方式
                             p为函数的指针
                             (1)p(实参列表)
                             (2)(*p)(实参列表)
                    
                    练习;
                          1.首先写一个函数用来求一维数组中所有元素之和
                            然后在main中定义一个指针p,通过p去调用sum_array.
                            
                           

 #include <stdio.h>
                            //int sum_array(int a[],int n)
                            int sum_array(int *a,int n)
                            {
                                int i,sum=0;
                                for(i=0;i<n;i++)
                                {
                                   sum += a[i];
                                }
                                return sum;
                            }
                            int main()
                            {
                               int m[10] = {0,1,2,3,4,5,6,7,8,9};
                               int (*p)(int *,int);
                               p = &sum_array;
                               int n = *p(m,10);
                               printf("%d\n",n);
                            }
12.数组作为函数参数问题

 数组作为函数的参数的话,数组名都是当作指针来看!!!
            
            把数组作为函数的形参:
                    一维数组 
                               a,数组元素类型 数组名[] , 元素个数
                               b,数组元素类型 * 指针变量名 ,元素个数

                    二维数组
                               a,数组元素类型 数组名[][列数] ,行数
                               b,数组元素类型 (*指针变量名)[列数],行数
                                                        
                    练习:
                          首先写一个函数(sum_2_array)用来求二维数组中所有元素之和,
                          然后,在main中定义一个函数指针q,通过q来调用sum_2_array.                          
                            

int sum_2_array(int (*a)[4], int m)
                            {
                                int i, j ;
                                int sum = 0;
                                for(i = 0; i < m; i++)
                                {
                                    for(j = 0; j < 4; j++)
                                    {
                                        sum += a[i][j];
                                    }
                                }

                                return sum;
                            }
                            int main()
                            {
                                int m[10] = {0,1,2,3,4,5,6,7,8,9};

                                //定义一个函数指针p,来保存sum_array
                                int  (* p)(int *, int );

                                //将函数的地址赋值给 p 
                                p = &sum_array; //p = sum_array

                                //通过p来调用sum_array函数 
                                int n = p(m, 10);
                                printf("%d\n", n);
                                int x = (*p)(m, 10);
                                printf("%d\n", n);

                                int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

                                //定义一个函数指针q,来保存sum_2_array
                                int  (*q)(int (*)[4], int);

                                //将函数的地址赋值给 q 
                                q = &sum_2_array; //q = sum_2_array 

                                //通过函数指针q来调用它所指向的那一个函数 
                                int y = q(a, 3); 
                                //int y = (*q)(a, 3); 
                                printf("%d\n", y);
                            }


             
                保存普通变量地址的  变量 ==> 指针变量
                指针变量也有地址,那么保存指针变量的地址的  变量 ==> 二级指针

13.二级指针以及多级指针

 int a= 1024;
                可以定义一个指针变量p来保存a的地址
                    int *p;
                    p = &a; //p指向a

                p本身也有地址,我们可以定义一个指针变量p2来保存p的地址:
                    typeof(p) *p2;
                    int * *p2;
                         // p2二级指针,它保存的是一个一级指针的地址
                         //要分清楚到底是几级指针,怎么去区分?
                         // QTMD,你只要知道他是一个指针,(保存的是谁的地址)就可以了
                    p2 = &p; //p2指向p
                    **p2 = **&p = *p = *&a = a

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值