C语言学习之路(黑马)

文章目录

  • 环境搭建
  • HelloWorld
    • 代码编写
    • 代码分析
    • 执行流程
  • 核心语法
    • 注释
      • 单行注释
      • 多行注释
      • 注释示例
    • 关键字
    • 常量
    • 变量
    • 计算机进制
    • 数据类型
    • 标识符
    • 键盘录入
  • 运算符
    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 自增减运算符
    • 逻辑运算符
    • 三元运算符
    • 逗号运算符
    • 运算符的优先级
  • 流程控制语句
    • 顺序结构
    • 分支结构
      • if语句
      • switch语句
    • 循环结构
      • for循环
      • while语句
      • do...while语句
    • 循环高级
    • goto
  • 函数
    • 函数的定义格式
    • 函数的定义调用
    • 函数的注意事项
    • C语言中常见的函数
    • 随机数
  • 数组
    • 数组的定义
    • 数组的初始化
    • 元素访问
    • 数组遍历
    • 内存中的数组
    • 数组的常见问题
    • 数组查找算法
      • 基本查找/顺序查找
      • 二分查找/折半查找
      • 插值查找
      • 分块查找
      • 哈希查找
      • 树表查找
      • 斐波那契查找
    • 数组排序算法
      • 冒泡排序
      • 选择排序
  • 指针
    • 指针定义格式
    • 指针的作用
      • 操作其他函数中的变量
      • 函数返回多个值
      • 函数的结果和计算状态分开
      • 方便的操作数组和函数
    • 指针的使用细节
    • 指针高级
      • 指针计算
      • 指向不明的指针
      • 没有类型的指针
      • 二级指针和多级指针
      • 数组和指针
      • 函数和指针
  • 字符串
    • 定义字符串
    • 遍历字符串
    • 遍历字符串数组
    • 字符串常用函数
  • 结构体
    • 结构体格式
    • 定义结构体
    • 结构体数组
    • 结构体别名
    • 结构体作为函数参数
    • 结构体嵌套
    • 内存对齐
  • 共用体
    • 共用体的使用
    • 共用体的特点
    • 结构体和共用体的区别
  • 动态内存分配
    • 常用函数
    • 动态内存分配的细节
  • C语言的内存结构
    • 变量数组
    • 全局变量和static变量
    • 字符串
    • malloc函数
  • 文件
    • 路径
    • 转义字符
    • 读写模式
    • 读取文件
    • 写出数据
    • 拷贝文件

环境搭建

HelloWorld

代码编写

Hello_World.c

#include<stdio.h>int main() 
{printf("Hello World!");return 0;
}

代码分析

  1. ‘#include’ : 引⼊头⽂件专⽤关键字。提前找到需要用到的东西。

  2. <> : ⽤来包裹 库头⽂件名

  3. stdio.h : 使⽤的头⽂件。因为程序中使⽤了 printf() 函数。就必须使⽤该头⽂件。

std:标准:standard
i: input 输⼊。
o: output 输出。

  1. int :main 函数返回值为整型。

  2. main: 整个程序的⼊⼝函数。 任何.c 程序,有且只有⼀个 main 函数。

  3. printf(); C语⾔向屏幕输出字符使⽤的函数。

  4. printf(“helloworld\n”)

    printf();向屏幕输出一段内容

    helloworld: 待写出的字符串内容。

    \n: 回⻋换⾏。

  5. return 0;

    return 返回。 C程序要求,main 函数要有返回值。借助 return 实现返回。

    0:成功!因为 int ,返回整数。

注意事项

  • 程序中使⽤的所有的字符,全部是 “英⽂半⻆” 字符。
  • 程序中,严格区分⼤⼩写。
  • “;” 代表⼀⾏结束。不能使⽤ 中⽂ “;”,必须是英⽂。

执行流程

完成的C语言运行,分为以下4步,在VS中我们直接运行,其实是把中间的步骤给省略了

  • 预处理(这一步后面单独讲解)

    简单理解,就是先找到#include后面的 <stdio.h>这个文件

  • 编译

    把c文件编译成二进制文件后缀名为obj

  • 连接/链接

    把预处理找到的h文件,还有编译之后产生的obj文件打包在一起,产生exe文件

  • 运行

    运行exe文件

在这里插入图片描述

核心语法

注释

单行注释

// 这是单行注释文字

多行注释

/*
这是多行注释文字
这是多行注释文字
这是多行注释文字
*/
注意:多行注释不能嵌套使用。

注释示例

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>// 主入口
int main() 
{// 输出语句printf("Hello World!\n");/*return:程序结束了0 程序是正常结束这里的0,需要跟上面的int呼应起来*/return 0;
}

关键字

关键字:在C语言中被赋予了特定含义的英文单词,一共有32个关键字

auto  break  case  char  const  continue  default  do  double else  enum 
extern float  for  goto  if  int  long  register   return  short  signed
sizeof  static struct  switch  typedef union  unsigned  void  volatile  while
  • 关键字全部小写
  • 在特定的编译器中,关键字是高亮显示的
    vs:蓝色或者紫色
    vs:蓝色

常量

程序运行的过程中,其值永远不会发生改变的数据
在这里插入图片描述

实型常量的小细节:

  • 小数点前后,如果只有0,可以省略不写

    0.93 可以写成 .93
    18.0 可以写成 18.
    
  • 科学计数法是实型常量,但是要写E

    12340000 可以写成 1.2340000E7
    但是写成 1.234 * 107次方就错误了,因为这是一个计算过程,不是最终的数字
    

输出常量

利用printf集合占位符的方式可以输出常量

格式:

​ printf(参数1,参数2);

​ 参数1:输出内容的最终样式,以字符串的形式体现 (必填)

​ 参数2:填补的内容 (选填)

占位符:

占位符说明
整形%d
实型%f
字符%c
字符串%s
  • 输出一个整数

    #include <stdio.h>
    int main()
    {printf("%d", 10);return 0;
    }
    
  • 输出一个小数

    #include <stdio.h>
    int main()
    {printf("%f", 1.93);return 0;
    }
    
  • 输出一个字符

    #include <stdio.h>
    int main()
    {printf("%c", 'A');return 0;
    }
    
  • 输出一个字符串

    #include <stdio.h>
    int main()
    {// 第一种方式:printf("Hello World!");// 第二种方式:printf("我的名字为:%s","尼古拉斯·纯情·暖男·天真·阿玮");return 0;
    }
    

输出并换行

操作系统换行符
windows\r\n
mac\r
Linux\n

平时写代码的时候,想要换行直接写\n即可,C语言会根据不同的操作系统解析成对应的换行符

变量

  • 变量的三要素

    1. 变量名:用来在程序中使用。
    2. 变量类型:开辟内存空间大小。
    3. 变量值:存储的实际数据

在这里插入图片描述

变量定义:

类型名 变量名 = 变量值(一般都这么写)

举例:

int m = 57;

会开辟内存空间给变量。变量声明不会开辟内存空间。

代码演示:

#include <stdio.h>int main()
{// 1. 定义格式:// 数据类型 变量名;int a;// 2. 赋值/修改值// 变量名 = 数据值;a = 10;// 3. 如果定义的时候已经知道了变量中要存储什么样的数据// 数据类型 变量名 = 数据值;int b = 20;return 0;
}

变量的注意事项

  • 只能存一个值
  • 变量名不允许重复定义
  • 一条语句可以定义多个变量
  • 变量在使用之前一定要进行赋值
  • 变量的作用域范围

计算机进制

  • 代码书写

  • 二进制:由0和1组成,代码中以0b开头

  • 十进制:由0~9组成,前面不加任何前缀

  • 八进制:由0~7组成,代码中以0开头

  • 十六进制:由0~9还有 a ~ f 组成,代码中以0x开头

  • 任意进制转十进制

    公式: 系数*基数的权次幂 相加
    

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  • 二进制转十进制
    在这里插入图片描述

  • 十进制转其他进制

    在这里插入图片描述

  • 总结

    在这里插入图片描述

数据类型

