C语言学习笔记 1

中央处理器(CPU):

包括运算器、控制器、寄存器

Enum:

Enum: 枚举类型

System函数:

       执行系统命令。如pause、cmd、calc、mspaint、notepad.....

System(“pause”);//暂停

System(“calc”);//打开计算机

System(“cls”);//清屏命令

System(“mspaint”);//打开画板

程序详细编译过程

  1. 预处理:

-E   生成 xxx.i    预处理文件

       gcc  -E xxx.c -o xxx.i

  1. .头文件展开----不检查语法错误,可以展开任意文件
  2. 宏定义替换-----将宏名替换为宏值
  3. 替换注释 ------变成空
  4. 展开条件编译---根据条件展开指令;

  1. 编译:

  -S   生成 xxx.s    汇编文件

          gcc -s xxx.i -o xxx.s

  1. 逐行检查语法错误。【重点】。---整个编译4步骤中最耗时的过程
  2. 将C程序编译成 汇编指令,得到.s 汇编文件

  1. 汇编:

  -c   生成 xxx.o    目标文件

         gcc -c xxx.s -o xxx.o

  1. 将汇编指令翻译成对应的二进制编码。

  1. 链接:

 无  生成 xxx.exe   可执行文件

        gcc  xxx.o -o xxx.exe

  1. 数据段合并
  2. 数据地址回填
  3. 库引入

调试程序:

  1. 设置断点,F5启动该调试;
  2. 停止的位置,是尚未执行的指令
  3. 逐语句执行下一条(F11) :进函数内部,逐条执行跟踪。
  4. 逐过程执行下一条(F10) :不进函数内部,逐条执行程序。
  5. 添加监视:

            调试-->窗口--》;输入监视变量名。自动监视变量值的变化

常量与变量

常量:不会变化的数据

  1. “hello”、’A’、-10、3.1415926(浮点常量)
  2. #define PI 3.1415926   定义宏  语法:#define 宏名 宏值。
  3. Int a=10;    const int a = 10;   const 关键字: 该关键字修饰常量,表示只读

变量:会变化的数据

      定义语法:类型名 变量名 = 变量值;(一般方法)

      变量三要素: 类型名、变量名、变量值  //int r = 3;

      变量的定义:int a = 40;

      变量的声明:in a;   没有变量值的变量定义 叫做声明。

                  extern int a;  添加关键字 extern

  1. 变量定义会开辟空间,变量的声明不会开辟空间;
  2. 当编译器编译程序时,在变量使用之前,如果没有看到变量定义,编译器会自动找寻一个变量声明提升成为定义,如果该变量的声明前有extern 关键字,则无法提升。

定义变量时,尽量不要重名。

标识符

标识符:  变量和常量的统称;

          命名规则:    1.通常常量用大写,变量使用小写,大小写严格分明

                        2.只能使用字母、数字、下划线(-)命名标识符,且数字不能开头   a-z/A-Z/0-9/_

数据类型

整型:

     Int 类型:      4字节    打印格式:%d

               Int 名 = 值;

     float 类型      4字节     

               float 名 = 值;

     short 类型:    2字节    打印格式:%hd

               short 名 = 值;

     long 类型:     4字节

               long 名 = 值;  打印格式:%ld  

     long long 类型: 8字节

               long long 名 = 值;打印格式:%lld

sizeof 关键字

   sizeof 关键字 不是函数,  作用:求变量/类型的大小  返回一个无符号整数,使用%u接收返回值

               用法:(1).sizeof(类型名)   //sizeof(int);

  1. .sizeof(变量名)   //sizeof(a);

无符号整型:

unsigned 无符号    只有数据量,没有方向(没有正负)

      unsigned Int 类型:      4字节

               unsigned Int 名 = 值;      打印格式:%u

     unsigned short 类型:    2字节

               unsigned short 名 = 值;

     unsigned long 类型:     4字节

              unsigned long 名 = 值;     打印格式:%lu

     unsigned long long 类型: 8字节

              unsigned long long 名 = 值;  打印格式:%llu

char

字符类型:一字节  本质是ASCII码

     存储一个字符 ‘A’、’a’、’#’、’65’

char 类型:一字节 8个bit位,数值位有7个。

     有符号:-2^7--- 2^7-1== -2^(8-1) -- 2^(8-1)-1

            -->  -128 -- 127

      

     无符号:0~2^8-1

             -->0~65535

不要超出该数据类型的存储范围;

Short 类型:

2字节16 bit

      有符号:-2^15 --- 2^15-1 == -2^(16-1) -- 2(16-1)-1

             -->-32768~32767

Int 类型:

4字节    -2^(32-1)  --  2^(32-1)-1

      有符号:

             -->-2147483648~2147483647

      

       无符号:      0~2^32-1

              -->0~4294967295

long 类型

4字节

       有符号:

           --> -2147483648~2147483647

无符号:      0~2^32-1

              -->0~4294967295

longlong类型: 

8字节

       有符号:

              -->2^(63)~ 2^(63)-1

       

       无符号:

             -->0 - 2^63-1

转移字符:

printf(“n=%08.3f\n”,n);//输出含义为:显示8位数(包含小数点),不足8位用0填充。并且保留3位小数。对第4位做四舍五入。

进制和转换:

         10进制转2进制:  ---除2取余法

         10进制转8进制    ---除8反向取余

         10进制转16进制   ---除16反向取余

         Int a = 56;  ----111000

         

         2进制转10进制

         2^10=1024

         2^9 =512

         2^8 =256

         2^7 =128

         2^6 =64

         2^5 =32

         2^4 =16

         2^3 =8

         2^2 =4

         2^1 =2

        

8进制转10进制

056, 0开头,每位数0~7之间

0  5  6  

2  1  0

