C语言 day06 基础知识学习

一、指针的定义
     1.int* p = &a;
       指向改变: p = &b;
       指向内容改变:*p = 123;
     2.指针运算
         指针和指针相加是非法。 
         指针和指针相减是可以的;
          double a[2] = {0}; double* p=&arr[0]; double*q = &arr[1]
          q指向a[1], 计算q-p =1;表示相差几个double
          指针加常量是可以的 p+1是可以的  p+2越界是错误的; 
          指针可以++ ,--
     3.解引用
          得到指针所指向的内存空间的值。
          在指针前加*,例如 int a = 10; int* p = &a; *p=110;
          给指针赋值 指针指向发生改变。(非常重要)
    4.野指针
          野指针:不指向某一个固定位置的指针。要防止野指针出现
          空指针:指向NULL的指针。当指针不知道指向哪里时,则赋值为NULL;
          段错误:核心已转储,操作了一块不允许操作的内存控件,数组越界,操作了野指针,用空指针解引用。
         void指针:通用型指针,可以保存任何类型的地址。使用前需要进行强制类型转换 。(int*)p;
       5.常指针(面试题):只有const挨着指针的时候才是修饰指针,指针指向不可以改变。
       指针常量:
       const int *p = &a;   p=&b 允许;但*p= 22是错误的; b=22可以的;
       常量指针:
       int  * const p = &a; p=&b不允许;但*p= 22;
       const int* const p=&a; 指针的指向和所指向的内存空间都不允许修改
       补充:如果定义一个const变量,用指针指向变量后,可以通过解引用修改
          const int a = 123;
          int *p = &a;
          *p=222;  允许;
      补充: 直接用指针给指针赋值是可以的
                int a = 123;
                int *p = &a;
                int *q = p;  是可以的,可以用指针直接给指针赋 值。
二、二级指针
       1.二级指针:保存的是一级指针的地址;同样是8个字节。
         一级指针:保存的是变量的地址。
       2.格式:数据类型** 二级指针变量名 = &一级指针变量名;
         案例: int a = 123;       int*     p  = &a;       int**  pp= &p;
     命名规则:         pp_num;  ppNum;  PpNum;
       3.使用案例:
       int main(int argc,char *argv[])
       {
    int a = 123;
    int b = 234;
    int* p = &a;
    int * q = &b;
    printf("*p  %d\n",*p);
    int **  pp = &p;//二级指针不可以是野指针
    *pp = &b;  //pp=&&a 错误
    printf("*p =%d,**pp =%d\n",*p,**pp);
    pp = &q; //pp指向q,
    printf("*p =%d,**pp =%d\n",*p,**pp);
              return 0;

      } 

