美文网首页
C语言续

C语言续

作者: 零散的蒲公英 | 来源:发表于2019-12-16 16:43 被阅读0次
    • 指针

      指针的定义
      指针的类型
      指针的指向内容
      指针的运算
      数组与指针
      指针与函数
    • 动态分配内存

    • 结构体

    • 文件读写

    • 头文件与实现文件实例之计算器

    • 文件操作训练之字符串查找

    指针

    指针的定义

    • 指针是一个变量
    • 指针只能存地址
    • 指针占据8个字节空间

    总结:指针是一种保存变量地址的变量

    int main(){
      int *a;
      char *b;
     printf("a的大小:%d\n", sizeof(a));
     printf("a的地址:%p\n",a);
     printf("%d\n", sizeof(b));
    }
    
    输出:
         a的大小:8
         a的地址:0000000000000001
         8
    
    • 指针的声明
    int *p;//声明一个int类型的指针p
    char *p;//声明一个char类型的指针p
    int *arr[5];//声明一个指针数组,数组内5个元素,每个元素都是一个指向 int 类型对象的指针
    int **p;//声明一个指针p,指针指向一个int类型的指针
    
    • 指针的声明相对于普通变量的声明多了一个一元运算符 “*”。
    • 运算符 “*” 是间接寻址或者间接引用运算符。当它作用于指针时,将访问指针所指向的对象。
    • p 是一个指针,保存着一个地址,该地址指向内存中的一个变量;*p 则会访问这个地址所指向的变量。
    • 声明一个指针变量并不会自动分配任何内存。
    • 在对指针进行间接访问之前,指针必须进行初始化:或是使他指向现有的内存,或者给他动态分配内存,否则这个指针会变成野指针。
    • 指针初始化
    /* 方法1:使指针指向现有的内存 */
    int a = 5;
    int *p = &a; 
    

    *: 定义的时候表明是一个指针变量,使用的时候表示取地址的值
    &: 取某一个变量地址

    指针初始化
    /* 方法2:动态分配内存给指针 */
    int *p;
    p = (int *)malloc(sizeof(int) * 10);    // malloc 函数用于动态分配内存
    free(p);                                  // free 函数用于释放一块已经分配的内存
    

    指针的初始化实际上就是给指针一个合法的地址,让程序能够清楚地知道指针的指向,而不至于变为野指针

    指针的类型

    判断指针类型的方法:去掉星号*和变量名就是指针的类型

    int p;//P是一个普通的整型变量*
    
    int *p;//P是一个返回整型数据的指针
    

    P与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int型.

    int p[3]; //所以P 是一个由整型数据组成的数组
    

    P与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的.

    int *p[3]; //P 是一个由返回整型数据的指针所组成的数组
    

    P与[]结合,因为其优先级比高,所以P 是一个数组,然后再与结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的.

    int (*p)[3]; //P 是一个指向由整型数据组成的数组的指针
    

    P与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.

    int **p;//P是一个指向整型数的指针的指针(二级指针)
    

    P与结合,说是P 是一个指针,然后再与结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.

    int p(int);//P是一个参数和返回值都为int的一个函数
    

    P与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据.

    int (*p)(int);//P 是一个指向有一个整型参数且返回类型为整型的函数的指针
    

    P与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型.

    指针的指向内容

    指针存储的内容为变量的地址,也就是说指针的是一个指向作用,指向变量所存储的内容

    int main(){
    int a = 5;
    int *p = &a;
    return 0;
    }
    
    指针指向

    指针的运算

    • 指针+(-)整数

    可以对指针变量 p 进行 p++、p--、p + i 等操作,所得结果也是一个指针,只是指针所指向的内存地址相比于 p 所指的内存地址前进或者后退了 i (对应指针指向类型对应大小)个操作数。

    int main(){
       char a = '1';
       char *p = &a;
       printf("p:%p\n",p);
       p++;
       printf("p++之后结果:%p\n",p);
       p--;
       printf("p--之后结果:%p\n",p);
       p+=5;
       printf("p+5之后结果:%p\n",p);
      return 0;
    }
    
    输出:
             p:000000000062FE17
    p++之后结果:000000000062FE18
    p--之后结果:000000000062FE17
    p+5之后结果:000000000062FE1C
    

    p 是一个 char 类型的指针,指向内存地址0062FE17处。则 p++ 将指向与 p 相邻的下一个内存地址,由于 int 型数据占 4 个字节,因此 p++ 所指的内存地址为 1000000b。其余类推。不过要注意的是,这种运算并不会改变指针变量 p 自身的地址,只是改变了它所指向的地址

    数组与指针
    • 数组的内存空间:
      数组
      数组的数组名其实可以看作一个指针,因为数组名是指向数组的第一个元素,上面num数组指向的也就是第一个元素1,数组名本身是没有占有内存的
    int array[10]={0,1,2,3,4,5,6,7,8,9},value;  
    value=array[0]; //也可写成:value=*array;  
    value=array[3]; //也可写成:value=*(array+3);  
    value=array[4]; //也可写成:value=*(array+4);
    

    另外一种解释是将数组名指向数组的第0个单元,那么(array+n)也就是一个指向数组里的第n个单元的指针

    int main(){
      int num[9] = {1,2,3,4,5,6,7,8,9};
      int *p = num;
      *p++;
       int a = (*p)++; //2
       int b = *(p++); //3
       printf("%d\n%d\n",a, b);
    
    输出:
         2
         3
    

    p指向的是数组的首地址,也就是数组的第一个元素,那么p++之后也就是对指针p前进了4(int类型)个操作数,而数组是分配了连续空间,所以相对地址是加减也就是数组元素的位置变换

    • 指针数组

    指针数组, 是一个数组,数组中的每一个元素都是指针

    int *data[10]={NULL};//注意,一定要初始化
    for(int i = 0; i < 10; ++ i){
       data[i] = (int*)malloc(sizeof(int) * 10);
    }
    data[1][2] = 1;
    

    对于上面的定义和初始化, data是指针数组的名字, 也就是指向指针数组首元素的指针. (指针的指针). data[i] 是data这一个数组的第i个元素, 也就是一个指向int的指针
    指针可以当成数组来使用, data[i][j]*(data[i]+j)是等价

    经过上述代码创建的一个指针数组data的使用和int data[10][10]基本相同, 区别在于后者保证数组和数组之间的内存地址是连续的. data[0][9] 和 data[1][0] 是连续的, 而如果使用指针数组方式创建的data, 不能保证 data[0][9] 和 data[1][0] 在内存上连续

    • 数组指针

    数组指针,是一个指针,它指向一个数组

    int (*)data[10] = NULL;//一个指向长度为10的int数组(int [10])的指针
    //一般, 我们并不会使用到数组指针
    //一般使用:
    int func(int data[][20]){
    }
    

    数组作为参数传入函数的时候, 对于被调用的函数参数就是指针. 因此, 这里参数是一个"元素为int[20]"的数组(数组的数组), 因此, 在函数内部, data实际上就是一个"指向int[20]"的指针(int(*)[20])

    • 尽量不要对数组和指针使用sizeof
    • 当且仅当如malloc(10*sizeof(int))时使用sizeof

    指针与函数

    • 函数指针是指向函数的指针变量
    • 通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数
    • 函数指针可以像一般函数一样,用于调用函数、传递参数
      函数指针声明
    typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
    

    下面实例声明了函数指针变量 p,指向函数 max:

    #include <stdio.h>
    int max(int x, int y){
       return x > y ? x : y;
    }
    
    int main(void){
       /* p 是函数指针 */
       int (* p)(int, int) = & max; // &可以省略
       int a, b, c, d;
       printf("请输入三个数字:");
       scanf("%d %d %d", & a, & b, & c);
       /* 与直接调用函数等价,d = max(max(a, b), c) */
       d = p(p(a, b), c);  
       printf("最大的数字是: %d\n", d); 
       return 0;
    }
    
    输出
    请输入三个数字:1 2 3
    最大的数字是: 3
    

    函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。也就是说回调函数是由别人的函数执行时调用你实现的函数


    • 下面事例中 populate_array 函数定义了三个参数,其中第三个参数是函数的指针,通过该函数来设置数组的值。
    • 实例中我们定义了回调函数 getNextRandomValue,它返回一个随机值,它作为一个函数指针传递给 populate_array 函数。
    • populate_array 将调用 10 次回调函数,并将回调函数的返回值赋值给数组
    #include <stdlib.h>  
    #include <stdio.h>
    
    // 回调函数
    void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)){
       for (size_t i=0; i<arraySize; i++)
           array[i] = getNextValue();
    }
    
    // 获取随机值
    int getNextRandomValue(void){
       return rand();
    }
    
    int main(void){
       int myarray[10];
       populate_array(myarray, 10, getNextRandomValue);
       for(int i = 0; i < 10; i++) {
           printf("%d ", myarray[i]);
       }
       printf("\n");
       return 0;
    }
    
    输出:
    41 18467 6334 26500 19169 15724 11478 29358 26962 24464
    

    动态分配内存

    • 动态分配内存的原因

    1.存储的数据 需要延长生命周期
    2.一个指针变量需要存储数据,变量本身只能存地址,不能存数据,需要分配内存空间来存储数据

    • C 语言为内存的分配和管理提供了几个函数(导入库为<stdlib.h>)
      提供的函数
    • 内存分配

    如果使用指针变量接收数据,必须先为这个指针变量分配一片指向的内存空间

     char *name ;
    

    用malloc(memory alloc)申请内存空间

     name = (char *)malloc(10*sizeof(char));
    

    使用realloc动态改变已经分配内存的大小

    name = (char *)realloc(name, 20*sizeof(char));
    

    使用完毕必须自己手动释放内存

     free(name); 
    

    结构体

    结构体的优势

    可以存储多种数据数据的变量

    结构体定义

    struct student{//定义一个学生结构体
           int age;
           char sex;
           char name[10];
      }:
    
    • student是结构体名称
    • int age等是标准的变量定义

    结构体定义变量

    struct student LiMing;//struct student是一种结构体类型类似于int,float类型等
    struct student *p = &LiMing;
    

    结构体的访问

    LiMing.age = 18;
    LiMing.sex ='m';
    LiMing.name ="李明";
    //指针使用->访问元素
    p->age = 29;
    p->sex = 'f';
    

    结构体内存大小计算

    对齐方式

    • 内存小的数据类型向内存大的数据类型对齐
    int main(){
    struct A{
           char a;
           int b;
       };
    struct B{
           double a;
           int b;
           char c;
    };
    struct Person{
           char *name;        
           double score;
           int age;
       };
    struct Student{
           char name[10];
           int age;
           double score;
       };
    printf("%d   %d\n",sizeof(struct A),sizeof(struct B));
    printf("%d   %d\n",sizeof(struct Person),sizeof(struct Student));
    return 0;
    }
    
    输出:
     8   16
     24  24
    
    • 在结构体A当中,char类型向int类型靠齐
      结构体A
    • 在结构体B当中,char ,int类型为double类型靠齐,由上自下的补齐,因为int类型占8位之后任有4位空着,这时候char会类型会自动补齐,占据剩下的4位
      结构体B
    • 在结构体Person当中,字符型指针和double相同大小,int类型向double靠齐,自上而下,没有空位让int类型补齐
      结构体Person
    • 在结构体Student当中,int类型和char类型向double靠齐,int类型分配8个字节,但前4位空着,char类型数组最后两位补齐,剩余两个空位
      结构体Student

    文件读写

    • 打开文件
    //fopen函数
    FILE *fopen( const char * filename, const char * mode );
    //fopen函数使用
    FILE *fp = fopen("/Users/pengxiaodong/Desktop/test.txt", "r");
    
    mode的值
    • 写入文件
    //fputc函数
    int fputc( int c, FILE *fp );
    //函数使用
    fputc('a', fp);
    //fputs函数
    int fputs( const char *s, FILE *fp );//按照一定的格式写入内容
    //函数使用
    fputs("jack", fp);
    

    • 读取文件
    //fgetc函数
    int fgetc( FILE * fp );
    //函数使用
    fgetc(fp);
    

    • 关闭文件
    //关闭文件
    int fclose( FILE *fp );
    //fclose函数使用
    fclose(fp);
    

    头文件与实现文件实例之计算器

    • 计算器的头文件Calculator.h
    #include <stdio.h>
    
    //头文件里面声明函数
    //加法 
    int add(int a, int b);
    //减法 
    int minus(int a, int b);
    //乘法 
    int multiply(int a, int b);
    //除法 
    int devide(int a, int b);
    
    • 计算器的实现函数Calculator.cpp
    //实现文件
    //1. 先导入需要实现的头文件
    //2. 实现这个头文件里面的所有方法 
    #include "Calculator.h"
    
    //加法 
    i>nt add(int a, int b){
      return a + b; 
    } 
    //减法 
    int minus(int a, int b){
      return a - b;
    } 
    //乘法 
    int multiply(int a, int b){
      return a * b;
    } 
    //除法 
    int devide(int a, int b){
      if (b == 0){
          return 0;
      }else{
          return a / b;
      }
    } 
    
    • 计算器main函数入口
    #include <stdio.h>
    
    //1.程序的入口函数
    //main.cpp 为了让阅读者 
    //知道我这里面写的是入口函数
    
    //2. 将不同的功能模块用不用的.h .cpp来封装
    //.h 头文件 函数声明 (不能实现)
    //.cpp .c 实现文件 函数的具体实现{} 
    
    //3.导入头文件进行使用 
    #include <stdio.h>
    
    //头文件里面声明函数
    //加法 
    int add(int a, int b);
    //减法 
    int minus(int a, int b);
    //乘法 
    int multiply(int a, int b);
    //除法 
    int devide(int a, int b);
    /*
    1.预编译 
    */ 
    int main(){
      int result = add(1,1);
      printf("1 + 1 = %d\n", result);
      printf("1 + 1 = %d\n", add(1,1));
      printf("2 - 1 = %d\n", minus(2,1));
      printf("2 * 2 = %d\n", multiply(2,2));
      printf("2 / 2 = %d\n", devide(2,2));
      return 0;
    }
    
    void test(){
      
    }
    
    
    

    文件操作训练之字符串查找

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    
    //从终端接收字符串 返回这个字符串的首地址
    char *inputName(){
       //1.定义一个指针变量 指向字符串的首地址
       char *pName = NULL;
       //2.接收输入
       int i = 0;
       //3.提示操作
       printf("请输入人名:");
       while (1) {
           //接收一个字符
           char c = getchar();
           //判断这个字符是不是\n
           if (c == '\n') {
               //输入结束
               break;
           }
           //判断是不是第一个字符
           if(i == 0){
               //使用malloc分配内存
               pName = (char *)malloc(1*sizeof(char));
               //判断是否分配成功
               if(pName == NULL){
                   exit(EXIT_FAILURE);
               }
               pName[0] = c;
           }else{
               //使用realloc在之前的基础上加一个
               pName = realloc(pName, (i+1)*sizeof(char));
               //判断是否分配成功
               if(pName == NULL){
                   exit(EXIT_FAILURE);
               }
               pName[i] = c;
           }
           
           i++;
       }
       //将当前的字符串首地址返回
       return pName;
    }
    
    //是否继续
    bool isContinue(){
       printf("是否继续(y/n)?:");
       while (1) {
           char c = getchar();
           getchar();
           if (c == 'y'){
               return true;
           }else if(c == 'n'){
               return false;
           }else{
               printf("输入格式不对,请重新输入:");
           }
       }
    }
    //初始化整个数组
    char **initNames(int *pNum){
       //1.定义指针变量指向每个名字的首地址的内存
       char **pHead = NULL;
       
       //2.记录元素个数
       int i = 0;
       while (1) {
           //判断是不是第一个
           //第一个使用malloc分配内存
           if (i == 0) {
               pHead = malloc(1*sizeof(char *));
               if (pHead == NULL) {
                   exit(EXIT_FAILURE);
               }
               
               //输入人名 将地址放到pHead对应位置
               pHead[0] = inputName();
           }else{
               //使用realloc重新再增加一个元素
               pHead = realloc(pHead, (i+1)*sizeof(char *));
               if (pHead == NULL) {
                   exit(EXIT_FAILURE);
               }
               //输入人名 将地址放到pHead对应位置
               pHead[i] = inputName();
           }
           
           i++;
           
           //是否继续
           bool result = isContinue();
           if (result == false) {
               break;
           }
       }
       
       *pNum = i;
       return pHead;
    }
    
    void show(char **pHead, int num){
       printf("输入%d个名字:\n",num);
       int i; 
       for ( i = 0; i < num; i++) {
           printf("%s\n",pHead[i]);
       }
       printf("\n");
    }
    
    int main(int argc, const char * argv[]) {
       char **pHead = NULL;
       int count = 0;
       pHead = initNames(&count);
       show(pHead, count);
       return 0;
    }
    

    相关文章

      网友评论

          本文标题:C语言续

          本文链接:https://www.haomeiwen.com/subject/uktygctx.html