0*8^2+5*8^1+6*8^0=46  --》46  再转二进制

八进制转二进制

 按4 2 1 码将每个八进制位展开

               4  2  1

         5     1  0  1

056                       -->101110

     6     1  1  0

二进制转八进制:

  4 2 1逆运用 从后往前分为三位一组 高位不足三位补0

101 100 110 001--> 5461

 5  4  6  1

16进制转10进制:

语法:以0x开头,每位 取0-9/A-F/a-f

  1. -10
  2. -11
  3. -12
  4. -13
  5. -14
  6. -15

Eg:0x1A-->    1-->1*16^1

  1. ->10*16^0     1A-->26

0x13F     1*16^2

          3*16^1

          15*16^0        13F-->

16进制--转2进制

8 4 2 1 运用

Eg:  0x1A-->   1:  0001   A: 1010     -->00011010

     0X13F    1:  0001  3:0011   F: 1111    -->000100111111

2进制转8进制

方法: 4 2 1 逆运用 从后往前分成四个一组,高位不足4位补0。

不能给变量直接赋值二进制;

Printf输出格式:

%d:有符号整型

%u:无符号整型

%x:输出十六进制

%c:输出字符

%s:输出字符串

存储知识:

1bit位  就是一个  二进制位

一个字节 1B  =  8bit位

1KB = 1024B

1MB = 1024KB

1GB = 1024MB

1TB = 1024GB

源码 反码 补码:

源码:

43  -->00101011

-43   -->10101011

反码:

43  -->00101011

-43 -->10101011

     11010100

补码(现今计算机采用的存储方式)

43  -->00101011      :正数不变

 -43  -->11010101      :负数,最高位表符号位,其余取反+1;

人为规定:0000000  ——》 0

         10000000  ——》  -128

  

特殊修饰符:

Extrn:

     表示声明,没有内存空间,不能提升。

Const:

限定一个变量为只读变量

Volatile:

      防止编译器优化代码

      Volatile int fig = 0;

Register

     定义一个寄存器变量

字符串:

c语言中,用双引号引着的一串字符,称之为字符串,一定有一个结束标记”\0”

Char ch = ‘A’;一个字符

“abc” --->’a’ ‘b’ ‘c’ ‘\0’

printf();函数  -->%s  挨着从字符串的第一个字符开始打印,打印到’\0’结束

‘a’ 不等价 ”a” (‘a’ ’\0’)

            --》%m.n : 打印实型,一共有m 位(整数、小数、小数点),n位小数;

            --》%0m.n :打印实型,一共有m位,不够的前面添0,保留n位小数;

%% : 输出一个 % 转义字符 ’\’  对  ‘%’  无效;

%Ns : 显示N个字符的字符串。不足N 用空格向左填充,

%0Ns : 显示N个字符的字符串。不足N 用0向左填充,

%-Ns :  显示N个字符的字符串。不足N 用空格向右填充;

putchar函数:

       输出一个字符到屏幕;

       直接使用ASCII码

       不能输出字符串

       ‘abc’ 既不是一个有效字符。也不是一个有效字符串。

Scanf(); 函数

        从键盘接收用户输入

  1. 接收 整数 %d

           Int a,b,c;  //创建变量空间等待接受用户输入

           Scanf(“%d %d %c\n”,&a,&b,&c);

  1. 接收字符 %c

       Char  a, b, c;

       Scanf(“%c %c %c”,&a, &b,&c);

  1. 接收字符串 %s

         char str [10];  //定义一个数组,用来接收用户输入的 字符串。

         scanf(“%s”, str);   //变量名要取地址符传递给scanf,数组名本身表示地址,不用&符。接收字符串:

  1. .具有安全隐患。如果储存空间不足,数据能存储到内存中,但不能被保护【空间不足不能使用】
  2. . scanf函数接收字符串时,碰到 空格和换行会自动终止,不能使用scanf接收带有空格的字符串

getchar函数

           函数从标准输入里读取下一个字符,返回值为int类型

           直接使用ASCII码

           出错直接返回EOF;

算数运算符

不允许对小数、0 取余,对负数取余,结果为余数的绝对值 。

前缀自增:++a // a=a+1;先自增,后取值;

后缀自增:a++ //     先取值,后自增;

逻辑运算符:

与:&&

或:||

非: !

三目运算符:

 ?:   表达式1  ?  表达式2 :表达式3

                     a>b?  max=a : max=b;

        

if与switch

If  分支语句,匹配一个范围,属于模糊匹配

   

Switch分支: 精确匹配

循环结构:

Break;//跳出一重循环 for , while , do while;

       // switch 语句,防止case穿透;

Continue:作用:结束当前(本次)循环,continue之后的循环体在本次不执行;

go to [了解]

用法:1)。先定义一个标签

      2)。go to 标签 跳转到标签处 (只在函数内部生效)

数组

数组:相同数据类型的有序结合。

Int ar[10]={1,2,3,4,5,6,7,8,9,10};

各个元素的内存地址 连续。

数组名为地址,是数组首元素的地址 arr=&arr[0];

数组的大小:printf(“数组大小:%u\n”,sizeof(arr));

Printf(“数组元素的大小:%u\n”,sizeof(arr[0]));

Printf(“数组元素个数:%u\n”,sizeof(arr)/sizeof(arr[0]));  

 Int arr[10]={1,2,3,4,5};  剩余未初始化的元素,默认 0 值。

 Int arr[10]={0};  初始化一个全为0 的数组

 Int arr[ ]= {1,2,3,4};   编译器自动求取元素个数

 Int arr[ ]={0};  只有一个元素0;

  Int arr[10];

  arr[0]=5;

  arr[1]=6;

  arr[2]=7;//其余 元素未被初始化, 默认值 随机数

二维数组:

Int arr[3][2] ={{1,2},{3,4},{5,6}};