数据类型可以决定变量中能存储什么类型的数据, 决定存储空间的大小

  • sizeof 运算符

    语法 1:sizeof(变量名)

    int a = 10;
    printf("%zu\n", sizeof(a));//sizeof(a) 获取 a 变量占用内存大小(字节)。可以用 printf 显示出来
    

    语法 2:sizeof(类型名)

    printf("%zu\n", sizeof(double)); // 也可以使用 sizeof 直接查看某种类型占用的内存大小
    
  • 整型

    数据类型字节数格式符数据范围最小值宏最大值宏
    short(短整型)2%hd-215 ~ 215-1 (-32768 ~ 32767)SHRT_MINSHRT_MAX
    int(整型)4%d-231 ~ 231-1 (-2147483648 ~ 2147483647)INT_MININT_MAX
    long(长整型)4%ld-231 ~ 231-1 (-2147483648 ~ 2147483647)LONG_MINLONG_MAX
    long long(长长整型)8%lld-263 ~ 263-1LLONG_MINLLONG_MAX
    unsigned short(无符号 短整型)同 short%hu0 ~ 216-1 (0 ~ 65535)0USHRT_MAX
    unsigned int(无符号 整型)同 int%u0 ~ 232-1 (0 ~ 4294967295)0UINT_MAX
    unsigned long(无符号 长整型)同 long%lu0 ~ 232-1 (0 ~ 4294967295)0ULONG_MAX
    unsigned long long(无符号 长长整型)同 long long%llu0 ~ 264-10ULLONG_MAX

    上表中列出的占用字节数和取值范围,是大多数情况下各种类型的取值。

    由于,C 标准没有具体规定以上各类数据所占用的字节数。因此,在不同系统、编译器下,数据类型占用的字节数会有所不同。

    比如:int 类型,在 Turbo C 环境占 2 字节,取值范围与 short 相同。 而在 Visual C 环境下是 4 字节。

    再如:long 类型,相同的 gcc 编译器下,在 Windows 系统中占 4 字节,而在 Linux 系统中占 8 字节。

    可以使用 sizeof 查看 数据类型 占用内存的大小。

    可以引入头文件 #include <limits.h> 借助宏来查看 数据类型 在当前平台上 对应的最小、最大值。

    // 预处理,程序运行之前,需要提前做的事情
    #include<stdio.h>// 主入口
    int main() 
    {short a = 10;printf("%hd\n", a);printf("%zu\n", sizeof(a));int b = 10;printf("%d\n", b);printf("%zu\n", sizeof(b));long c = 10L;printf("%ld\n", c);printf("%zu\n", sizeof(c));long long d = 10LL;printf("%lld\n", d);printf("%zu\n", sizeof(d));return 0;
    }
    
  • 实型

    实型表示有符号的十进制小数,在计算机内部以浮点方式表示(小数点是浮动的),因此也叫浮点型。

    常见实型有两种: float (单精度)、 double (双精度)

    实型数据没有八、十六进制,也没有 unsigned 无符号形式。在计算机底层采用的是近似计算,实现比较复杂,且不同平台处理方式不同。我们这里只学习它基本的知识。

    小数的取值范围比整数的要大

    C语言中的小数默认double类型的

    小数不可以和unsigned组合,unsigend只能和整数类型组合。

    // 预处理,程序运行之前,需要提前做的事情
    #include<stdio.h>// 主入口
    int main() 
    {//float:单精度小数 (精确度小数点后6位)windows占4个字节 (38位)float a = 3.14F;printf("%f\n", a);//3.140000printf("%.2f\n", a);//3.14printf("%zu\n", sizeof(a));//4//double:双精度小数 (精确度小数点后15位)windows占8个字节 (308位)double b = 1.23456789;printf("%lf\n", b); //1.234568printf("%.2lf\n", b); //1.23printf("%.8lf\n", b); //1.23456789printf("%zu\n", sizeof(b));//8//long double 高精度小数 (精确到小数点后18~19位)windows占8个字节(其他12,16)long double c = 3.1415926L;printf("%lf\n",c);//3.141593printf("%.2lf\n",c);//3.14printf("%.7lf\n",c);//3.1415926printf("%zu\n", sizeof(c));//8return 0;
    }
    
  • 字符

    char 取值范围:ASCII码表中的字母,数字,英文符号

    // 预处理,程序运行之前,需要提前做的事情
    #include<stdio.h>// 主入口
    int main() 
    {//char 取值范围:ASCII码表中的字母,数字,英文符号char c1 = 'a';printf("%c\n", c1);//如果用 %d,就显示 其 ASCII 值了。printf("%d\n", c1);//97printf("%zu\n", sizeof(c1));//1char c2 = '1';printf("%c\n", c2);printf("%zu\n", sizeof(c2));//1char c3 = 'A';printf("%c\n", c3);char c4 = '.';printf("%c\n", c4);return 0;
    }
    
  • 字符串

    // 预处理,程序运行之前,需要提前做的事情
    #include<stdio.h>// 主入口
    int main() 
    {printf("请输入一段文字:");/*字符串变量的定义方式:数据类型 变量名[大小] = "字符串";char str[内存占用大小] = "abc";内存占用大小的计算方式:英文:1个字母、符号、数字占用一个字节中文:默认情况下一个中文占两个字节结束标记:1个字节*/char str[4];scanf("%s", &str);printf("%s", str);return 0;
    }
    
  • 隐式转换
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 强制转换
    在这里插入图片描述
    在这里插入图片描述

标识符

标识符:代码中所有我们自己起的名字。比如变量名、函数名等。

标识符的硬性要求:
以数字、字母、下划线组成
不能以数字开头
不能是关键字
区分大小写

标识符的软性建议:
​ 用英文单词,见名知意
​ 变量名:全部小写
​ 文件名:全部小写,单词之间用下划线隔开

键盘录入

scanf ,是scanner format的缩写,是c语言提供的一个函数。可以结合 格式符 读取各种类型数据。

在这里插入图片描述

//在 CRT 中关闭这些函数的弃用警告
#define _CRT_SECURE_NO_WARNINGS
// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>// 主入口
int main() 
{int a;scanf("%d", &a);printf("%d\n", a);return 0;
}
	printf("请输入三个整数,用逗号间隔:");int a, b, c;   // 可以不赋初值。scanf("%d,%d,%d", &a, &b, &c);int d = a + b + c;printf("%d", d);

注意事项

  1. 不要在 scanf 的参 1 中,添加类似 printf() 的提示字符串和 \n 换行符。

  2. 键入数据时,数据个数、类型、顺序,必须与参 1 中占位符一一对应。

  3. 键入数据时,数据间的分割符,必须与 参 1 中 占位符的分割符一致。

  4. scanf 的返回值,代表格式符成功匹配数据的次数。(较少使用)

  5. VS2019 以后的版本编译时,会将 scanf 划为 “不安全函数”,爆出C4996 错误,推荐你使用 s_scanf() 函数。

    但,学习、练习、测试时,直接使用 scanf 很方便,可暂时屏蔽该错误。

    • 方法 1: 在项目中设置:工程名→右键→属性→C/C++→预处理器→预处理器定义→编辑→将 _CRT_SECURE_NO_WARNINGS 加入“预处理定义” 中
    • 方法 2: 在每个.c文件开头(第一行)添加宏:#define _CRT_SECURE_NO_WARNINGS

运算符

在这里插入图片描述

算术运算符

在这里插入图片描述

  1. 先 * / %,后 + -。
  2. 除法
    • 两整数相除,结果整数(舍小数部分)
    • 两浮点数相除,结果 double 浮点数
    • 整数和实数相除,自动类型转换为实型
    • 不允许 除 0(与数学一样)
  3. 取模(取余)
    • 运算数必须是整型
    • 对负数取余,结果为余数的绝对值
    • 不允许 除 0(与数学一样)
  4. 小数直接参与计算,结果有可能是不精确的

比较运算符

在这里插入图片描述

注意: 判断 “相等”,使用 “==”, 不能使用 “=”(赋值符)

优先级:

  1. 整体,比算术运算符 低。
  2. 整体,比赋值运算符 高。
  3. > >= < <= 高于 == !=

赋值运算符

包括:基本赋值运算符 和 复合赋值运算符( = 与算数运算符 组合而成)。

作用:给变量赋值。 结合性:自右向左
在这里插入图片描述

**注意:** 赋值运算符,会修改变量的原始值。 赋值符左侧,必须可修改(变量)。

自增减运算符

在这里插入图片描述

注意:++ 和 – 如果 不是单独使用(如:用在表达式中),前缀和后缀 差异巨大

  • 放在变量前,先 +1、-1 再取值使用。

    int a = 10;
    int res = ++a;  // 先 +1,再取 a 值给 res。 (先加再用)
    
  • 放在变量后,先 取值用,再 +1、-1

    int b = 10;
    int res2 = b--; // 先取 b 值给 res2, 而后 b 再 -1  (先用再减)
    

    注意事项:

    1. 不能用于常量
    2. 不能用于表达式
    3. 优先级:整体高于算数运算符(必然高于比较、赋值);后缀高于前缀
    4. 不要在一个表达式中,对同一变量 多次 ++、-- 运算。可读性差,且不用编译系统结果不同。

