Day1【normal】
相关知识点
函数
(一) 概念:完成某个特定功能的指令序列的封装
作用:
(一) 用来实现代码的复用
要求:(高内聚【相关功能集中在一个函数】,低耦合【函数与函数之间没有什么关系】)
(二) 实现模块化设计 ——> 结构非常清晰
(三) 可以灵活组合调用
(二) 设计:
(一) 需求分析:要完成什么功能?需要什么资源【输入、输出】 ——> 从而确定了 “函数名 和 形参列表”
(二) 功能实现 ——> 算法
(三) 定义:
格式:
返回值类型 函数名(形参列表)
{
功能语句;
return ;
}
注意:
1、返回值类型:return后面表达式值的类型
【一般为单值类型(基本类型、指针类型),也可以不需要返回值(void),如果不指定时,默认为int类型】
2、函数名:须符合C语言标识符的规则
3、形参列表:【void】数据类型1 形参1,数据类型2 形参2...
4、不可在函数内部定义函数
(四) 调用:
1、主调函数和被调函数:调用和被调用
2、实际参数和形式参数
(五) 数据传递:
“分时”:
主调函数 ——> 传参 ——> 被调函数
主调函数 <—— return <—— 被调函数
“实时”:
1、全局变量
(六) 作用域与生存期:
作用域:变量名能够产生作用的范围
1、全局变量:函数体外,定义在头文件之下
作用域范围:从定义开始到整个文件结束
注意:
1、全局变量在别的文件也可以调用,但要extern声明(不可赋值,否则会从声明变成定义)
2、对于每一个函数来说,在函数前都有一个extern
3、如果不想让其他文件修改全局变量和调用函数【保护】在定义之前加上static修饰
------------------------------------------------------------------------------------------------------
2、局部变量:在函数体内或者某个复合语句内
作用域范围:从定义开始到复合语句结束【即"}"】
------------------------------------------------------------------------------------------------------
【重点!!!!】
(一) 不同作用域的两个变量(就算两个变量同名),存在不同的两个不同的空间【就近往上寻找】
(二) 当主调函数调用被调函数的时候,就相当于只是简单赋值而已
生存期:变量过了生存期就会释放空间
1、全局变量:随进程持续存在
------------------------------------------------------------------------------------------------------
2、局部变量
(一) 普通局部变量:从定义处到函数结束或者复合语句结束
(二) static修饰的局部变量:随进程持续存在
(七) 递归:在函数调用中,直接或间接调用自己
经典例题:求阶乘 n!
练习作业
1、作业
作业1
例题1:
递归求数列
a1=10,an=a(n-1)+15
求出输入第n项的值和前n项的和
arg:
n 第n项的值
作业2
例题2:
求最近的质数
如果输入的数本身就是质数,输出它自己
如果输入的数最近的质数有两个,输出小的那个
arg:
n 输入的参数
作业3
例题3:
汉诺塔 打印出移动的路径
arg:
n 汉诺塔的层数
思考拓展
(一) 形参列表相关拓展
(一) main函数默认的两个参数:argc、argv【虽然我们一般不写但是它们是存在的】:
1、argc【arguments count】:必须是整型变量
2、argv【arguments value/vector】:必须是指向字符串的指针数组
(二) 可变形参【待补充】
(二) for循环的两个生存周期
例题:
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int i = 0; i < 5; i++)
{
printf("%2d", i);
}
printf("%5d\n",i);
}
}
结果:
0 1 2 3 4 0
0 1 2 3 4 1
0 1 2 3 4 2
0 1 2 3 4 3
0 1 2 3 4 4
分析:
说明在for循环内部定义的生存周期只是在其对应的循环体中
(三) 函数返回值类型不一定是单值类型
之前笔记中有说过函数返回值一般是单值类型,但不是说只能返回单值类型【复合类型也是可以的】
(四) 函数指针和指针函数
(一)、函数指针【本质为指针】
格式:
数据类型 (*函数名)(形参列表)
例子:
int (*fun)(void); -------> 是一个int类型函数的指针
(二)、指针函数【本质为函数】
格式:
数据类型* 函数名(形参列表)
例子:
int * fun(void); -------> 返回一个int*类型的数据
(五) void*类型
1、void【补充】(无类型):
对函数返回的限定和对参数返回的限定,即可以不用输入值和输出值
2、void*(无类型指针):
其他类型指针可以不需要强转为(void*),直接给void*变量赋值
例如:
void *p1;
int *p2;
p1 = p2;
Day2 + Day3 + Day4【normal】
相关知识点
数组(一组具有相同数据类型的数据集合)
【如果有多个同类型的变量,就可以用到数组】
(1) 分类
1、一维数组
1、定义格式:
类型说明符 数组名[整型表达式]
【以 int a[4];为例子:本质还是int[4] a;相当于有一种数据类型叫int[4],这个对二维数组的理解很重要】
说明:
(一) 类型说明符:只要是合法的类型即可
(二) 数组名:1、要符合C语言标识符规则规范 2、数组名为数组的首地址(常量),所以一定【不能修改】
(三) 整型表达式
2、使用:
引用:数组名[下标] <————————> 跟普通变量用法一致
【注意】:下标从0开始,可以是常量表达式也可以是变量表达式
初始化:"{ }"
【注意】:1)、只能在定义的时候初始化
2)、在进行全部初始化时,注意不能越界
3)、可以只对部分元素进行初始化,没有赋值到的部分自动赋值为0
4)、 如果对数组元素赋值,那么我们就可以不指定数组长度,例如 int a[]={1,2,3,4};
5)、一个特殊数组:int a[]={}; 数组长度为0的数组【然而并没有什么很大的用处】
6)、对于一个可变数组时,不可进行初始化【补充:只能通过scanf循环来循环赋值】
2、二维数组
1、定义格式:
类型说明符 数组名[整型表达式1][整型表达式2]
说明:
(一) 类型说明符:只要是合法的类型即可
(二) 数组名:1、要符合C语言标识符规则规范 2、数组名为数组的首地址(常量),所以一定【不能修改】
(三) 整型表达式
2、使用:
引用:数组名[行][列] <————————> 跟普通变量用法一致
【注意】:下标从0开始,可以是常量表达式也可以是变量表达式
初始化:"{ }"
【注意】:1)、只能在定义的时候初始化
2)、在进行全部初始化时,可以用{}来区分(没什么很多用),注意不能越界
3)、可以只对部分元素进行初始化,可以用{}来区分(会把一列没有不全的值补齐),没有赋值到的部分自动赋值为0
4)、 如果对数组元素赋值,那么我们就可以不指定数组长度,例如 int a[][3]={1,2,3,4,5,6};
5)、对于一个可变数组时,不可进行初始化【补充:只能通过scanf循环来循环赋值】
3、字符数组
1)、定义
两种定义方法:
1、char c1[]={'c','h','i','n','a'}; 【sizeof(c1) --> 5】
2、char c2[]="china"; 【sizeof(c2) --> 6】
【易错点】:
char c2[]="china";--->char c2[]={'c','h','i','n','a','\0'};
2)、相关函数
(一) gets、puts
1、gets【BUG】
【注意】:
a. gets有巨大的BUG,gets函数不会考虑保存这个字符串的数组的空间是否足够,如果超出了分配的范围还会继续写数据,可能会改变其他变量的数据。
b. gets函数不会获取到'\n',当遇到'\n'的时候,会补一个'\0'并且结束输入,puts函数在输出的时候会自动换行
c. gets获取字符串的时候,还可以获取空格,tab等空白字符,回车结束输入
2、puts
【注意】:把str指向的空间中保存的字符串打印出来,直到遇到第一个'\0',且自动换行
----------------------------------------------------------------------------------------------------------
(二) scanf
【注意】:scanf函数遇到空格、tab、回车结束输入
----------------------------------------------------------------------------------------------------------
(三) strcpy/strncpy
1、 char *strcpy(char *dest, const char *src);
@dest:指向用来保存的字符串数据
@src:指向要拷贝的字符串的数据
返回值:
如果成功,返回拷贝后字符串的首地址,就是dest指向的地址
如果失败,返回NULL
【注意】:
a.不会关心dest指向的空间是否够用,造成内存溢出
b.strncpy函数就是为了修复strcpy函数的BUG的,它的功能与strcpy一样,只不过顶多拷贝n个字节
2、 char *strncpy(char *dest, const char *src, size_t n);
@dest:指向用来保存的字符串数据
@src:指向要拷贝的字符串的数据
@n:最多拷贝n个字节
返回值:
如果成功,返回拷贝后字符串的首地址,就是dest指向的地址
如果失败,返回NULL
【注意】:
strncpy到底拷贝了多少个字节?
(1)在还没有拷贝n个字节的时候,提前遇到src的'\0',剩余的字节也会拷贝'\0'
(2)没遇到'\0',但是已经拷贝了n个字节,结束拷贝。(不会补'\0')
----------------------------------------------------------------------------------------------------------
(四) strcat/strncat
1、char *strcat(char *dest, const char *src);
@dest:指向用来保存的字符串的空间,注意空间是否够用
@src:指向要追加的字符串
返回值:
如果成功,返回追加后字符串的首地址,就是dest指向的地址
如果失败,返回NULL
【注意】:
a.strcat函数也有bug,不关系dest指向的空间是否够用,可能会造成内存溢出
b.strncat与strcat功能类似,只不过只追加n个字节
2、char *strncat(char *dest, const char *src, size_t n);
@dest:指向用来保存的字符串的空间,注意空间是否够用
@src:指向要追加的字符串
@n:至多追加n个字节
返回值:
如果成功,返回追加后字符串的首地址,就是dest指向的地址
如果失败,返回NULL
【注意】:
strncat到底追加了多少个字节?
(1)正常追加了n个字节的数据后,会在末尾补一个'\0'
(2)在还没有拷贝到n个字节的时候,遇到了src的'\0',提前结束追加(会在末尾补一个'\0')
----------------------------------------------------------------------------------------------------------
(五) strcmp/strncmp
都是从第一个字符开始比较,一个一个字符的比较ASCII码的大小
if(c1 > c2)则返回c1的ASCII-c2的ASCII >0
if(c1 < c2)则返回c1的ASCII-c2的ASCII <0
if(c1 == c2) 则继续比较下一个,如果全部相等则返回0
1、int strcmp(const char *s1, const char *s2);
2、int strncmp(const char *s1, const char *s2, size_t n);
【注意】:strncmp与strcmp功能类似,只不过只比较两个字符串的前n个字符
----------------------------------------------------------------------------------------------------------
(六) strlen
【注意】:strlen函数遇到'\0'自动结束计数且不包括'\0',与sizeof有很大区别
练习作业
1、作业
作业1
// 例题1:冒泡排序法
/*
功能:
完成冒泡排序法的算法
输入:
void
输出:
void
*/
作业2
// 例题2:斐波那契数列
/*
功能:
输出斐波那契数列第n项的值
输入:
n 第n项
输出:
-1 参数有误
其他值 斐波那契数列第n项的值
*/
作业3
// 例题3:判断数组类型
/*
功能:
判断数组类型:【升序、降序、平序、乱序】
输入:
void
输出:
void
*/
作业4
// 例题4:二分查找法
/*
功能:
输入指定元素,找到对于下标
输入:
void
输出:
-1 无对应元素的下标
其他值 所查元素所在的下标
*/
思考拓展
(一) printf( )的执行顺序
例子:
int num=0;
int add()
{
num++;
return num;
}
int main()
{
printf("%d %d\n",num,add());
printf("%d %d\n",add(),num);
}
结果:
1 1
2 1
分析:
【由于printf输出的顺序实际是从右向左】
所以第一个是先add()再输出,结果:1 1
同理第二个是先再输出add(),结果:2 1
(二) 排序【待补充】
1、冒泡排序
2、插入排序
3、选择排序
4、快速排序
5、堆排序
6、希尔排序
7、归并排序
8、基数排序
9、计数排序
10、桶式排序
(三) 查找(基于C)【待补充】
1、顺序查找
int sequentialSearch(int[] array, int target) {
for (int i = 0; i < array.length; i++) {
if (array[i] == target) {
return i;
}
}
return -1;
}
2、二分查找
int binarySearch(int[] array, int target) {
int left = 0, right = array.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] == target) {
return mid;
} else if (array[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
3、插值查找
4、斐波那契查找
5、树表查找
6、分块查找
7、哈希表查找
(四) 数组指针和指针数组
(一)、数组指针【本质为指针】
格式:
数据类型 (*函数名)[整型表达式]
例子:
int (*nums)[4]; -------> 是一个指向int[4]类型数组的指针
(二)、指针数组【本质为数组】
格式:
数据类型* 函数名[整型表达式]
例子:
int * nums[4]; -------> 是一个4个单元且每个单元是一个指针的一维数组
(五) int a[ ][3]
int a[ ][3] 的本质就是 -------> int[3] a[],即不定长的以int[3]为一个单元的数组
(六) 数据库知识的基础复习
对于表操作:
1、“增”
2、“删”
3、“改”
4、“查”
(七) 字符串可读可写性【常量的判断,很重要!!!】
例题:
1、char str1[10] = "abcd";
2、char str2*="abcd";
对于上述两种表达式,判断以下操作是否是正确的?
a.str1[0]='z';
b.str2[0]='z';
c.str1++;
d.str2++;.
结果&分析:
a.right char str1[10] = "abcd";是已经向系统申请了空间来存放"abcd"并将其放在栈里【可读可写】
b.error char str2*="abcd",只是将指针指向"abcd"空间,没有单独开辟空间,此时“abcd”是常量,如果赋值就犯了1=2;的问题【赋值符号左边必须是变量】
c.error 由于对于数组名来说是一个常量,所以如果赋值就犯了1=2;的问题【赋值符号左边必须是变量】
d.right 由于str2是变量,可以进行赋值操作
Day5【easy】
相关知识点
C程序的组成部分:
1、c文件
(1)、预处理语句
a.文件包含命令 #include
【注意】(再次提到):#include <> 和 #include ""的区别:
1、前者在库函数指定的路径去搜索(/usr/include,......)
2、后者
1、先在工程目录(当前目录)下去查找头文件
2、找不到再去指定的路径去搜索(必须是编译命令+ -I[你自己指定的位置])
3、还是找不到再去库函数指定的路径
b.宏定义 #define
【注意】
1、#define 可以不带参数,即我们”熟知“的【符号常量】
2、#define 可以带入参数,但是这是简单的替换,一般需要我们添加括号
【例如】 #define Fun(a,b) a*b 和 #define Fun(a,b) (a)*(b) 的区别
c.条件定义
1、#ifdef + #endif
2、#ifndef + #endif
2、#if + #endif
【拓展】:查看预处理: gcc 1.c -E -o 1.i //用gcc把1.c进行预处理,还没有编译,然后生成1.i
(2)、全局声明
"声明":C语言中的声明就是把一个名字的属性进行说明。
一般在定义变量或者函数的时候,就规定了变量或者函数的属性 所以称之为定义也属于声明
C语言的编译顺序是从上到下,所以我们在调用某个变量或者函数的时候,必须要先声明(定义),让系统知道它们属性之后,再调用它们。
声明的分类:
变量的声明(定义变量,声明外部变量)
函数的声明(定义函数,声明函数)
类型声明(定义类型) -->构造类型 【补充】
(3)、函数
1、main函数(由它开始【唯一入口】,由它结束) 【由操作系统调用main函数】
2、其他函数
------------------------------------------------------------------------------------------------------------------
2、h文件【来进行一些声明,比如导出一个函数接口(API)或者全局变量 】
内容:
1、函数声明
2、构造类型声明【拓展:结构体/联合体的构建是不会占据内存】
3、全局变量的外部声明
【提示】: 每个独立的模块引用其他模块的时候,尽量不用放在h文件中,重复引用
工程搭建的结构:
project【工程名字】
| ----- bin 程序运行文件
| ----- src 所有的源文件(.c文件)
| ----- lib 可能用的到的外部的库文件
| ----- inc 所有的自定义的头文件(.h文件)
| ----- LICENSE.txt 项目的说明书(项目名称,启动时间,负责人,功能介绍.....)
思考拓展
(一) 随机函数的使用
1.播放随机数种子
srandom(time());
2.随机数获取
int num = random();
(二) 考试错题总结
1、对于笔试考试时,一定要堤防变量是否舒适化的问题
2、关系运算符的先后顺序【以后再错,就抄10边那个关系比较式!!!】
3、if - else的组合关系 【注意看if()后是否有;】
4、对于任意一个数组,都不可直接对数组名直接进行赋值【懂得都懂,但考试的时候很容易被迷惑】
例如:char str[4]; str = "123"; 【错误】
5、如果出现野指针,栈溢出等问题时,系统的提示是"段错误",不是"下标越界"!!!!!
6、字符长度 -----> strlen() 判断结果 数组长度 -----> sizeof() 判断结果