等价于

Int arr[3][2] ={

{1,2},

{3,4},

{5,6}

};

二维数组大小:sizeof(arr);

二维数组第一行大小:sizeof(arr[0]);

二维数组一个元素大小:sizeof(arr[0][0]);

二维数组行数:sizeof(arr)/sizeof(arr[0]);

二维数组列数:sizeof(arr[0])/sizeof(arr[0][0]);

地址合一:数组的首地址==数组的首元素地址== 数组的首行元素地址;

Printf(“%p\n”,arr); == printf(“%p\n”,&arr[0][0]); == printf(“%p\n”,arr[0]);

 字符串 char str[6]=”hello”;//需要给\0 留空间;

printf(“%s”);//输出字符串,必须碰到\0结束;

字符串获取scanf:

    注意:

  1. 用于存储字符串的空间必须足够大,防止溢出;
  2. 获取字符串,%s, 遇到空格 和 \n 终止;

借助”正则表达式”可以获取带有空格的表达式 scanf(“%[^\n]”,str);

字符串操作函数:

 gets:

 获取一个字符串,返回字符串的首地址,可以获取带有 空格的字符串。

      char *gets(char *s);

           参数:用来存储字符串的空间。

           返回值:返回实际获取到的字符串的首地址。

fgets: 

获取一个字符串  预留 \0 的存储空间

      char *fgets(char *s, int size, FILE *stream);

           参1:用来存储字符串的空间大小;

           参2:描述空间大小;

           参3:读取字符串的位置,键盘--》标准输入:stdin

           返回值:返回实际获取到的字符串的首地址。

puts:

输出字符串后会自动添加 \n;

   Int puts(const char *s);

     参1:待写出到屏幕的字符串。

     返回值:成功:非负数;失败:-1;

fputs:

将一个字符串写到屏幕stdout,输出字符串后不添加 \n;

   Int fputs(const char *str, FILE * stream );

     参1:待写出到屏幕的字符串。     屏幕--》标准输出:stdout

     参数:写出位置 stdout

     返回值:成功:非负数;失败:-1;

strlen:

返回字符串的有效长度,不包括\0; 碰到\0结束;

 size_t strlen(const char * s);

参1:待求长度的字符串

返回:有效的字符个数。

函数

函数的作用:

  1. 提高代码的复用性
  2. 提高程序模块化组织性。

函数分类:

       系统库函数:标准C 库libc

  1. 引入头文件---声明函数
  2. 根据函数原型调用。

       用户自定义

                除了需要提供函数原型,还要提供函数实现

随机数:

  1. 播种随机数种子:srand(time(NULL));
  2. 引入头文件#include<stdio.h> <time.h>
  3. 生成随机数: rand() % 100;

函数定义:

      包括 函数原型(返回值类型、函数名、形参列表)和 函数体(大括号一对,具体代码实现)一定包含类型名 形参名;   

Int add(int a,intb,int c)

{return a+b+c;}

函数调用:

包括 函数名(实参列表);

Int ret = add(10,20,30);

实参(实际参数):在调用时,传参必须严格按照形参填充。(参数的个数、类型、顺序) 没有类型描述符;

函数声明:

要求 在函数调用之前,编译必须见过函数定义,否则 需要函数声明;

隐式声明:默认编译器做隐式声明函数时,返回都为 int ,根据调用语句不全函数名和形参列表。

exit 函数:

          return:

               返回当前函数调用,将返回值返回给调用者。

          exit() 函数:

               退出当前程序;

多文件编程:

        多个含有不同函数功能 .c 文件模块,编译到一起,生成一个 .exe 文件

        

        防止头文件重复包含:头文件守卫

  1. #pragma once         --->windows中
  2. #ifndef __HEAD_H__    head.h

#define __HEAD_H__

指针

指针和内存单元:

指针: 地址;

内存单元:计算机中内存最小的存储单位。——内存单元。大小为一个字节,每个内存单元都有一个唯一的编号。

指针定义和使用:

int a = 10;

int *p = &a;

*p = 250;            //指针的解引用。间接引用

            //*p :将p变量的内容取出,当成地址看待,找到该地址对应的内存空间。

            //如果做左值,存数据到空间中,

            // 如果做右值,取出空间中的内容。

指针的大小与 类型 无关。只与当前使用的平台架构有关。32位:4字节; 64位:8字节;

野指针:

  1. .没有一个有效地址空间的指针。

        int *p;

    *p = 200;

  1. .p变量有一个值,但是该值不是访问的内存区域;

        int *p = 10;

   *p = 1000;

杜绝野指针

空指针:

     int *p = NULL;

     *p 时 p 所对应的存储空间一定是一个 无效的访问区域

万能指针/泛型指针(void *)

          可以接收任意一种变量地址。但是在使用必须借助“强转” 具体化数据类型

         char ch = R;

void *p;

p = &ch;

printf(%c\n,*(char *)p);

const 关键字:  

      修饰变量:

           const int a = 20;

           int *p = &a;

           *p = 650;

printf(%d\n,a);

      修饰指针:

           const int *p;

                  可以修改p;

                  不可以修改*p;

            int const  *p;

                  可以修改p;

                  不可以修改*p;

     

            int * const p;

                  可以修改*p;

                  不可以修改 p;

           const int *const p;

                  不可以修改p;

                  不可以修改*p;

总结:const向右修饰,被修饰部分即为只读。

常用:在函数形参内,用来限制指针所对应的内存空间为只读;

指针和数组:

数组名:【数组名是地址常量】不可以被修改(赋值)。

指针是变量,可以用数组名给指针赋值。

取数组元素:

int arr[] = {1,3,5,7,8};

int *p = arr;

arr[0]==*(arr+0) == p[0] == *(p+0);