三:动态内存分配
    1. 总共分为:代码区,数据区,堆区,栈区
      根据地址从小到大分布如下:
      代码区:存放代码,不可以修改
      数据区: 静态常量区 :静态变量和常量
      全局变量区:已初始化的全局变量
      bss段:未初始化的全局变量
     堆区:动态内存申请,自己申请自己释放,先从低地址分配,在分配高地址。
     栈区:存放局部变量,自动申请自动释放,先从高地址分配,分配低地址空间,堆区和栈区无明显的分割线。
    2.  动态内存申请

       void *malloc(size_t size);
        作用:在堆空间中申请内存。
        参数: size 需要申请的内存大小
        头文件: #include <stdlib.h>
        返回值:申请到的内存空间的首地址。
                       int* p = (int*)malloc(8);
        注意:malloc申请的内存空间没有初始化时,存放的是乱值。
                  malloc的返回值需要强制类型转换才能使用。
                  malloc申请的内存空间,用完后需要释放。
        案例:用malloc申请一块内存,存放3个学生的信息,学生(id,age);代码如下:
          #include<stdio.h>
    #include <stdlib.h>
    #include <string.h>

    typedef struct student
    {
        int id;
        int age;
    }stu;

    int main(int argc,char *argv[])
    {
          //连续内存空间可以使用下标操作
          // 结构体数组 stu arr[3];  p=&arr[0];
    stu* p = (stu*)malloc(3*sizeof(stu));
    if(NULL == p)
    {
        perror("malloc");//打印错误信息
        exit(0);  //结束程序
    }
    memset(p,0,sizeof(stu)*3);//malloc申请的空间是乱值。
    for(int i = 0; i < 3;i++)
    {
        printf("请输入学生id和age\n");
        scanf("%d %d",&p[i].id,&p[i].age);
    
    }
    for(int j = 0;j < 3;j++)
    {
        printf("第%d个学生的id是%d age是%d\n",j+1,p[j].id,p[j].age);
    }
    //相同数据类型连续的集合
    int* pa = (int*)malloc(10*sizeof(int));
    printf("%p,%p\n",pa+1,pa);//相差4字节
    //如下会出现野指针
    //int a = 123;
    //pa = &a;   malloc申请的内存pa不再指向,导致内存泄露
       return 0;

}

        备注: 对于连续的内存空间可以使用下标操作(重点)
            int* p = (int*)malloc(5* sizeof(int));
                 p[0] = 123;
                 接受malloc返回值的变量指针,不允许被修改,不可以指向其他位置。如果修改会导致malloc申请的内存没有指针指向,内存泄漏。


      void free(void *ptr);
      作用:释放动态申请的内存空间,如果不释放内存泄漏
      参数:malloc返回的指针
      返回值: 无
     
       void *calloc(size_t nmemb, size_t size);
       作用:动态申请内存。申请的内存默认初始化成0.
       参数:nmemb申请的内存块的个数
                 size 每一块内存的大小
       返回值:申请的内存空间的首地址。
 
      void *realloc(void *ptr, size_t size);
      作用:重新申请内存空间。重新分配内存空间
      参数: ptr   从ptr地址开始申请,一般malloc的返回值。
                 size  一共申请size大小。
      返回值:重新申请的内存空间首地址。
      注意:realloc申请的内存不一定是从ptr开始,当ptr开始的内存不足以有size个的时候,会先释放当前内存后,重新在另外一个区域申请。
                
四、指针和函数的关系
     1.函数:存在代码区(不允许修改)
     2.函数名: 就是函数的首地址。  void  fun(int x);
     3.练习: 请写一个函数,修改两个参数的值。形参和实参都需要修改。
                  void chage(int *a ,int * b);  
      4.注意:不可以返回局部变量的地址。
                   malloc申请的内存空间函数结束依然存在。只有free释放之后才不会存在。 

作业:函数中的可变长参数学习 例如 void  fun(int a,...);
gdb使用:给大家一个工程,请查看其中变量的值,和调用顺序。

预习:fopen fread fwrite fgets fputs fgetc fputc fclose fseek rewind  ftell 的使用。

    1.fopen:(打开文件)
头文件:#include<stdio.h>
原型:FILE *fopen(const char *path, const char *mode);
返回值是一个FILE型的指针(文件指针)
**path:**文件名
mode: 权限(文件使用方式) 比如可读,可写,可读可写…
r只读方式打开一个文本文件
rb 只读方式打开一个二进制文件
w 只写方式打开一个文本文件
wb 只写方式打开一个二进制文件
a 追加方式打开一个文本文件
ab 追加方式打开一个二进制文件
r+ 可读可写方式打开一个文本文件
rb+ 可读可写方式打开一个二进制文件
w+ 可读可写方式创建一个文本文件
wb+ 可读可写方式生成一个二进制文件
a+ 可读可写追加方式打开一个文本文件
ab+ 可读可写方式追加一个二进制文件

2.fwrite:(写入文件)
头文件:#include <stdio.h>
原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
ptr:写入的内容 (指针)
**size:**写入多大
**nmemb:**写多少次
**stream:**文件指针
成功写入则返回实际写入的数据个数 就是返回写入次数(nmemb的值)
注意:写完数据后要调用fclose()关闭流,不关闭流的情况下,每次读或写数据后,文件指针都会指向下一个待写或者读数据位置的指针。

