C语言学习 指针part3

day10-指针

预处理:
1.整个编译过程的一个阶段 
  
  gcc编译
  a.预处理
  b.编译 
  c.汇编
  d.链接 

2.预处理阶段 
  预处理命令的执行 

  #define N   
  #include 
  #ifdef  //多文件编程 --- 头文件的处理
  
  效果:
      文本的替换   

    int a = 0x1000;
    
    &a; // 0x1000
    
指针:
   1.所谓的指针,其实就是地址,地址是内存单元的编号 
   (理解:
      指针(地址) --作为内存单元的编号,本身也是一种数据
     但是这种数据含义比较特殊 
   2.指针还是一种数据类型,
   专门用来,保存,地址(内存单元的编号 )这种数据的,数据类型
   
 
指针 ---> 内存单元的编号 ---> 一块内存空间对应 

指针变量的定义:
语法:
    基类型  *  指针变量名
      (1)  (2)  (3)
      
    (1) 基类型     --- 表示 地址编号 所代表的那块空间 中要存放的数据的类型
                      //表示的p中存放的 地址编号 所代表的那块空间 中要存放的数据的类型    
    (2) *          --- 表示 定义的变量,不是一般变量,而是指针类型的变量
                      指针类型变量,意味着这个变量将来是存储 地址 这种数据的 
    (3) 指针变量名 --- 标识符,命名规则与变量名命名规则一致。
int a;//表示定义了一个int类型的a a是一个变量,一定对应有一块空间 
int * p; //表示,定义了一个 指针类型的变量 p
         //既然是变量,则p一定有自己的一块空间 
         //这块空间是用来存放 地址 这种特殊的数据的
         //p指向的数据类型是int类型   //p指向的类型 基类型 (目标类型)
         //p变量,可以存放的是 int类型的 地址
         
         
         //指针是一种数据类型, p是什么类型 => int * //int型的指针(地址)类型
         
int a= 10;
int *p = &a;  //从p的角度来看,我们说p指向了 a 

float a = 10.1;
int *p = &a; //此时编译会报警,
             //因为左右两边类型不一致,p是int *  //int * 表示 是一个 存放int类型数据的 空间的地址 
             //a 是一个float类型的变量, &a 表示获得了这块空间的地址,这个值的类型是float * 
  

  
通过指针访问数据:
  *  //单目运算  ---运算数 必须是 地址值
     //作用:
     // *地址值 =>访问到了地址值对应的那块空间 
     // eg:
     //相当于 酒店中 知道了门牌号 并且 拿到了钥匙,可以进入房间 
     
有了指针之后:
   通过a变量名方式访问,这种直接访问 
   通过p指针方式访问 ,这种间接访问
int a = 10;
int *p = &a;
printf("a = %d\n",a); //直接访问 
printf("*p = %d\n",*p); //间接访问 

int *p = &; //p的基类型是? int 

*p运算:
 step1: 首先,拿出p中地址,到内存中定位
 step2: 定位到之后,从定位处开始,偏移出 sizeof(基类型) 的一块空间 
 step3: 将这块空间 当做一个 基类型 的对象(变量)来看
 
注:
   所以 *p 整体相当于是一个基类型的变量 
 
练习:
  检测一下你的电脑的大小端?
  
为什么需要指针?
有些时候,不使用指针,没有办法达到想要的效果!


问题:
   一次函数调用,能带出几个结果?
   
可以实现指针的方式
 
指针核心用途: (被调修改主调)
  使用方法,
  1. 要修改谁,就把谁的地址传过去 
  2. 一定要对其做* 运算 
  
  
练习:
  通过指针的方式,带出两个数的和 和 差
    
   
练习:
  通过指针的方式,求出两个数的最大值 和 最小值
   
练习:
   实现一个函数,对主函数中两个变量,进行交换 
   void swap(int a, int b)
   {
      t = a ;
      a = b;
      b = t;
   }
 
 int main()
 {
     int m = 10;
     int n = 20;
     swap(a,b);
 }
 
指针的初始化:
 int *p;  //
 
 野指针  //指向不明确的指针 
 
 int *p = NULL; //NULL 是编号为0的这个地址
 
-----------------------------------
总结:
1.指针
2.指针变量定义 
  基类型 * 变量名;
3.指针变量的引用
  *指针变量名
  *p //理解三步运算过程 
4.指针的初始化 
  如果指针未初始化,此时指针变量中是个随机的地址 --- 野指针 
  为了让指针变量有明确的值,
  int a = 10;
  int *p = &a; 

  int *p = NULL; 
5.应用 
  被调修改主调 
  使用方法:
  a.要修改谁,把谁的地址传过去 
  b.被调函数中,一定要有 *运算
-----------------------------------
指针 + 数组 
指针 + 函数 
指针 + 指针 

----------------------------------
指针 操作 数组

int a[10] = {1,2,3,4};

指针访问一维数组 

*(p+i) <=> a[i] //等价 数组下标访问 本质上就是指针运算 

指针的运算:

p+N // 指针+数字  
    //含义: 表示 指向了 第N个元素的位置 
    //数值上看,sizeof(基类型)*N

p-N  
 
 
练习:
   找出数组中的最大值
   
   
作业:
1.通过指针,实现数组逆序  //写成函数 
2.通过指针,实现数组排序  //三个排序 
3.C语言100道题 6~10 指法练习 


a[0]的类型 int 
&a[0] => 取了一块存放着 int型数据的 空间的地址 
         int *  
int *p = a;
*(p+i) <=> a[i] <=>p[i]<=> *(a+i)

void printArray(int a[],int len)
{
   
}
void printArray(int *a,int len)
{
   
}
 
二分查找 


指针运算:


+N
-N
p++   //往后跳了一个元素 
p--   //往前 
p-q   //相同类型的指针 减出的来的结果为 ,地址之间相差的元素个数 
关系运算
p > q    
p < q 
> >= < <= != 


迭代 //从上一个 推到下一个 

练习:
    以迭代的方式 (指针操作) 的方式,实现数组累加求和
    
练习:
    以迭代的方式,实现数组逆序 
    
    b               e
    1 2 3 4 5 6 7 8 9
       
      b           e
    1 2 3 4 5 6 7 8 9
    
    
               |     
               V
 0 1 2 3 4 5 6 7 8 |下标
 -----------------------
 4 2 6 3 8 1 5 7 8 |数值 
 ^               ^
 |               |
 b               e

选择排序:
  for (i = 0; i < len-1; ++i)
  {
      for (j=1+i; j < len; ++j)
      {
        if (a[i] > a[j])
        {
            swap(a+i,a+j);
        }
      }
  }  
  
  
  

               |     
               V
 0 1 2 3 4 5 6 7 8 |下标
 -----------------------
 4 2 6 3 8 1 5 7 8 |数值 
 ^               ^
 |               |
 b               e

冒泡排序:
  for (i = len-1; i > 0; --i) //
  {
      for (j=0; j < i; ++j)
      {
        if (a[j] > a[j+1])
        {
            swap(a+j,a+j+1);
        }
      }
  }   
------------------------------


               |     
               V
 0 1 2 3 4 5 6 7 8 |下标
 -----------------------
 4 2 6 3 8 1 5 7 8 |数值 
 ^               ^
 |               |
 b               e

插入排序:
  for (i = 1; i < len; ++i) //
  {
     int k = a[i];
     j = i;
  
      while(j>0&& a[j-1] > k)
      {
        a[j] = a[j-1]; 
        --j;
      }
      
      a[j] = k;
  }   
------------------------------

               |     
               V
 0 1 2 3 4 5 6 7 8 |下标
 -----------------------
 4 2 6 3 8 1 5 7 8 |数值 
 ^               ^
 |               |
 b               e
 
 

 begin  //地址 
 end    //地址 
while (begin <= end)
{
 mid = (end-begin)/2 + begin; 
 if ()
 {
 }else if() 
 {
 }else 
 {
    break;
 }
}

return mid // 没找到 NULL 
  
  


               |     
               V
 0 1 2 3 4 5 6 7 8 |下标
 -----------------------
 4 2 6 3 8 1 5 7 8 |数值 
 ^     ^ ^       
 |     | |       
 b     e m         
 
 
作业:
1.快速排序 
 画一下流程图 
2.指针操作的算法 再写一下 
3.ptc题 
5.c语言100道 11~15 指法 

----------------------------------
 指针操作一维整型数组
1. 迭代的写法 
2. 快速排序 
  a.确定一个基准值 (选择的数组首元素)
  b.从右向左 找比基准值 小的值 
  c.从左往右 找比基准值 大的值 
  d.交换找到的两个值 
  e.重复 b ~ d ,直到 begin 与 end相遇 ,
    将相遇位置的值 与 基准位置上的值交换 
  f.针对 比基准值小的一部分 继续快速排序 
         比基准值大的一部分 继续快速排序 
--------------------------------------------
指针操作 一维 字符数组 
1. 字符型数组 --- 存放字符串的
char s[] = "hello";

['h' ] <---0x1000
['e' ]
['l' ]
['l' ]
['o' ]
['\0']

//谁能这块空间的地址 --- 数组名 
s --->怎么能保存s所代表的地址值 
     //s数组名 --- 数组首元素的地址 &s[0]
   --->地址的类型 
       char * 
       
char *p = s; //指针变量p 指向了 s (数组)

int puts(const char *s);


注意:
   char s[] = "hello"; //s的空间开在 栈上 
   char *s1 = "hello"; //s1的空间开在栈上,但是s1指向的"字符串常量"
                       //存放"字符串常量区"
                       
                       
  *s1 = *s1 - 32; //不能做 ? 原因是 s1 指向的数据是在 字符串常量区
                  //常量区的数据不能修改 
                  
  const int a = 10; //a成了只读变量 
  
  
  const char * s1 = "hello";   //表示 将 *s1 限定为只读
                               //如果,不希望修改 *s1 的数据
                               //一般 建议加上const 
                               
  char * const s1 = "hello";  //const 离谁近,就是限定谁的 
  
  char const *s1 = "hello"; //此时 还是 *s1 不能被修改  
  int *p; //int 代表基类型 
          //p 本身   
  char const * const s1 = "hello"; // *s1 和 s1 本身都不能被修改 
  
  
总结:
  1.const 离谁近,就限定谁 
  2.const 修饰的变量,为只读变量 //变量还是变量 只是成了只读的 
  
  
 
实现:
int Puts(char *s)
{
   *s = 'a'; //编译时,不报错 
}
int Puts(const char *s)
{
   *s = 'a'; //编译时,就会报错 
}

const char *s // *s 这种方式修改不了数据 

形参设计:
什么时候需要加const 
1.如果函数内部本身不需要通过*s 修改外面的数据 
此时,建议 统统加上const 

好处:
   1.可以将 错误提前 到 编译时 发现 
   2.提高了参数的适用性 
     可以接收数组名
     也可以接收 字符串常量  //const char * 
     

练习:
   strlen
   
   int Strlen(const char *s) 
   {
   }
   
   size_t Strlen(const char *s)
   {
   
   }
   
   
练习:
   Strcpy(char *dest,const char *src)
   
   
   
 
      char *Strcpy(char *dest, const char *src)
      { 
            char *ret = dest;
            ...拷贝过程 
            
            return ret; 
      }
      返回值:
          成功 表示的是dest的首地址 
          返回值设计成char * 是为了实现 字符串链式操作 
          

       
      char *strncpy(char *dest, const char *src, size_t n);
      功能 
          将src中的前n个字节拷贝到 dest中 
      注意:
          src 长度 > n     此时将前n个字节拷过去,此时dest也不会拷过去'\0'
          src 长度 < n     此时将src拷贝完成后,继续拷贝(添加的是'\0')直到完成了n次拷贝

     char * Strncpy(char *dest,const char *src, size_t n)
     {
        //这个函数,拷贝过程,n来控制 
        
     }     

        
   char src[] = "hello";
   strncpy(dest,src,10); //
   
练习:
   strcat 
   
   
       char *strcat(char *dest, const char *src);
       功能:
           拼接字符串 
           
       实现: //返回值 表示的是dest 存放字符串的空间的首地址 
        char *Strcat(char *dest, const char *src)
        {  
            char *ret =dest;
            //1.先定位到 dest 的 '\0'的位置 
            //2.从dest的 '\0'位置开始,拷贝字符串 
            //3.最终dest中 需要有 '\0'结束标志 
            return ret;
        }
       
       char *strncat(char *dest, const char *src, size_t n)
       功能:
            将 src中的前 n 个字符拷贝过去 
        注意:
           始终保证 dest中是一个字符串 ('\0')结束标志的 
           //使用时,需要确保dest空间足够 
           
           
      char * Strncat (char *dest, const char *src, size_t n)
      {
             char *ret =dest;
            //1.先定位到 dest 的 '\0'的位置 
            //2.从dest的 '\0'位置开始,拷贝字符串  //受到n的控制  
            //3.最终dest中 需要有 '\0'结束标志  
            return ret;
      }
      
-----------------------------------------
  Strcmp(const char *s1,const char *s2)
   

  int Strcmp(const char *s1, const char *s2)
  {
     功能:
        从左到右,逐个字符比较 
        
        遇到不同的字符时结束 或者 遇到 '\0'
        
        返回 最终不同的字符的差值 
  }
  

  int strncmp(const char *s1, const char *s2, size_t n);
  
  
  strncmp("hello","helloa",3); //
  
  
   int Strncmp(const char *s1, const char *s2, size_t n)
   {
       功能:
        从左到右,逐个字符比较 
        
        遇到不同的字符时结束 或者 遇到 '\0'  n个比完也停
        
        返回 最终不同的字符的差值 
      
   }
----------------------------------------------------------
总结:
   指针操作一维数组 
   
   思路,
   通过指针变量 获得数组所在空间的地址 
   
   int a[10];
   int *p = a;
   
   *(p+i) <=> a[i]


   函数实现的 

  printArray(int *a,int len)
  {
     
  }  
  
  Puts(const char *s)
  {
     
  }
  
 
 
---------------------------
指针 操作 二维数组 
int a[3][4] = {1,2,3,4,5,6,7,8,9};

a 还是首元素的地址 
  a[0]
  a[0][0]

int[4] a[3];  //a数组名 -- &a[0] 

a<=>&a[0] //值的角度 
    a[0] //数据类型 int[4] 
    &a[0] //int[4] * 指针类型 
          //标准C语法: int (*)[4]
          
定义变量:
int (*p)[4]; //数组指针
             //基类型 int[4]这种类型 ---数组类型 
                          
*p //等价与 a[0] 
   //相当于是 int[4] 这个一维数组的数组名 
   
*(*(p+0)+0) <=> a[0][0]

*(*(p+i)+j) <=> a[i][j]


//指针操作 一维字符型数组  和 二维整型数组:

char s1[] = "hello";
s1[0] = s1[0]-32;
 
char *s2  = "hello"; 
*s2 = *s2 - 32;

const char *s2 = "hello";

char *p; 
p = s1;
p = s2;

puts 
strlen
strcpy
strncpy 
strcat 
strncat 
strcmp
strncmp 


指针操作 二维数组 
1.二维数组
  c语言中,并不存在真正的二维数组
  二维数组本质上,是一维数组的 一维数组 
  
2.指针操作 二维数组 
 int a[3][4]; //需要一个什么类型的指针变量?
 
 
 int a[3][4] => int[4] a[3];//元素类型 int[4] 
             => //谁能代表数组空间的地址 --- 数组名 
             => 数组名 --- 代表的值 是数组首元素的地址, &a[0]
             => a[0]的数据类型 -- int[4]
             =>&a[0] --- int[4] *  //指针类型 
             =>c语言中的语法: int(*)[4] //数组指针     


int a[3][4]; 

a[0][0] => int类型 
a[0]    => int[4]类型

a数组名代表的值 是 数组中首元素的地址 &a[0] 

int(*p)[4] = a;

 p //int(*)[4]
*p //int降级   //int[4] 是 *p 整体对应的数据类型 
   //*p对应的值 是 a[0] ---内部的一维数组 数组名 --- &a[0][0] --> int *
 *p 降级 (将 行指针 降级成 列指针

 &*p
 &a[0]  //升级 (将 列指针 升级 为 行指针)


  *(*(p+i) + j)
 
          0         1       2       3 
a[0] | a[0][0] a[0][1] a[0][2] a[0][3]
a[1] | a[0][0] a[0][1] a[0][2] a[0][3]
a[2] | a[0][0] a[0][1] a[0][2] a[0][3]


int a = 10;
int *p = &a;
 
*p <=> a
             


作业:
1. 编写程序实现单词的倒置
   "how are you" -> "you are how"
    
   //1.整个字符串整体逆序 
    "uoy era woh"  
   //2.每个单词再逆序 
            V
    "uoy era woh"
    
2. 编写一个程序实现功能:
   将字符串”Computer Science”赋给一个字符数组,
   然后从第一个字母开始间隔的输出该字符串,用指针完成。
    Cmue cec

3. c语言100道 题 16~20

//指针操作字符型二维数组
char s[3][10] = {"hello","world","china"};

char s1[] = "hello";
char s2[] = "world"
char s3[] = "china"

char *p1 = "hello";
char *p2 = "world";
char *p3 = "china";


char s[5][10] = {"hello","world","china"}; //二维字符型数组 
char *s[ ] = {"hello","world","china"}; //指针的数组 --- 指针数组 

char(*p)[10] = s;

练习:
    准备三个字符串,找出字符串中最大的字符串 
    
练习:
    将二维数组中的字符串逆序 

    
char *s[ ] = {"hello","world","china"}; //指针的数组 --- 指针数组 

练习:
      从 s数组中找出最大的字符串 
练习: 
      实现逆序  
练习:
     实现排序 
     
//选择 
练习:
    查找
    
总结:
   指针数组  // char* s[10]
             // int* a[10]
   数组指针  int(*p)[4]
---------------------------------------------------------------
函数指针


int add(int a,int b)
{
    return a + b;
}

//函数名 --- 代表函数入口地址 


函数类型 

add函数的类型 ?

把函数名去掉,剩下的是函数的类型 
 
int (int a,int b) //这表示一类函数 --- 返回值为int型,形参为两个int型的函数
注意:
   函数指针变量使用时,可以不进行 *p运算
   直接使用变量名 + () 传实参,进行函数调用即可 

  

回调函数 (callback)
      
   
练习:
    写一个程序 实现加,减,乘,除
    以回调函数的形式,打印对应的结果 
    
    
    void processData(int a,int b, int (*pfunc)(int,int))
    {
        printf();
    }
    
    
   输入 一个表达式 
   
   1+2
   scanf("%d%c%d");
   
   ch
  
  
       void qsort(
                  void *base,   //表述要排序的数组的起始地址
                  size_t nmemb, //表示要排序的数组的元素个数
                  size_t size,  //表示要排序的数组的单个元素的大小
                  int (*compar)(const void *, const void *)//表示 排序 比较的依据
                  );

                  
    void * //万能指针
           //万能 表示 可以接收任意类型的 指针 
           //void 空类型 (无类型)
           
    void *p
    *p 

    练习:
        用qsort 对 二维字符型数组排序 


         
        
        
        
        


        
    
作业:
1、封装一个函数,统计一个字符串中出现指定单词的个数
    int  WordCnt(char  *pStr,  char  *pWord)
    {  
    }
                P
    "fdsahelfdashellofdashellofdashellofdashellohello"
    "hello"
    
    strncmp //
    
2、编写程序实现将"12345"  转化为12345 (数值)
  ‘1’ – ‘0’ ? 1
  ‘1’ ---49
  ‘0’ --- 48 
         
3.c语言100道题 20~25 
         
         

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值