指针和数组的区别:

  1. 指针是变量,数组名为常量。
  2. sizeof(指针) ==》4/8

             sizeof(数组)==》数组的实际字节数。

指针++操作数组:

int arr[] = {1,2,3,4,5,6,7,8,9,0};

int *p = arr;

for(size_t i=0;i<n;i++)

{

     printf(“%d”,*p);

      p++;//p=p+1;一次加过一个int大小,一个元素。

}

p的值会随着循环不断变化。打印结束后,p指向一块无效的地址空间(野指针)

指针加减运算:

数据类型对指针的作用:

  1. .间接引用:

     决定了从指针存储的地址开始,向后读取的字节数(与指针本身存储空间无关。)

  1. .加减运算:

                      决定了指针进行+1操作向后加过的 字节数。        

指针不能进行:*  / %  运算

   指针+-整数

  1. .普通指针变量+-整数:

                  char *p:  打印p、p+1  偏过 1 字节

                  short *p:  打印p、p+1  偏过 1 字节

                  int *p:  打印p、p+1  偏过 1 字节

  1. 在数组中+-整数

                 short arr[] = {1,3,5,8};

                 int *p = arr;

                 p+3;              //向右(后)偏过 3 个元素

                 P-2;              //向左(前)偏过 2 个元素     

  1. &数组名+1;

     加过一个 数组的大小 (数组元素个数*sizeof(数组元素类型))

              

指针 +  指针 :error!!!

指针 — 指针:

  1. .普通变量来说,语法运算。无实际意义。
  2. 数组来说,偏移过的元素个数

指针实现strlen函数:

char str[] = “hello”;

char *p = str;

 while(*p !=’\0’)

{

    p++;

}

p-str:即为 数组有效元素个数

指针比较运算:

  1. .普通变量来说,语法语法允许,无实际意义。
  2. .数组来说: 地址之间可以进行比较大小。

                        可以得到 元素存储的先后顺序

  1. . int *p;

              p = NULL;     //这两行代码等价于:int *p = NULL;

指针数组:

一个存储地址的数组。数组内部所有元素都是地址 。

本质:是一个二级指针

二维数组,也是一个二级指针。

多级指针:

int a = 0;                   

int *p = &a;                一级指针是变量的地址。

int  **pp = &p;             二级指针是一级指针的地址。

int ***ppp = &pp;           三级指针是二级指针的地址。

int  ****pppp = &ppp        四级指针是三级指针的地址。

多级指针不能跳跃定义;

对应关系:

ppp === &pp;    三级指针

*ppp = pp =&p;       二级指针

**ppp = *pp = p = &a;     一级指针

***ppp = **pp = *p = a        普通整型变量

指针和函数:

      栈帧:当函数调用时,系统会在stack 空间上申请一块内存区域,用来功函数调用,主要存放 形参 和 局部变量(定义在函数内部)。

             当函数调用结束,这块内存区域自动被释放(消失)。

传值和传址;

       传值:函数调用期间,实参将自己的值,拷贝一份给形参

       传址:函数调用期间,实参将地址值,拷贝一份给形参。

                (地址值--》 在swap函数栈帧内部,修改了main函数栈帧内部的局部变量值)

  指针做函数参数:

              int swap2(int *a,int *b);

               调用时,传有效的地址值。

  数组做函数参数:

void BubbleSort(int arr[10]) == void BubbleSort(int arr[]) == void BubbleSort(int *arr)

       传递的不再是整个数组,而是数组的首地址。(一个指针的大小)

      所以,当整型数组做函数参数时,我们通常在函数定义中,封装2个参数,一个表数组首地址,一个表元素个数

 指针做函数返回值:

int *test_func(int a,int b)

指针做函数返回值,不能返回【局部变量的地址值】。

数组做函数返回值:  C语言,不允许!!!只能写成指针形式。

指针和字符串:

1).   char str1[] = {h,i,\0};

char str2[] = hi;

char *str3[] = hi;

char *str4[] = {h,i,\0};//错误!!!

2). 当字符串(字符数组),做函数参数时,不需要提供2个参数,因为每个字符串都有’\0‘.

字符串去空格:

//字符串去空格实现 -数组

void str_no_space(char *src, char *dst)

{

int i = 0;//遍历字符串src

int j = 0;//记录dst存储位置

while (src[i] != 0)

{

if (src[i] != ' ')

{

dst[j] = src[i];

j++;

}

i++;

}

dst[j] = '\0';

}

//字符串去空格实现 -指针

void str_no_space2(char *src, char *dst)

{


    while (*src)

    {

        if (*src != ' ')

        {

            *dst = *src;

            dst++;

        }

        src++;

    }

    *dst = '\0';

}

带参数的main函数:

       无参main函数: int main(void) == int main()

       带参数的main函数:int main(int arge,char *argv[]) == int main(int argc,char **argv)

          参1:表示给main 函数传递的参数总个数;

          参2:是一个数组:数组的每一个元素都是字符串 char *;

 测试1:命令行终端中,使用gcc编译器生成 可执行文件,如:test.exe

 test.exe a b c d

argc---5

tect.c -->argv[0];

a-->argv[1];

b-->argv[2];

c-->argv[3];

d-->argv[4];

 测试2:在vs中,项目名称上——》右键——》属性——》调试——》命令行参数——》将 test.exe a b c d 写入

argc---5

tect.c -->argv[0];

a-->argv[1];

b-->argv[2];

c-->argv[3];

d-->argv[4];

       

str中 substr出现次数:

      strstr函数:在str中,找substr出现的位置。

   char *strstr(char *str,char *substr)    ---#include <string.h>

   参1:原串

   参2:子串

   返回值:返回子串在原串中的位置。(地址值)

           如果没有:返回 NULL;

字符串处理函数#include<string.h>