3.fseek:(移动光标)
头文件:#include <stdio.h>
原型:int fseek(FILE *stream, long offset, int whence);
stream:文件指针
offset:偏移量(正数表示正向偏移,负数表示负向偏移)
whence:光标位置
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
成功,返回0,失败返回非0值,并设置error的值,可以用perror()函数输出错误。


4.fread:
头文件:#include<stdio.h>
原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
**ptr:**指向要读取的数组中首个对象的指针
**size:**读取多大
**nmemb:**读取多少次
**stream:**文件指针
返回值 是读取了多少次的次数。

5.fclose(FLILE* mm)
关闭文件直接 fclose(文件指针);


char *fgets( char *str, int num, FILE *stream );
函数fgets()从给出的文件流中读取[num - 1]个字符并且把它们转储到str(字符串)中. fgets()在到达行末时停止,在这种情况下,str(字符串)将会被一个新行符结束. 如果fgets()达到[num - 1]个字符或者遇到EOF, str(字符串)将会以null结束.fgets()成功时返回str(字符串),失败时返回NULL.


int fgetc(FILE *stream);
函数说明:从stream文件指针所指向的文件读取1个字符,通过函数的返回值返回。例如:
char c = fgetc(fp);
就是从fp指向的文件,读取1个字符的数据,通过函数返回值,设置给字符变量c。
int fputc(int c, FILE *stream);
函数说明:把参数c表示的字符,写入stream文件指针所指向的文件。

char *fgets(char *buf, int size, FILE *stream);
函数说明:从stream函数指针所指向的文件中,读取size-1个字节到buf缓存,不是读取size个字节,因为,buf[ ]缓存的第size个字节存放字符串结束符,形成一个有效的字符串。
当读取到换行符时,或者到达文件末尾时,结束文件的读取操作。
返回值:读取数据成功,返回存储数据的缓存地址,就是参数buf的地址。如果读取数据失败,返回NULL。例如,读取文件完,没有数据可以再读取,则返回NULL。


int fputs(const char *buf, FILE *stream);
函数说明:把参数buf指针指向的字符串,写入到stream指向的文件。参数buf是一个字符串指针,必须指向一个合法的字符串。合法的字符串是以字符串结束符'\0'结束,表示一个字符串的结尾。
返回值:写入数据成功,返回成功写入的字符个数。失败返回EOF标记。
     

      

ftell 函数的原型为:lng ftell(FILE *fp);
该函数用于得到文件位置指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁前后移动,程序不容易确定文件的当前位置。在使用 fseek 函数后,再调用函数 ftell 就能非常容易地确定文件的当前位置


rewind是C 程序中的库函数。
功 能: 将文件内部的位置指针重新指向一个流(数据流/文件)的开头。
注意:不是文件指针而是文件内部的位置指针,随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。
      

自己的方法:
fprintf();
fscanf();


void write_student_information(int a)   //向文件中写入信息
{
    int i,k;
    FILE* fp;
    fp=fopen("gw.txt","r+");
    if(fp==NULL)
    {
        printf("打开文件失败!\n");
        return;
    }
//    fprintf(fp,"学号\t 姓名\t 性别\t 年龄\t 语文成绩\t 数学成绩\t 英语成绩\t  总成绩\n");
    for(i=0;i<a;i++)
    {    
        fprintf(fp,"%d\t\t %s\t %s\t\t %d\t\t %d\t\t\t %d\t\t %d\t\t  %d\n",W[i].id,W[i].name,W[i].sex,W[i].age,W[i].chinese,W[i].math,W[i].english,W[i].sum);
    }
    fclose(fp);
}

void read_student_information()   //从文件中读取内容
{
    int n,i=0;
    FILE* fp;

    fp=fopen("gw.txt","r+");
    if(fp==NULL)
    {
        printf("打开文件失败!\n");
        return;
    }

    while(fscanf(fp,"%d %s %s %d %d %d %d %d",&W[i].id,W[i].name,W[i].sex,&W[i].age,&W[i].chinese,&W[i].math,&W[i].english,&W[i].sum)!=EOF)
    {
        i++;
    }
    vacancy=i;
    fclose(fp);
}


          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值