逻辑运算符

在这里插入图片描述

三元运算符

在这里插入图片描述

优先级:

高于 赋值运算符,低于 算数、逻辑、比较运算符。

逗号运算符

在这里插入图片描述

运算符的优先级

在这里插入图片描述

流程控制语句

顺序结构

顺序结构是程序默认的执行流程
顺序结构是从上往下依次运行的

分支结构

if语句

	if (关系表达式){语句体A;}else if(关系表达式) {语句体B;}else{语句体C;}

switch语句

把所有的选择一一列举出来,根据不同的条件任选其一。

	switch (表达式){case1:语句体1break;case2:语句体2break;......default:语句体n;break;}

在这里插入图片描述
在这里插入图片描述

循环结构

for循环

	/*for(初始化语句; 条件判断语句; 条件控制语句){循环体语句;}*///打印3遍Hellofor (int i = 0; i < 3; i++){printf("Hello\n");}

while语句

	/*初始化语句while(条件判断语句){循环体语句条件控制语句}*///打印3遍Helloint i = 0;while (i<3){printf("Hello\n");i++;}

在这里插入图片描述

do…while语句

在这里插入图片描述

循环高级

  • 无限循环
    在这里插入图片描述

  • 跳转控制语句

    • break:不能单独书写,只能写在switch,或者循环中,表示结束、跳出的意思。
    • continue:只能写在循环中,表示跳过本次循环,继续下次循环
  • 循环嵌套

    在这里插入图片描述

goto

可以跳转到任意地方。但一般只用于跳出循环。

	//外循环for (int i = 1; i <= 3; i++) {//内循环for (int j = 1; j <= 3; j++) {printf("打印内循环\n");break;//只能跳出内循环}printf("内循环结束!!!\n");}
	//外循环for (int i = 1; i <= 3; i++) {//内循环for (int j = 1; j <= 3; j++) {printf("打印内循环\n");//break;//只能跳出内循环goto a;}printf("内循环结束!!!\n");}//标号a:printf("外循环结束---");

函数

  • 函数:函数就是程序中独立的功能。

函数的定义格式

	返回值类型 函数名(形参1, 形参2...) {函数体;return 返回值;}

函数的定义调用

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>//定义一个函数
void test()
{printf("hello\n");
}// 主入口
int main()
{//调用test()函数test();return 0;
}
// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>//定义一个求和函数
int sum(int num1, int num2) {return num1 + num2;
}// 主入口
int main()
{//调用sum()函数int count = sum(2, 3);printf("%d\n", count);return 0;
}
// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>int sum(int num1, int num2);// 主入口
int main()
{//调用sum()函数int count = sum(2, 3);printf("%d\n", count);return 0;
}//定义一个求和函数
int sum(int num1, int num2) {return num1 + num2;
}

函数的注意事项

在这里插入图片描述

C语言中常见的函数

https://zh.cppreference.com/
  • math.h

    // 预处理,程序运行之前,需要提前做的事情
    #include<stdio.h>
    #include<math.h>// 主入口
    int main()
    {/*math.hpow()	幂sqrt()	平方根ceil()	向上取整floor()	向下取整abs()	绝对值*/double res1 = pow(2, 3);printf("%lf\n", res1);//8.000000double res2 = sqrt(9);printf("%lf\n", res2);//3.000000double res3 = ceil(9.1);printf("%lf\n", res3);//10.000000double res4 = floor(12.7);printf("%lf\n", res4);//12.000000int res5 = abs(-5);printf("%d\n", res5);//5return 0;
    }
    
  • time.h

    // 预处理,程序运行之前,需要提前做的事情
    #include<stdio.h>
    #include<time.h>// 主入口
    int main()
    {/*time.htime()	获取当前时间戳形参:表示获取的当前时间是否需要在其他地方进行存储,一般来说不需要在其他地方进行存储。NULL返回值:long long*/long long res = time(NULL);printf("%lld\n", res);return 0;
    }
    

随机数

在这里插入图片描述
在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<stdlib.h>// 主入口
int main()
{//设置种子srand(1);//获取随机数int num = rand();//输出打印printf("%d\n", num);return 0;
}

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<stdlib.h>
#include<time.h>// 主入口
int main()
{/*随机数的弊端:1.种子不变,随机数结果是固定的,种子默认是12.随机数的范围:默认0~32767目标:任意范围之内取一个随机数[1-100]  [7-23]  [8-49]1.范围包头不包尾,包左不包右,左闭右开  1-101  7-24  8-502.拿着尾巴 - 开头  101-1=100  24-7=17  50-8=423.修改代码 *///用一个变化的数据充当种子srand(time(NULL));//获取随机数int num1 = rand()%100 + 1; // 0~99 +1   >   1~100int num2 = rand()%17 + 7; // 0~16 +7   >   7~23int num2 = rand()%42 + 8; // 0~42 +8  >   8~50//输出打印printf("%d\n", num1);printf("%d\n", num2);return 0;
}

数组

数组是一个容器,可以用来存储同种数据类型的多个值。

数组的定义

数据类型 数组名[长度];int arr[3];
  • 连续的空间
  • 一旦定义,长度不可变

数组的初始化

初始化:定义数组的时候,第一次给数组赋值。

数据类型 数组名[长度] = {数据值,数据值...}
//长度省略:数据值的个数就是数组长度
int arr1[] = {1,2,3};
//长度未省略:数据值的个数<=长度
//未赋值的部分有默认值
//整数:0   小数:0.0  字符:'\0'  字符串:NULL
int arr2[5] = {1,2,3};

元素访问

	int arr1[] = {1,2,3};//数据类型 变量名 = 数组名[索引]int a = arr1[1];printf("%d\n",a);//2

数组遍历

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>// 主入口
int main()
{int arr[] = {1,2,3};int len = 3;//数组的长度for(int i=0; i<len; i++){printf("%d\n", arr[i]);}return 0;
}

内存中的数组

内存:软件在运行时,用来临时存储数据的。
内存地址:内存中申请空间的编号,用来快速管理内存空间。
64位操作系统:以64位二进制表示,书写时转成十六进制。
在这里插入图片描述

  • 变量的内存地址

    	int a = 10;printf("%p\n", &a);//000000F9C27AF9E4
    
  • 数组的内存地址

    	int arr[3] = { 1,2,3 };printf("%p\n", &arr);//数组首地址:000000C1E88FFB18printf("%p\n", &arr[0]);//数组第1个元素地址:000000C1E88FFB18printf("%p\n", &arr[1]);//数组第2个元素地址:000000C1E88FFB1Cprintf("%p\n", &arr[2]);//数组第3个元素地址:000000C1E88FFB20
    

数组的常见问题

  • 数组作为函数的形参
    实际上传递的是数组的首地址,如果要在函数中对数组进行遍历的话,要把数组的长度传递过去。
    定义处:arr表示完整的数组。
    函数中的arr:只是一个变量,用来记录数组的首地址。

    // 预处理,程序运行之前,需要提前做的事情
    #include<stdio.h>
    void printArr(int arr[],int len);// 主入口
    int main()
    {int arr[3] = { 1,2,3 };printf("%p\n", &arr);//000000178F9EFB48int len = sizeof(arr) / sizeof(int);printf("%p\n", &len);//000000178F9EFB74printArr(arr,len);return 0;
    }void printArr(int arr[],int len)
    {printf("%p\n", &len);//000000178F9EFB28printf("%zu\n", sizeof(len));//4printf("%p\n", arr);//000000178F9EFB48   在函数里面打印arr地址时,不需要&printf("%zu\n", sizeof(arr));//8  8个字节,这是内存地址值的大小for (int i = 0; i < len; i++){printf("%d\n", arr[i]);}
    }
    
  • 数组的索引越界
    最小索引:0
    最大索引:长度-1

数组查找算法