字符串拷贝:

strcpy:

将str的内容,拷贝给dest,返回dest,注意要保证dest空间足够大,防止内存溢出;char *strcpy(char *dest,const char *src);

函数调用结束,返回值和dest 结果一致;

strncpy:

将 src的内容拷贝给 dest,只拷贝n个字节,通常n与dest 对应的空间一致。

默认 不添加\0

char *srencpy(char *dest,const char *src, size_t n);

特性:n>src:只拷贝src 的大小;

      n<src: 只拷贝 n字节的大小。不添加‘\0’;

 字符串拼接:

strcat: 将src的内容,拼接到dest后,返回拼接的字符串。需确保dest空间足够大

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

    

strncat: 将src的前n个字符,拼接到dest后形成一个新的字符串。需确保dest空间足够大

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

字符串比较:

strcmp:比较s1和s2两个字符串,如果相等,返回0,如果不相等,进一步s1和s2对应位ASCII码值   int strcmp(const char *s1,const char *s2,size_t n)

s1>s2 返回  1

s1<s2 返回  -1

strcmp:比较s1和s2两个字符串的前n个字符

如果相等,返回0,如果不相等,进一步s1和s2对应位ASCII值(不比较字符串ASCII码的和)

函数原型: int strcmp(const char *s1,const char *s2,size_t n)

s1>s2 返回  1

s1<s2 返回  -1

字符串格式输入、输出:

sprintf():

函数原型: int sprintf(char *str, const char *format,...)

                    对应printf,将原来写到屏幕上的“格式化字符串”,写到 参数1 str中

sscanf():

    函数原型:int sscanf(const char *str,const char *format,...)

  对应scanf:将原来从屏幕获取的“格式化字符串”,从参数1 str中获取

 scanf("%d+%d=%d",&a,&b,&c);

——》

char str[]="10+24=34":

sscanf(str,“%d+%d=%d”,&a,&b,&c);a->10;b->24; c->34;

字符串查找字符、子串

strchr():

在字符串str中 找一个字符出现的位置,返回字符在字符串中的地址

 函数原型:char *strchr(const char *s,int c);

 printf(%s\n strchr(hehehheahahahohoh,a));

-->返回的结果为:“ahahahohoh”

strrchr():

自右向左,在字符串str中 找一个字符出现的位置,返回字符在字符串中的地址

 函数原型:char *strrchar(const char *s,int c);

 printf(%s\n strrchr(hehehheahahahohoh,a));

-->返回的结果为:“ahohoh”

strstr():

在字符串str中,找子串substr第一次出现的位置,返回地址

函数原型:char *strstr(const char *str,const char *substr);

在字符串中找子串的位置。

printf(%s\n strrchr(hehehheahahahohoh,ho));--》“hohoh”

printf(%s\n strrchr(hehehheahahahohoh,xixi));--》NULL;

字符串分割:

  strtok();按照既定的分隔符,来拆分字符串www.baidu.com

char *strtok(char *str const char *delin);

             参1:带分割字符串; 参2:分割符组成的分割串

             返回 字符串拆分后的首地址。”拆分“ :将 分割字符用‘\0’

strtok(www.baidu.com,.);

 第一次拆分,参一传待拆分的原串,第+ 次拆分时,参1传NULL

练习www.itcast.co$This is a strtok$test

char str[] = "www.itcast.co$This is a strtok$test";

char *p = strtok(str, " .$");
while (p != NULL)

{

    p = strtok(NULL, " .$");

    printf("p = %s\n", p);

}

atoi/atof/atol:使用这类函数进行转换,要求,原串必须是可转换的字符串。

atoi:字符串转整数

atof字符串转浮点数

atol字符串转:长整型数

内存管理:

局部变量:

定义在函数内部的变量。

作用域:从定义位置开始,到包裹该变量的第一个大括号结束

全局变量:

  概念:定义到函数 外 部的变量;

作用域:从定义位置开始,默认到本文件内部,其他文件如果想要使用,可以通过声明方式将作用域导出

静态全局变量:

static 全局变量:

定义语法:在变量定义之前添加static 关键字

作用域:被限制在本文件内部,不允许通过声明导出到其他文件。

静态局部变量:

static 局部变量

定义语法:在局部变量定义之前添加static 关键字

特性:静态局部变量只定义一次,在全局位置,通常用来做计数器。

作用域:从定义位置开始,到包裹该变量的第一个大括号结束。

全局函数:

函数

定义语法:函数原型+函数体;

static函数:

定义语法:static+函数原型+函数体

static 函数 只能在本文件内部使用其他文件即使声明也无效。

生命周期:

局部变量: 从变量定义开始,函数调用完成结束;---函数内部。

全局变量: 程序启动开始,程序终止结束---程序执行期间。

static局部变量: 程序启动开始,程序终止结束---程序执行期间。

static全局变量: 程序启动开始,程序终止结束---程序执行期间。

全局函数: 程序启动开始,程序终止结束---程序执行期间。

static函数: 程序启动开始,程序终止结束---程序执行期间。

Windows:内存四区模型:

代码段: .test段(代码段);(二进制形式存储)

数据段: .data段:存放初始化为非0的全局变量和静态变量

.bss段:初始化为0,未初始化的全局变量和静态变量。程序加载执行前,宏将该段整体赋值为0。

.rodata:只读数据段。存放常量

(栈)stack:比较小,在其之上开辟栈帧Windows:1M-- 最大10M;Linux :8M--最大16M

  系统自动管理,自动分配,自动释放

  存储特性是;FILO

(堆)heap:空间足够大,给用户自定义数据提供空间,约1.3G+;

开辟释放heap空间:

void malloc(size_t size)申请size大小的空间

 返回实际申请到的内存空间首地址【我们通常拿来当数组】

void free(void *ptr)释放申请的空间

参数:malloc返回的地址值。

使用heap空间:空间连续时,当成数组使用

               free后的空间,不会立即失效,通常将free后的地址置为NULL;

  free 地址必须是malloc申请地址,否则出错

  如果malloc之后的地址一定会变化,那么使用临时变量temp保存

二维指针对应的heap空间:

申请外层指针:

 char **p = malloc(sizeof(char)*5);

申请内存指针:

 for(i = 0;i<5;i++)

{

     p[i] = (char *)malloc(sizeof(char)*10);

}

使用:不能修改p的值                

 for(i = 0;i<5;i++)

{

    strcpy(p[i],”helloheap”);

}

//释放内层:

for(i = 0;i<5;i++)

{

    free(p[i]);

}

//释放外层:

free(p);

结构体:

结构体成员赋值:

struct Student s1;

struct Student s2 = {1,”张三”,99.9};



//方法1:逐个成员赋值

s1.id = s2.id;

strcpy(s1.name , s2.name);

s1.score = s2.score;



//方法2:整体赋值(相同数据类型结构体变量之间)推荐

//s1 = s2;



//方式3:memcpy

memcpy(&s1,&s2,sizeof(struct Student));



//结构体变量交换:

struct Student s1 = {1,”tom”,60};

struct Student s2 = {2,”jerry”,90};



//三杯水法:
int idTemp = s1.id;

s1.id = s2.id;

s2.id = idTemp;



char nameTemp[64];

strcpy(nameTemp,s1.name);

strcpy(s1.name,s2.name);

strcpy(s2,name,nameTemp);



int scoreTemp=s1.score;

s1,score = s2.score;

s2.score = scoreTemp;



//直接交换

struct Student temp;

temp = s1;

s1 = s2;

s2 = temp;



//结构体数组:
struct Student arr[5]={

{1,”张三”,100},

{1,”李四”,59},

{1,”王五”,97},

{1,”赵六”,98},

{1,”陈冠希”,99}

//最后一个成员的最后 可写可不写’,’,

}

遍历数组:

int len = sizeof(arr)/sizeof(struct Student);

for(int i=0;i<len;i++)

{

    printf(“学号:%d 姓名:%s 得分:%lf\n”,arr[i].id,arr[i].name,arr[i].score);

}

结构体嵌套结构体:

struct Stu

{

    int id;

    int age;

};

struct Tea

{

    int id;

    struct Stu st;

};

struct Tea t1 = { 100,{1,18}};

struct Tea t2 = {101,2.19};//两种调用方式均可用

//通过指针获得键盘输入

void test02()

{

    struct Student s;

    struct Student *p = &s;

    scanf("%d %s %lf", &p->id, p->name, &p->score);

    printf("id = %d name = %s,score = %.1lf\n", p->id, p->name, p->score);

}

//结构体创建在堆区

void test03()

{

struct Student * p = malloc(sizeof(struct Student));

if (p == NULL)

{

    return;

}

scanf("%d %s %lf", &p->id, p->name, &p->score);

printf("id = %d name = %s,score = %.1lf\n", p->id, p->name, p->score);



//堆区释放空间

if (p != NULL)

{

    free(p);

    p = NULL;

}

}

//结构体数组创建在堆区

void test04()

{

    int n;

    printf("请输入数组元素个数:");

    scanf("%d\n",&n);

    struct Student *arr = malloc(sizeof(struct Student)*n);

    //清空空间

    memset(arr,0, sizeof(struct Student)*n);

    for (int i = 0; i < n; i++)

    {

        printf("请给第%d个学生赋值:\n",i+1 );

        scanf("%d %s %lf", &arr->id, arr->name, &arr->score);

    }

    for (int i = 0; i < n; i++)

    {

        printf("id=%d name=%s score=%.1lf\n", arr[i].id, arr[i].name, arr[i].score);

    }

    //释放数组

    if (arr != NULL)

    {
    
        free(arr);

        arr = NULL;

    }

}

//结构体做函数参数

void printStudent(struct Student s)//值传递

{

    printf("id=%d name=%s,score=%.1lf\n", s.id, s.name, s.score);

}

void printStudent2(struct Student *s)

{

    printf("id=%d name=%s score=%.1lf\n", s->id, s->name, s->score);

}

void test05()

{

struct Student s1 = { 1,"tom",80 };

printStudent(s1);

printStudent2(&s1);

}

//封装一个函数,给数组赋值

void setStudentArray(struct Student *arr, int len)

{

    for (int i = 0; i < len; i++)

    {

        printf("请给第%d个同学赋值\n", i + 1);

        scanf("%d %s %lf", &arr[i].id, arr[i].name, &arr[i].score);

    }

}

void test06()

{

    struct Student arr[2];

    memset(arr, 0, sizeof(arr));
    
    int len = sizeof(arr) / sizeof(arr[0]);

    setStudentArray(arr, len);

    for (size_t i = 0; i < len; i++)

    {

        printf("id=%d name=%s score=%lf", arr[i].id, arr[i].name, arr[i].score);

    }

}

const 修饰结构体:

const struct Student *p1 = &s1;//常量指针 指针指向的值不可以改,指向可以改

//p1->id = 200;//error指针指向的值不可以改,

p1 = &s2;//success


struct Student * const p2 = &s2;//指针常量,指针指向不可以改,指向的值可以改

p2->id = 200;//success

//p2=&s1;指针的指向不可以改


const struct Student *const p3 = &s1;//指针的指向和指针指向的值都不能修改

结构体嵌套一级指针:需要进行多次翻译堆区空间

//创建

struct Teacher ** teaArray = malloc(sizeof(struct Teacher*) * 3);

for (size_t i = 0; i < 3; i++)