基本查找/顺序查找

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>int order(int arr[], int len, int num);// 主入口
int main()
{/*数组的基本查找核心思路:从0索引开始依次往后找,如果找到了则返回对应索引,如果没找到就返回-1*///1.定义数组int arr[] = { 11,22,55,77,44 };int len = sizeof(arr) / sizeof(int);//2.定义一个要查找的数据int num = 55;//3.调用函数查找数据int index = order(arr, len, num);//4.输出索引printf("%d\n", index);//2}int order(int arr[], int len, int num)
{for (int i = 0; i < len; i++){if (arr[i] == num) {return i;}}return -1;
}

二分查找/折半查找

前提条件:数组中的数据必须是有序的。如果是乱的,先排序再查找的话没意义,索引变了,只能确定是否存在。
核心逻辑:每次排除一半的查找范围。
在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>int binarySearch(int arr[], int len, int num);// 主入口
int main()
{//1.定义数组int arr[] = { 7,23,79,81,103,127,131,147 };int len = sizeof(arr) / sizeof(int);//2.定义一个要查找的数据int num = 131;//3.调用函数查找数据int index = binarySearch(arr, len, num);//4.输出索引printf("%d\n", index);//2}int binarySearch(int arr[], int len, int num)
{int min = 0;int max = len - 1;int mid;while (min <= max) {//确定中间位置mid = (max + min) / 2;if (arr[mid] < num) {//要查找的数据在当前mid右边min = mid + 1;}else if (arr[mid] > num) {			//要查找的数据在当前mid左边max = mid - 1;}else {return mid;}}//如果min>max,表示数据不存在,返回-1return -1;
}

插值查找

在这里插入图片描述

在这里插入图片描述

分块查找

数组特点:无序中透露着有序。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

哈希查找

树表查找

斐波那契查找

数组排序算法

冒泡排序

相邻的数据两两比较,小的放前面,大的放后面。

在这里插入图片描述

	//1.定义数组int arr[] = { 7,5,6,3,1,2,4 };int len = sizeof(arr) / sizeof(int);//2.排序//内循环:找到本次循环的最大值,再把最大值放到了最右边,即上一次最大值的左边//外循环:把上面这个动作重复了len-1次for (int i = 0; i < len - 1; i++){for (int 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;}}}//3.输出for (int i = 0; i < len; i++){printf("%d\n", arr[i]);}

选择排序

从0索引开始,拿着当前索引上的元素跟后面每一个元素依次比较,小的放前面,大的放后面。

在这里插入图片描述

	//1.定义数组int arr[] = { 7,5,6,3,1,2,4 };int len = sizeof(arr) / sizeof(int);//2.排序,小的在前面,大的在后面for (int i = 0; i < len - 1; i++){//i表示数组中的每一个索引for (int j = i+1; j < len; j++){//j表示i索引后的每一个索引if (arr[i] > arr[j]) {int temp = arr[j];arr[j] = arr[i];arr[i] = temp;}}}//3.输出for (int i = 0; i < len; i++){printf("%d\n", arr[i]);}

指针

指针其实是一个指针变量,里面存放的是内存地址。

指针定义格式

在这里插入图片描述

指针的作用

在这里插入图片描述
在这里插入图片描述

// 主入口
int main()
{//利用指针去获取变量中的数据 / 修改数据//定义一个变量int a = 10;//定义一个指针指向变量aint* p = &a;//利用指针获取变量中的数据printf("%d\n", *p);//10//利用指针修改变量中的数据*p = 200;//利用指针获取变量中的数据printf("%d\n", *p);//200//直接输出变量aprintf("%d\n", a);//200
}

操作其他函数中的变量

如果不使用指针,那么交换的只是传递过去的参数的值。

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>void swap(int num1, int num2);// 主入口
int main()
{//定义两个变量,要求交换变量中记录的值//注意:交换代码卸载一个新的函数swap中int a = 10;int b = 20;printf("调用前:%d,%d\n", a, b);swap(a, b);printf("调用后:%d,%d\n", a, b);return 0;
}void swap(int num1, int num2)
{//仅仅交换的是num1和num2的值int temp = num1;num1 = num2;num2 = temp;
}
// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>void swap(int* p1, int* p2);// 主入口
int main()
{//定义两个变量,要求交换变量中记录的值//注意:交换代码卸载一个新的函数swap中int a = 10;int b = 20;printf("调用前:%d,%d\n", a, b);swap(&a, &b);printf("调用后:%d,%d\n", a, b);return 0;
}void swap(int* p1, int* p2)
{//此时交换的是p1指针和p2指针指向的变量的值int temp = *p1;*p1 = *p2;*p2 = temp;
}

在这里插入图片描述
在这里插入图片描述

函数返回多个值

如果用数组返回的话,你怎么区分最大最小值呢

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>void getMaxAndMin(int arr[], int len, int* max, int* min);// 主入口
int main()
{//函数返回多个值//定义一个函数,求数组的最大值和最小值,并进行返回//1.定义一个数组int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int len = sizeof(arr) / sizeof(int);//2.调用getMaxAndMin函数求最大值和最小值int max = 0;int min = 0;printf("调用前:%d,%d\n", max, min);getMaxAndMin(arr, len, &max, &min);printf("调用后:%d,%d\n", max, min);return 0;
}void getMaxAndMin(int arr[], int len, int* max, int* min)
{*max = arr[0];for (int i = 1; i < len; i++){if(arr[i] > *max){*max = arr[i];}}*min = arr[0];for (int i = 1; i < len; i++){if (arr[i] < *min){*min = arr[i];}}
}

函数的结果和计算状态分开

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>int getRemainder(int num1, int num2, int* res);// 主入口
int main()
{//函数结果和计算状态分开//定义一个函数,将两数相除,获取余数//1.定义变量int a = 10;int b = 3;//结果int res = 0;//2.调用函数获取余数int flag = getRemainder(a, b, &res);if (!flag) {printf("余数:%d\n", res);}return 0;
}//返回值:表示计算状态 0正常 1不正常
int getRemainder(int num1,int num2,int* res)
{if (num2 == 0){//停止return 1;}*res = num1 % num2;return 0;
}

方便的操作数组和函数

指针高级

指针的使用细节

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

指针高级

指针计算

在这里插入图片描述

	int a = 10;int* p = &a;printf("%p\n", p);  //000000911D0FFB84printf("%p\n", p+1);//000000911D0FFB88printf("%p\n", p-1);//000000911D0FFB80

在这里插入图片描述

	//数组,内存空间是连续的int arr[] = { 1,2,3,4,5,6,7,8,9 };//获取0索引的内存地址int* p1 = &arr[0];printf("%d\n", *p1);printf("%d\n", *p1+1);//获取5索引的内存地址int* p2 = &arr[5];//间隔步长printf("%p\n", p2);//000000C03ED5F86Cprintf("%p\n", p1);//000000C03ED5F858  相差20个字节printf("%d\n", p2-p1);//步长5

指向不明的指针

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>int* method();// 主入口
int main()
{int a = 10;int* p1 = &a;printf("%p\n", p1);//00000013746FF524printf("%d\n", *p1);//10//p2 野指针:指针指向的空间未分配int* p2 = p1 + 10;printf("%p\n", p2);//00000013746FF54Cprintf("%d\n", *p2);//其他数字//p3 悬空指针:指针指向的空间已分配,但是被释放了int* p3 = method();printf("%p\n", p3);//000000986ED8F514printf("%d\n", *p3);//其他数字return 0;
}int* method()
{int num = 10;int* p = &num;return p;
}

没有类型的指针

在这里插入图片描述

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>void swap(void* p1, void* p2, int len);// 主入口
int main()
{int a = 10;short b = 20;int* p1 = &a;printf("%p\n", p1);//000000899D6FF804printf("%d\n", *p1);//10short* p2 = &b;printf("%p\n", p2);//000000899D6FF824printf("%d\n", *p2);//20//不同类型的指针之间,是不能互相赋值的char* p3 = (char*)p1;//warning C4047: “=”:“short *”与“int”的间接级别不同printf("%p\n", p3);//000000899D6FF804printf("%d\n", *p3);//10//void类型的指针打破了上面的观念//void没有任何类型//好处:可以接收任意类型指针记录的内存地址//缺点:无法获取变量里面的数据,也不能进行加减计算void* p4 = p1;//那有什么用呢?//可以把之前的swap优化成交换任意两个类型的//让函数更具有通用性int c = 10;int d = 20;swap(&c, &d, sizeof(c));printf("%d\n", c);//20printf("%d\n", d);//10return 0;
}void swap(void* p1, void* p2, int len)
{//把void类型的指针,转换成char类型的指针char* pc1 = p1;char* pc2 = p2;char temp = 0;//以字节为单位,一个字节一个字节进行交换for (int i = 0; i < len; i++){temp = *pc1;*pc1 = *pc2;*pc2 = temp;pc1++;pc2++;}}

二级指针和多级指针

作用:二级指针可以操作一级指针记录的地址。
在这里插入图片描述

	//定义变量int x = 10;printf("%p\n", &x);//000000D5AF5AF6C4 变量x的内存地址int y = 20;printf("%p\n", &y);//000000D5AF5AF6E4 变量y的内存地址 //定义一级指针int* p = &x;printf("%p\n", p);//000000D5AF5AF6C4 指针p指向变量x的内存地址printf("%d\n", *p);//10  指针p指向变量x  解引用出来是10printf("%p\n", &p);//000000D5AF5AF708 指针变量p的内存地址//定义二级指针int** pp = &p;printf("%p\n", pp);//000000D5AF5AF708 二级指针pp指向指针p的内存地址printf("%p\n", *pp);//000000D5AF5AF6C4 二级指针pp指向指针p 解引用出来是x的内存地址printf("%p\n", &pp);//000000D5AF5AF728 二级指针变量pp的内存地址//利用二级指针修改一级指针里面记录的内存地址//二级指针pp指向指针p的内存地址//指针p那块空间存的其实是变量x的内存地址//现在把指针p那块空间存的内容改为变量y的内存地址*pp = &y;//输出打印printf("%p\n", p);//000000D5AF5AF6E4 printf("%d\n", *p);//20 printf("%d\n", **pp);//20 

数组和指针

在这里插入图片描述

	//利用指针遍历数组//定义数组int arr[] = { 10,20,30,40,50 };int len = sizeof(arr) / sizeof(int);//获取数组的指针//实际上获取的是数组的首地址int* p1 = arr;int* p2 = &arr[0];printf("%p\n", p1);//000000992BF0FAF8 printf("%p\n", p2);//000000992BF0FAF8 for (int i = 0; i < len; i++){//printf("%d\n", *(p1 + i));printf("%d\n", *p1);p1++;}

在这里插入图片描述

在这里插入图片描述

	//定义数组int arr[] = { 10,20,30,40,50 };//sizeof运算的时候,不会退化,arr还是整体printf("%zu\n", sizeof(arr));//20 个字节//arr参与计算的时候,会退化为第一个元素的指针,记录的是内存地址的第一个元素首地址,也是数组的首地址,步长:数据类型 int 4 字节// &arr获取地址的时候,不会退化,记录的内存地址是第一个元素的首地址,步长:数据类型 * 数组的长度 20 个字节printf("%p\n", arr);//00000043205FFA88printf("%p\n", &arr);//00000043205FFA88printf("%p\n", arr+1);//00000043205FFA8Cprintf("%p\n", &arr+1);//00000043205FFA9C

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

	//定义二维数组int arr[3][2] = {{1,2},{3,4},{5,6} };//利用索引遍历for (int i = 0; i < 3; i++){for (int j = 0; j < 2; j++){printf("%d ", arr[i][j]);}			printf("\n");}
	//定义二维数组int arr1[3] = { 1,2,3 };int arr2[4] = { 2,5,4,9 };int arr3[5] = { 5,6,8,9,4 };int* arr[3] = { arr1, arr2, arr3 };//利用索引遍历for (int i = 0; i < 3; i++){//行不通,arr[i]参与计算时会退化为指向第一个元素的指针//int len = sizeof(arr[i]) / sizeof(int);for (int j = 0; j < len; j++){printf("%d ", arr[i][j]);}			printf("\n");}
	//定义二维数组int arr1[3] = { 1,2,3 };printf("%zu\n", sizeof(arr1));//12int arr2[4] = { 2,5,4,9 };int arr3[5] = { 5,6,8,9,4 };int* arr[3] = { arr1, arr2, arr3 };//在数组指针中,arr1使用数组名进行计算时,退化为指向第一个元素的指针,此时不在表示数组那个整体了//指针 --- 内存地址  64位win 8个字节printf("%zu\n", sizeof(arr[0]));//8int len1 = sizeof(arr1) / sizeof(int);int len2 = sizeof(arr2) / sizeof(int);int len3 = sizeof(arr3) / sizeof(int);int* lenArr[3] = {len1, len2, len3};//利用索引遍历for (int i = 0; i < 3; i++){//行不通,arr[i]参与计算时会退化为指向第一个元素的指针//int len = sizeof(arr[i]) / sizeof(int);for (int j = 0; j < lenArr[i]; j++){printf("%d ", arr[i][j]);}			printf("\n");}

在这里插入图片描述

	//定义二维数组int arr[3][2] ={{1,2},{3,4},{5,6}};// 数组内部元素的类型// 数据类型 * 指针名 = arr// int[2] * p// 书写习惯:int * p[2]// 怕误解为定义了一个数组,所以加括号 int(*p) [2]int(*p)[2] = arr;for (int i = 0; i < 3; i++){for (int j = 0; j < 2; j++){//遍历一维数组printf("%d ", *(*p + j));}printf("\n");//移动二维数组的指针p++;}

在这里插入图片描述

	//定义三个一维数组int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 11,22,33,44,55 };int arr3[5] = { 11,222,333,444,555 };//把三个一维数组的内存地址放入二维数组中int* arr[3] = { arr1,arr2,arr3 };//获取指针int** p = arr;//遍历数组for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++){printf("%d ", *(*p + j));}//换行printf("\n");//移动指针p++;}

在这里插入图片描述

函数和指针

在这里插入图片描述

void method1();
int method2(int num1, int num2);// 主入口
int main()
{//定义指针指向两个函数void (*p1)() = method1;int (*p2)(int, int) = method2;p1();int num = p2(2,3);printf("%d\n",num);return 0;
}void method1()
{printf("method1\n");
}
int method2(int num1, int num2)
{printf("method2\n");return num1 + num2;
}

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>int add(int num1, int num2);
int sub(int num1, int num2);
int mutiply(int num1, int num2);
int divide(int num1, int num2);// 主入口
int main()
{/*定义加、减、乘、除四个函数用户键盘录入三个数字前两个表示参与计算的数字第三个表示调用的函数1:加法2:减法3:乘法4:除法细节:只有形参完全相同而且返回值相同的函数,才能放到同一个函数指针数组中*///定义一个数组去装四个函数的指针//函数指针数组int (*arr[4])(int, int) = {add, sub, mutiply, divide};//让用户键盘录入三个数据printf("请录入两个数字参与计算:");int num1;int num2;scanf_s("%d%d", &num1, &num2);printf("函数方法:\n");printf("1:加法\n2:减法\n3:乘法\n4:除法\n");int choose;printf("请选择要调用的函数方法:");scanf_s("%d", &choose);//根据用户的选择,来调用不同的函数int res = (arr[choose - 1])(num1, num2);printf("计算结果:%d\n", res);return 0;
}int add(int num1, int num2) 
{return num1 + num2;
}
int sub(int num1, int num2) 
{return num1 - num2;
}
int mutiply(int num1, int num2) 
{return num1 * num2;
}
int divide(int num1, int num2)
{return num1 / num2;
}

字符串

定义字符串

	/*两种定义字符串的方式*///1.利用字符数组 + 双引号的方式定义字符串char str1[10] = "asdfghjkl";printf("%s\n", str1);//asdfghjklstr1[0] = 'w';printf("%s\n", str1);//wsdfghjkl//细节1:在底层实际存储的时候,c语言还是会帮我们把字符串转换成字符数组进行保存,并且还会在最后加上'\0'//细节2:数组的长度要么不写,写的话要把结束标记的空间预留出来//细节3:字符数组 + 双引号的方式定义字符串,内容可以发生改变//2.利用指针 + 双引号的方式定义字符串char* str2 = "zxcvbnm";printf("%s\n", str2);//细节1:在底层实际存储的时候,c语言还是会帮我们把字符串转换成字符数组进行保存,并且还会在最后加上'\0'//细节2:会把底层的字符数组放在只读常量区(不可修改、可以复用)//str2[0] = 'p';//错误代码为 -1073741819//printf("%s\n", str2);char* str3 = "zxcvbnm";printf("%p\n", str2);//00007FF67936AC10printf("%p\n", str3);//00007FF67936AC10

遍历字符串

	/*1.键盘录入一个字符串底层逻辑:程序在运行时,首先会创建一个长度为100的字符数组str在键盘录入时,会把每一个字符存入str数组中,并加上结束标记\0在这个过程中,需要修改字符数组的内容,所以第一种方式可以,第二种方式不可以*/char str[100];printf("请输入字符串:");scanf("%s", str);printf("输入的字符串为:%s\n", str);//遍历字符串得到每一个字符char* p = str;while (1){//利用指针获取字符串的每一个字符,直到遇到\0为止char a = *p;if (a == '\0') break;printf("%c\n", a);p++;}

遍历字符串数组

	/*定义一个数组存储5个学生名字并遍历*/char nameArr[5][20] = {"zhangsan","lisi","wangwu","zhaoliu","tianqi",};for (int i = 0; i < 5; i++){char* str = nameArr[i];printf("%s\n", str);}//第二种方式//把五个字符串的指针放入一个数组中char* strArr[5] = {"zhangsan","lisi","wangwu","zhaoliu","tianqi",};for (int i = 0; i < 5; i++){char* str = strArr[i];printf("%s\n", str);}

字符串常用函数

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<string.h>// 主入口
int main()
{char* str1 = "abc";//只读常量区,不可修改char str2[100] = "Abc";char str3[5] = { 'q','w','e','r','\0' };printf("----------strlen(长度)----------\n");//细节1:strlen这个函数在统计长度时不计算结束标记//细节2:在windows中,默认一个中文占两个字节//int len1 = strlen(str1);//3//int len2 = strlen(str2);//3//int len3 = strlen(str3);//4//printf("%d\n", len1);//printf("%d\n", len2);//printf("%d\n", len3);printf("----------strcat(拼接)----------\n");//细节1:把第二个字符串拷贝到第一个字符串的末尾//前提:第一个字符串可修改,第一个字符串剩余空间足够//strcat(str2,str3);//printf("%s\n", str2);//abcqwer//printf("%s\n", str3);//qwerprintf("----------strcpy(拷贝)----------\n");//细节1:把第二个字符串拷贝到第一个字符串,把原有内容覆盖//前提:第一个字符串可修改,第一个字符串空间足够//strcpy(str2, str3);//printf("%s\n", str2);//qwer//printf("%s\n", str3);//qwerprintf("----------strcmp(比较)----------\n");// 完全一样:0// 只要有一个不一样:非0// 细节:顺序不一致也是不一样,空格也是不一样//int res = strcmp(str2, str3);//printf("%d\n", res);//-1printf("----------strlwr(变小写)----------\n");//细节:不能转换中文_strlwr(str2);printf("%s\n", str2);	printf("----------strupr(变大写)----------\n");//细节:不能转换中文_strupr(str2);printf("%s\n", str2);return 0;
}

结构体

  • 结构体可以理解位自定义的数据类型
  • 它是由一批数据组合而成的结构型数据
  • 里面的每一个数据都是结构体的成员

结构体格式

在这里插入图片描述

定义结构体

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<string.h>struct Girl
{char name[20];int age;char gender;double height;
};// 主入口
int main()
{/*结构体:自定义的数据类型就是由很多数据组合成的一个整体每一个数据,都是结构体的成员定义的位置:函数里面:局部位置,只能在本函数中使用函数外面:全局位置,在所有函数中都可以使用*///使用结构体//定义一个女孩变量struct Girl lucy;//lucy.name = "露丝";//字符串赋值strcpy(lucy.name, "露丝");lucy.age = 23;lucy.gender = 'X';lucy.height = 1.63;printf("名字:%s\n", lucy.name);printf("年龄:%d\n", lucy.age);printf("性别:%c\n", lucy.gender);printf("身高:%.2f\n", lucy.height);return 0;
}

结构体数组

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>struct Student
{char name[20];int age;
};// 主入口
int main()
{//定义三个学生并进行赋值struct Student stu1 = { "zhangsan",23 };struct Student stu2 = { "lisi",24 };struct Student stu3 = { "wangwu",25 };//把三个学生放入数组struct Student stuArr[] = { stu1,stu2,stu3 };//遍历数组得到每一个元素for (int i = 0; i < 3; i++){struct Student temp = stuArr[i];printf("学生的信息为:姓名:%s\t年龄:%d\n", temp.name, temp.age);}return 0;
}

结构体别名

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>//Student大名可省略
typedef struct Student
{char name[20];int age;
} STU;// 主入口
int main()
{STU stu = { "zhangsan",23 };printf("学生的信息为:姓名:%s\t年龄:%d\n", stu.name, stu.age);return 0;
}

结构体作为函数参数

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>//Student大名可省略
typedef struct Student
{char name[20];int age;
} STU;//函数要写在结构体下面
void method(STU st);// 主入口
int main()
{/*定义一个函数,修改学生信息	*/STU stu = { "zhangsan",23 };printf("学生的初始信息为:姓名:%s\t年龄:%d\n", stu.name, stu.age);method(stu);printf("学生的信息修改为:姓名:%s\t年龄:%d\n", stu.name, stu.age);return 0;
}//void method(struct Student stu) 起别名前
/*细节:如果函数中写的是结构体类型的变量,相当于定义了一个新变量此时时把main函数中stu的数据,传递给了method的变量st在函数中仅仅修改了变量st中的值,对于main函数中stu的值没有修改
*/
void method(STU st)
{printf("接收到的学生的初始信息为:姓名:%s\t年龄:%d\n", st.name, st.age);printf("请输入要修改的学生姓名:");scanf("%s", st.name);printf("请输入要修改的学生年龄:");scanf("%d", &(st.age));printf("在函数中修改后的学生的初始信息为:姓名:%s\t年龄:%d\n", st.name, st.age);}
学生的初始信息为:姓名:zhangsan        年龄:23
接收到的学生的初始信息为:姓名:zhangsan        年龄:23
请输入要修改的学生姓名:lisi
请输入要修改的学生年龄:25
在函数中修改后的学生的初始信息为:姓名:lisi    年龄:25
学生的信息修改为:姓名:zhangsan        年龄:23
// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>//Student大名可省略
typedef struct Student
{char name[20];int age;
} STU;//函数要写在结构体下面
STU method(STU st);// 主入口
int main()
{/*定义一个函数,修改学生信息	*/STU stu = { "zhangsan",23 };printf("学生的初始信息为:姓名:%s\t年龄:%d\n", stu.name, stu.age);//接收函数返回值并赋值给stustu = method(stu);printf("学生的信息修改为:姓名:%s\t年龄:%d\n", stu.name, stu.age);return 0;
}STU method(STU st)
{printf("接收到的学生的初始信息为:姓名:%s\t年龄:%d\n", st.name, st.age);printf("请输入要修改的学生姓名:");scanf("%s", st.name);printf("请输入要修改的学生年龄:");scanf("%d", &(st.age));printf("在函数中修改后的学生的初始信息为:姓名:%s\t年龄:%d\n", st.name, st.age);return st;
}
学生的初始信息为:姓名:zhangsan        年龄:23
接收到的学生的初始信息为:姓名:zhangsan        年龄:23
请输入要修改的学生姓名:lisi
请输入要修改的学生年龄:26
在函数中修改后的学生的初始信息为:姓名:lisi    年龄:26
学生的信息修改为:姓名:lisi    年龄:26
// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>//Student大名可省略
typedef struct Student
{char name[20];int age;
} STU;//函数要写在结构体下面
void method(STU* p);// 主入口
int main()
{/*定义一个函数,修改学生信息	*/STU stu = { "zhangsan",23 };printf("学生的初始信息为:姓名:%s\t年龄:%d\n", stu.name, stu.age);//传递内存地址method(&stu);printf("学生的信息修改为:姓名:%s\t年龄:%d\n", stu.name, stu.age);return 0;
}//如果要在函数中修改stu的值,此时就不要再定义新的变量了
//直接接收stu的内存地址
//指针p里卖弄记录的是main函数中stu的内存地址
void method(STU* p)
{printf("接收到的学生的初始信息为:姓名:%s\t年龄:%d\n", (*p).name, (*p).age);printf("请输入要修改的学生姓名:");scanf("%s", (*p).name);printf("请输入要修改的学生年龄:");scanf("%d", &((*p).age));printf("在函数中修改后的学生的初始信息为:姓名:%s\t年龄:%d\n", (*p).name, (*p).age);}
学生的初始信息为:姓名:zhangsan        年龄:23
接收到的学生的初始信息为:姓名:zhangsan        年龄:23
请输入要修改的学生姓名:lisi
请输入要修改的学生年龄:25
在函数中修改后的学生的初始信息为:姓名:lisi    年龄:25
学生的信息修改为:姓名:lisi    年龄:25

结构体嵌套

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<string.h>typedef struct Message
{char phone[13];char mail[100];
} MESS;//Student大名可省略
typedef struct Student
{char name[20];int age;MESS mess;
} STU;// 主入口
int main()
{STU stu;strcpy(stu.name, "zhangsan");stu.age = 23;strcpy(stu.mess.phone, "13572321981");strcpy(stu.mess.mail, "13572321981@qq.com");printf("学生的初始信息为:姓名:%s\t年龄:%d\t手机号:%s\t邮箱:%s\t\n", stu.name, stu.age, stu.mess.phone, stu.mess.mail);strcpy(stu.name, "lisi");stu.age = 26;strcpy(stu.mess.phone, "199");strcpy(stu.mess.mail, "199@qq.com");printf("学生的信息修改为:姓名:%s\t年龄:%d\t手机号:%s\t邮箱:%s\t\n", stu.name, stu.age, stu.mess.phone, stu.mess.mail);//批量赋值/*STU stuu;stuu = { "lisi",25,{"15596330708","123@qq.com"} };//不可以在已经定义的结构体中批量赋值*/STU stu2 = { "lisi",25,{"15596330708","123@qq.com"} };//在对其声明的时候才可以批量赋值printf("批量赋值的信息为:姓名:%s\t年龄:%d\t手机号:%s\t邮箱:%s\t\n", stu2.name, stu2.age, stu2.mess.phone, stu2.mess.mail);return 0;
}

内存对齐

在这里插入图片描述

想当然是如下图,但其实不是。。。
在这里插入图片描述

实际上
在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>struct Num1
{double a;	//0 1 2 3 4 5 6 7char b;		//8 //补空白字节 9 10 11int c;		//12 13 14 15char d;		//16//补空白字节 17 18 19 20 21 22 23 24 (补至最大字节8的倍数)
};
struct Num2
{double a;	//0 1 2 3 4 5 6 7char b;		//8char d;		//9//补空白字节 10 11int c;		//12 13 14 15//补空白字节 16 (补至最大字节8的倍数)
};// 主入口
int main()
{/*内存对齐:不管是结构体,还是普通的变量都存在内存对齐规则:只能放在自己类型整数倍的内存地址上简单理解:内存地址 % 占用字节 = 0举例:int存放的位置,内存地址一定能被4整除结构体的内存对齐:结构体在上面的基础上又多了一点,结构体的大小是最大类型的整数倍用来确定最后一个数据补位的情况切记:对齐的时候会补空白字节,但是不会改变原本字节的大小心得:把小的数据类型写在最上面,大的数据类型写在下面*/int x = 10;//4个字节printf("%d\n", &x);struct Num1 n1;printf("%zu\n", sizeof(n1));//24struct Num2 n2;printf("%zu\n", sizeof(n2));//16return 0;
}

共用体

在这里插入图片描述

共用体的使用

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<string.h>//也可以起别名
union MoneyType
{int moneyi;double moneyd;char moneystr[100];
};// 主入口
int main()
{//需求:金融项目中,钱有可能是整数、小数、字符串//利用共同体定义钱的变量union MoneyType money;//赋值//整数moeneyi 小数moneyd 字符串moneystr//而且每次只能赋一个值//money.moneyi = 100;//money.moneyd = 100.32;strcpy(money.moneystr,"100万");//printf("%d\n", money.moneyi);//printf("%lf\n", money.moneyd);printf("%s\n", money.moneystr);return 0;
}

共用体的特点

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<string.h>//也可以起别名
union MoneyType
{int moneyi;			//4double moneyd;		//8char moneystr[100];	//100个1
};// 主入口
int main()
{//需求:金融项目中,钱有可能是整数、小数、字符串//利用共同体定义钱的变量union MoneyType money;//所有的变量都使用同一个内存空间printf("%p\n", &(money.moneyi));	//000000DF8397F4D0printf("%p\n", &(money.moneyd));	//000000DF8397F4D0printf("%p\n", &(money.moneystr));	//000000DF8397F4D0//所占的内存大小 = 最大成员的长度的整数倍printf("%zu\n", sizeof(money));	//104 printf("%zu\n", sizeof(money.moneyi));	//4printf("%zu\n", sizeof(money.moneyd));	//8printf("%zu\n", sizeof(money.moneystr));	//100//第二次复制会覆盖原有的数据money.moneyi = 99;printf("%d\n", money.moneyi);//99money.moneyd = 1.32;printf("%d\n", money.moneyi);//1374389535printf("%.2lf\n", money.moneyd);//1.32return 0;
}

结构体和共用体的区别

  • 结构体:一种事务中包含多个属性
  • 共用体:一个属性有多种类型

在这里插入图片描述

在这里插入图片描述

动态内存分配

在这里插入图片描述

常用函数

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<stdlib.h>// 主入口
int main()
{/*<stdlib.h>malloc	申请空间(连续)calloc	申请空间 + 数据初始化realloc	修改空间大小free	释放空间*///1.利用malloc函数申请一片连续的空间//存储100个int类型的整数//返回这片空间的首地址int* p = malloc(sizeof(int) * 100);printf("%p\n", p);//赋值for (int i = 0; i < 100; i++){//第一种赋值//*(p + i) = i + 1;//第二种赋值 底层 p[i] ---> p+ip[i] = i + 1;}//遍历for (int i = 0; i < 100; i++){printf("%d\n",*(p + i));}return 0;
}
	//2. calloc	申请空间 + 数据初始化 //int* p = malloc( 100, sizeof(int) );
// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>
#include<stdlib.h>// 主入口
int main()
{/*<stdlib.h>malloc	申请空间(连续)calloc	申请空间 + 数据初始化 realloc	修改空间大小free	释放空间*///1.利用malloc函数申请一片连续的空间//存储10个int类型的整数//返回这片空间的首地址int* p = malloc(sizeof(int) * 10);printf("%p\n", p);//0000024CA963D0A0//赋值for (int i = 0; i < 10; i++){//第一种赋值//*(p + i) = i + 1;//第二种赋值 底层 p[i] ---> p+ip[i] = i + 1;}//遍历for (int i = 0; i < 10; i++){//printf("%d\n",*(p + i));printf("%d\n", p[i]);}//3. 扩容 把原来的拷贝到新的空间int* p2 = realloc(p, sizeof(int) * 20);printf("%p\n", p2);//0000024CA9635CF0//追加赋值for (int i = 10; i < 20; i++){//第二种赋值 底层 p2[i] ---> p2+ip2[i] = i + 1;}//遍历for (int i = 0; i < 20; i++){printf("%d\n", *(p2 + i));}//4.释放原来的p指针内存空间free(p2);return 0;
}

动态内存分配的细节

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>// 主入口
int main()
{//1,malloc创建空间的单位是字节int* p1 = malloc(sizeof(int) * 10);//4*10 也就是40字节//2,malloc返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转int* p2 = (int*)malloc(sizeof(int) * 10);//c语言底层帮我们转了//3,malloc返回的仅仅是首地址,没有总大小,最好定义一个变量记录总大小int size = 10;//元素的个数int* p3 = malloc(sizeof(int) * size);//4*10 也就是40字节	//赋值for (int i = 0; i < size; i++){//*(p3 + i) = i + 1;p3[i] = i + 1;}for (int i = 0; i < size; i++){//printf("%d\n", *(p3 + i));printf("%d\n", p3[i]);}//4,malloc申请的空间不会自动消失,如果不能正确释放,会导致内存泄露//5,malloc申请的空间过多,会产生虚拟内存//6,malloc申请的空间没有初始化值,需要先赋值才能使用//7,free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值//8,calloc就是在malloc的基础上多一个初始化的动作//9,realloc修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失//10,realloc修改之后,无需释放原来的空间,函数底层会进行处理return 0;
}

C语言的内存结构

在这里插入图片描述

变量数组

在这里插入图片描述

全局变量和static变量

在这里插入图片描述

字符串

在这里插入图片描述

malloc函数

在这里插入图片描述

文件

在这里插入图片描述

路径

在这里插入图片描述

转义字符

表示改变 \ 后面这个符号原本的含义,把它变成一个普通的字符。

	/*绝对路径C:\aaa\a.txt相对路径:参照物:当前项目不以盘符开始:aaa\a.txt*/char* file = "C:\\aaa\\a.txt";printf("%s\n", file);

读写模式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

读取文件

在这里插入图片描述

// 预处理,程序运行之前,需要提前做的事情
#include<stdio.h>// 主入口
int main()
{//1.打开文件FILE* file = fopen("F:\\C\\program\\a.txt","r");//2.读取数据 // fgetc 一次读取一个字符,读不到返回-1int c = fgetc(file);printf("%c", c);//int c;//while ((c = fgetc(file))!=-1) {//	printf("%c", c);//}//fgets 一次读取一行,读不到返回null//每次读一行,以换行符为准//char arr[1024];//char* str = fgets(arr, 1024, file);//printf("%s", arr);//printf("%s", str);//char arr[1024];//char* str;//while ((str = fgets(arr, 1024, file))!=NULL)//{//	printf("%s", str);//}//fread 一次读多个//细节:abc占一个字节// 中文windows64位默认占两个字节//第一次:读取前面20个字节,把数组装满,函数返回20//第二次:读取后面20个字节,把数组装满,函数返回20//第三次:读取剩余的9个字节,把数组装满,函数返回9//第四次:没东西可读了,函数返回0char arr[1024];//装读到的数据//int n = fread(arr, 1, 1024, file);//printf("%d", n);//printf("%s", arr);int n;while ((n = fread(arr, 1, 1024, file))!=0) {for (int i = 0; i < n; i++){printf("%c", arr[i]);}}//3.关闭文件fclose(file);return 0;
}

写出数据

在这里插入图片描述

	/*fputc:一次写一个字符,返回写出的字符fputs:一次写一个字符串,写出成功返回非负数,一般忽略返回值fwrite:一次写多个,返回写出的字节个数*///1.打开文件FILE* file = fopen("F:\\C\\program\\b.txt", "w");//2写出数据//fputc:一次写一个字符,返回写出的字符//int c = fputc(97, file);//printf("%c", c);//a//fputs:一次写一个字符串,写出成功返回非负数,一般忽略返回值//int d = fputs("你好世界", file);//printf("%d", d);//0//fwrite:一次写多个,返回写出的字节个数char arr[] = { 97,98,99,100 };int n2 = fwrite(arr,1,4,file);printf("%d\n", n2);//4//3.关闭文件fclose(file);

拷贝文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/354000.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置Linux DNS服务器作为自己的windows 的 DNS服务器和 配置遇到的问题

安装DNS 库 和 DNS工具&#xff1a; # bind 是用于创建 dns服务的&#xff0c; bind-utils是用于测试DNS服务的工具 yum -y install bind bind-utils配置主配置文件&#xff1a; # 下载好后就已经有DNS服务&#xff0c;但是需要你自己去配置DNS服务信息# 配置主配置文件 [rootl…

ChatGPT 提示词技巧一本速通

目录 一、基本术语 二、提示词设计的基本原则 三、书写技巧 2.1 赋予角色 2.2 使用分隔符 2.2 结构化输出 2.3 指定步骤 2.4 提供示例 2.5 指定长度 2.6 使用或引用参考文本 2.7 提示模型进行自我判断 2.8 思考问题的解决过程 ​编辑 2.10 询问是否有遗漏 2.11 …

Spring源码-xxxAware实现类和BeanPostProcessor接口调用过程

xxxAware实现类作用 以ApplicationContextAware接口为例 ApplicationContextAware的作用是可以方便获取Spring容器ApplicationContext&#xff0c;从而可以获取容器内的Bean package org.springframework.context;import org.springframework.beans.BeansException; import or…

RK3588 Android12音频驱动分析全网最全

最近没有搞音频相关的了&#xff0c;在搞BMS, 把之前的经验总结一下。 一、先看一下Android 12音频总架构 从这张图可以看到音频数据流一共经过了3个用户空间层的进程&#xff0c;然后才流到kernel驱动层。Android版本越高&#xff0c;通用性越高&#xff0c;耦合性越低&#…

hdfs文件系统增删查原理

目录 1、hdfs读取文件原理 1.1、读取流程图解 1.2、架构层面读取流程详解 1.3、源码层面读取流程详解 2、hdfs写入文件原理 2.1、写入流程图解 2.2、架构层面写入流程 2.3、源码层面写入流程 3、hdfs删除文件原理 3.1、删除文件图解 3.2、架构层面删除流程 3.3、源码…

【Java】已解决java.lang.UnsupportedOperationException异常

文章目录 问题背景可能出错的原因错误代码示例正确代码示例注意事项 已解决java.lang.UnsupportedOperationException异常 在Java编程中&#xff0c;java.lang.UnsupportedOperationException是一个运行时异常&#xff0c;通常表示尝试执行一个不支持的操作。这种异常经常发生…

Word2Vec基本实践

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

MATLAB入门知识

目录 原教程链接&#xff1a;数学建模清风老师《MATLAB教程新手入门篇》https://www.bilibili.com/video/BV1dN4y1Q7Kt/ 前言 历史记录 脚本文件&#xff08;.m&#xff09; Matlab帮助系统 注释 ans pi inf无穷大 -inf负无穷大 i j虚数单位 eps浮点相对精度 0/&a…

【AI】通义千问使用指南:让你快速上手,成为问题解决高手!

大家好&#xff0c;我是木头左。 近日&#xff0c;继文心一言和讯飞星火之后&#xff0c;阿里虽迟但到&#xff0c;直接宣布开源两款“通义千问”大模型。作为国内首个开源且可商用的人工智能大模型&#xff0c;这会给我们带来哪些变化呢&#xff1f; 如何申请阿里通义千问&am…

JupyterLab使用指南(六):JupyterLab的 Widget 控件

1. 什么是 Widget 控件 JupyterLab 中的 Widget 控件是一种交互式的小部件&#xff0c;可以用于创建动态的、响应用户输入的界面。通过使用 ipywidgets 库&#xff0c;用户可以在 Jupyter notebook 中创建滑块、按钮、文本框、选择器等控件&#xff0c;从而实现数据的交互式展…

springboot集成积木报表,怎么将平台用户信息传递到积木报表

springboot集成积木报表后怎么将平台用户信息传递到积木报表 起因是因为需要研究在积木报表做数据筛选的时候需要拿到系统当前登录用户信息做筛选新的模块 起因是因为需要研究在积木报表做数据筛选的时候需要拿到系统当前登录用户信息做筛选 官网有详细介绍怎么集成进去的&…

力扣每日一题 6/19 排序+动态规划

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 2713.矩阵中严格递增的单元格数【困难】 题目&#xff1a; 给你一个下标从…

爆赞!GitHub首本Python开发实战背记手册,标星果然百万名不虚传

Python (发音:[ paiθ(ə) n; (US) paiθɔn ] n. 蟒蛇&#xff0c;巨蛇 )&#xff0c;是一种面向对象的解释性的计算机程序设计语言&#xff0c;也是一种功能强大而完善的通用型语言&#xff0c;已经具有十多年的发展历史&#xff0c;成熟且稳定。Python 具有脚本语言中最丰富…

无问芯穹Qllm-Eval:制作多模型、多参数、多维度的量化方案

前言 近年来&#xff0c;大语言模型&#xff08;Large Models, LLMs&#xff09;受到学术界和工业界的广泛关注&#xff0c;得益于其在各种语言生成任务上的出色表现&#xff0c;大语言模型推动了各种人工智能应用&#xff08;例如ChatGPT、Copilot等&#xff09;的发展。然而…

list集合自定义排序

一、基本类型排序 1.list中只有数字或字符串 //升序排序 List<T> ,T为数字或字符串 Collections.sort(list); //降序排序 Collections.sort(list,Collections.reverseOrder());2.list中为对象 基于jdk.18 import lombok.Data;Data public class User {private int i…

Android网络性能监控方案 android线上性能监测

1 Handler消息机制 这里我不会完整的从Handler源码来分析Android的消息体系&#xff0c;而是从Handler自身的特性引申出线上卡顿监控的策略方案。 1.1 方案确认 首先当我们启动一个App的时候&#xff0c;是由AMS通知zygote进程fork出主进程&#xff0c;其中主进程的入口就是Ac…

linux环境编程基础学习

Shell编程&#xff1a; 相对的chmod -x xx.sh可以移除权限 想获取变量的值要掏点dollar&#xff08;&#xff04;&#xff09; 多位的话要加个花括号 运算&#xff1a;expr 运算时左右两边必须要加空格 *号多个含义必须加转义符 双引号可以加反单&#xff0c;但是发过来就不行 …

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA 的幸运游戏(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

Idea连接GitLab的过程以及创建在gitlab中创建用户和群组

上期讲述了如何部署GitLab以及修复bug&#xff0c;这期我们讲述&#xff0c;如何连接idea。 首先安装gitlab插件 下载安装idea上并重启 配置ssh免密登录 使用管理员打开命令行输入&#xff1a;ssh-keygen -t rsa -C xxxaaa.com 到用户目录下.ssh查看id_rsa.pub文件 打开复制…

算法第六天:力扣第977题有序数组的平方

一、977.有序数组的平方的链接与题目描述 977. 有序数组的平方的链接如下所示&#xff1a;https://leetcode.cn/problems/squares-of-a-sorted-array/description/https://leetcode.cn/problems/squares-of-a-sorted-array/description/ 给你一个按 非递减顺序 排序的整数数组…