{

    teaArray[i] = malloc(sizeof(struct Teacher));



    //老师的姓名 在堆区创建

    teaArray[i]->name = malloc(sizeof(char) * 64);

    sprintf(teaArray[i]->name, "Teacher_%d", i + 1);
    


    teaArray[i]->age - 30 + i;

}

for (size_t i = 0; i < 3; i++)

{

    printf("姓名:%s 年龄:%d\n", teaArray[i]->name, teaArray[i]->age);

}

//释放

for (size_t i = 0; i < 3; i++)

{

    if (teaArray[i]->name != NULL)

    {

        printf("%s被释放了\n", teaArray[i]->name);

        free(teaArray[i]->name);

        teaArray[i]->name = NULL;

    }

    if (teaArray[i] != NULL)
    
    {

        free(teaArray[i]);

        teaArray[i] = NULL;

    }

}

    //释放数组

if (teaArray != NULL)

{

    free(teaArray);

    teaArray = NULL;

}

}

共用体(联合体):

联合体中的成员,公用同一块内存空间  按照最大的数据类型分配内存空间

修改其中一个成员变量的值,其他成员变量会随之修改

枚举: 

neum color {枚举常量};//能穷举得尽的

枚举常量的值:是整型常量,不能是浮点数,可以是负值,默认初始值从0开始,后续常量较前一个常量+1,可以给任意一个常量赋任意初值,后续常量较前一个常量+1;

读写文件与printf 、scanf关联

printf--屏幕--标准输出

scanf--键盘--标准输入

perror--屏幕--标准错误

系统文件:

标准输入--stdin--0

标准输出--stdout--1

标准错误--stderr--2

应用程序启动时,自动被打开,程序执行结束时,自动被关闭;

文件指针和普通指针区别:

FILE *p = NULL;

借助文件操作函数来改变fp为空、野指针的状况。fopen();-->相当于fp=malloc();

操作文件,使用文件读写函数来完成。fputc、fgetc、fputs、fgets、fread、fwrite;

文件分类:

设备文件:

屏幕、键盘、磁盘、网卡、声卡、显卡、扬声器、

磁盘文件:

文本文件

二进制文件

文件操作的一般步骤:

  1. 打开文件 fopen();
  2. 读写文件 fputc、fgetc、fputs、fgets、fread、fwrite
  3. 关闭文件 fclose()

打开 关闭文件函数:

FILE *fopen(const char * filename,const char * mode);

参数1:待打开文件的文件名(访问路径)

参数2:文件打开权限:

“r” 只读方式打开文件,文件不存在,报错。存在,以只读方式打开。

“w”只写方式打开文件 文件不存在,创建一个空文件文件如果存在,清空并打开。

“w+”:读、写方式打开文件,文件不存在,创建一个空文件,文件如果存在,清空并打开。

“r+”读、写方式打开文件,文件不存在,报错。存在,以读 写方式打开。

“a”以追加的方式打开文件。

“b”:操作的文件是一个二进制文件(Windows)

返回值:成功:返回打开文件的文件指针

失败:NULL

int fclose(FILE * stream);

参数1:打开文件的fp(fopen的返回值)

返回值:成功:0 失败:-1;

文件访问路径:

绝对路径:

从系统磁盘的根盘符开始,找到带访问的文件路径

C:\Users\24720\Desktop\新建 DOC 文档.doc

C:/Users/24720/Desktop/新建 DOC 文档.doc --- 也使用于linux

相对路径:

  1. .如果在VS 环境下,编译执行文件相对路径是指相对于工程文件所在目录位置
  2. .如果双击xx.exe文件执行,文件的相对路径是相对于xxx.exe所在目录位置

按字符写文件:fputc:

   int fputc(int ch,FILE *stream);

参1:待写入的字符

参2:打开文件fp(fopen 的返回值)

返回值:成功:写入文件中的字符对应的ASCII码

      失败:-1

按字符读文件 fgetc

int fgetc(FILE * stream)

参数1:带读取的文件fp(fopen的返回值)

返回值:成功:写入文件中的字符对应的ASCII码

      失败:-1

文本文件的结束标记:EOF--》-1

feof()函数:

int feof(FILE * stream);

参数1:fopen 的返回值

返回值:到达文件结尾——》非0【真】

没有到达文件结尾--》0【假】

作用::用来判断到达文件结尾,既可以判断文本文件,也可以判断二进制文件

特性:要想使用feof()检测文件结束标记,必须在该函数调用之前,使用读文件函数。

feof()调用之前,必须有读文件函数调用;

fgets()函数:

获取一个字符串。以\n作为结束标记,自动添加\0,空间足够大 读\n,空间不足舍弃\n,必须有\0;

char *fgets(cahr *str,int  size,FILE * stream);

char buf[10]; //  hello  -->  hello\n\0

返回值:成功:读到的字符串;  失败:NULL;

fputs()函数:

写出一个字符串,如果字符串中没有\n不会写出\n;

int fputs(const char *str,FILE *stream)

返回值: 成功  0

 失败  -1

fgets() 和fputs() 练习:

FILE *fp = fopen("test07.txt" ,"w");

if (fp == NULL)

{

    perror("fopen error");

    return -1;

}

char buf[4096] = { 0 };

while (1)

{

    fgets(buf, 4096, stdin);

    if (strcmp(buf, ":wq\n") == 0)

    {

        break;

    }

    fputs(buf, fp);

}

fclose(fp);

练习 文件版四则运算:

  1. 封装write_file函数,将四则运算表达式写入;

FILE *fp = fopen(w);

futs(10/2=\n,fp);

futs(10+2=\n,fp);

.....

  1. 封装read_file函数,将四则运算表达式读出,拆分、运算、写回。
  1. .读出:

FILE *fp=fopen(r);

int buf[4096];

while(1)

{

fgets(buf,sizeof(buf),fp);  //buf 中存储的 四则运算表达式

}

  1. 拆分:

sscanf(buf,%d%c%d=\n,&a,&ch,&b);//得到运算数,运算符

  1. 根据运算符,得到运算结果

switch(ch)

{

case /:

a+b;

....

}

  1. 拼接 结果到 运算式 上

char result[4096];

sprintf(result,%d%c%d=%d\na,ch,b,a+b);//result 中包含带有结果的运算式。

  1. 将 多个带有结果的运算 拼接成为一个字符串

char sum_rea[4096];  //存总的字符串

strcat(sum_rea,result),//在while中循环拼接

  1. 重新打开文件,清空原有的四则运算表达式

   fclose(fp);

fp = fopen(w);

  1. 将 拼接成一个字符串,写入到空文件中

  fputs(sum_res);

printf --- sprintf --- fprintf : 均为变参函数:参数形参中有“...,最后一个固参通常是格式描述串(包含格式匹配符),函数的参数个数、类型、顺序由这个固参决定。

printf(hello);

printf(%s,“hello”);

printf(ret = %d\n,ret);

char buf[4096] = {0};

sprintf(buf,“%d = %d%c%d\n”,10+5,10,+,5);--》buf中

FILE *p = fopen()

fprintf(fp,%d = %d%c%d\n,10+5,10,+,5);-->fp对应的文件中

scanf --- sscanf ---fscanf

scanf(%d,&m);

char[] = 98;

sscanf(str,%d,&m);//str-->m

FILE *fp = fopen(r);

fscanf(fp,%d,&m);//fp指向文件中——>m

fprintf()函数:

int fprintf(FILE *stream,const char *format,...)

fscanf()函数:

int fscanf(FILE *stream,const char *format,...)

  1. .边界溢出:储存读取的数据空间,在使用之前清空。
  2. .fscamf()函数,每次在调用时都会判断下一次调用调用是否匹配参数2,如果不匹配提前结束文件读操作(feof(fp)为真)。

练习:文件版排序:生成随机数,写入文件,将文件内乱序随机数读出,排好序再写回文件

void write_file()//在文件中添加10个随机数

{

    FILE *fp = fopen("test11.txt", "w");

    if (fp == NULL)

    {

        perror("fopen error");

        return -1;

    }

    srand(time(NULL));

    for (size_t i = 0; i < 10; i++)

    {

        fprintf(fp, "%d\n", rand() % 100);//在文件中添加10个随机数


    }

    fclose(fp);

}

//冒泡排序

void BubbleSort(int * arr, int len)

{

    for (size_t i = 0; i < len-1; i++)
    
    {

        for (size_t j = 0; j < len-1-i; j++)

        {

            if (arr[j] > arr[j + 1])

            {

                int temp = arr[j];

                arr[j] = arr[j + 1];

                arr[j + 1] = temp;

            }

        }

    }

}

//将文件中的随机数取出,排序

void read_file()

{

    int arr[10],i=0;

    FILE *fp = fopen("test11.txt", "r");

    if (fp == NULL)

    {

        perror("fopen error");

        return -1;

    }

    while (1)
    
    {

        fscanf(fp,"%d\n", &arr[i]);

        i++;

        if (feof(fp))//到最回一个跳出

        {

            break;

        }

    }

     for (size_t i = 0; i < 10; i++)

    {

        printf("%d\n", arr[i]);

    }

    BubbleSort(arr,sizeof(arr)/sizeof(arr[0]));



    //fclose(fp);



    //写回文件中,覆盖之前数据

    fp = fopen("test11.txt", "w");//清空未排序文件

    if (!fp)

    {

        perror("fopen error");

        return -1;

    }



    //重新写入

    for (size_t i = 0; i < 10; i++)

    {

    fprintf(fp, "%d\n", arr[i]);//排好序的数组到文件

    }



    fclose(fp);

}

fgetc--fputc

fgets--fputs

fprintf--fscanf   默认处理文本文件

fwrite()函数:

---用来处理二进制文件(按二进制形式存储)

size_t fwrite(const void *ptr size_t nmemb, FILE *stream)

参数1:待写出数据的地址

参数2:待写出数据的大小

参数3:写出的个数 --参数2*参数3 = 写出数据的总大小

参数4:文件

返回值:成功 永远是  参数3 的值

      失败:0

fread():

size_t fread(const void *ptr size_t nmemb, FILE *stream)

参数1:待读出数据的地址

参数2:待读出数据的大小

参数3:读出的个数 --参数2*参数3 = 读出数据的总大小

参数4:文件

返回值:成功   参数3 ---通常把参数2传1,将参3传欲读出的字节数

      失败:0

0--读失败--feof(fp)--到文件结尾;

练习:大文件拷贝:

已知一个任意类型的文件对该文件复制,产生一个相同的新的文件:

  1. 打开两个文件,一个“r”.另一个“w”
  2. 从r中fread,fwrite 到w文件中
  3. 判断到达文件结尾 终止
  4. 关闭

注意:在windows下,打开二进制文件(MP3、MP4、avi、jpg...)时需要使用“b”如“rb”“wb”;

随机位置读:

fseek():

文件读写指针,在一个文件内只有一个。

int fseek(FILE *stream,long offset,int whence);

参1:待读写的文件。

参2:偏移量(矢量:+ 向后; - 向前)

参3:  SEEK_SET:WE:文件开头位置

     SEEK_CUR:当前位置

   SEEK_END:文件结尾位置

返回值: 成功:0,失败:-1;

ftell():

获取文件读写指针位置

long ftell(FILE *stream);

返回ftell + fseek(SEEK_END);来获取文件大小

rewind:

        回卷文件读写指针,将读写指针移动到起始位置。

        void rewind(FILE *stream)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值