- 鹏哥C语言
一.初识C语言(杂项)
-
C语言语法规定,变量要定义在代码块最前
-
extern关键字,用来声明变量,声名外部函数或变量
-
scanf是C语言提供的
scanf_s是VS编译器提供的,不可在其他编译器使用,因此会产生警告
-
解决方案1:加预处理指令
#define _CRT_SECURE_NO_WARNINGS 1
解决方案2:项目属性→配置属性→C/C++→预处理器定义→编辑→加上
_CRT_SECURE_NO_WARNINGS 1
-
-
常量类型:
-
字面常量 eg:1、0.3、3.14
-
const修饰的常量 eg:
const int num=4;//此时num的值将不能改变
-
#define定义的标识符常量 eg:
#define MAX 10
-
枚举常量。枚举关键词—enum
eg:
enum Sex { MALE, //男,默认0 FEMALE, //女,默认1 SECERT //保密,默认2 }
-
-
字符串:字符串的结束标志是
\0
一个转义字符,在计算字符串长度是\0
是结束标志,不计入字符串长度eg:"abc"字符串后隐藏一个
\0
-
strlen()计算字符串长度:
在库函数<string.h>中
注意:在计算字符串长度直到空结束字符,但不包括空结束字符
-
转义字符:
eg1:
//要输出"c:\test\32\test.c",转义字符会使代码无法正常输出 //代码需要写成 printf("%s\n","c:\\test\\32\\test.c");//才能正常输出
eg2:
//要输出一个单引号时 printf("%c\n",'\'');
eg3:
printf("%d\n",strlen("c:\test\32\test.c"));//结果为13 //因为\t、\32都是一个字符(\32表示八进制的32,字符串输出时要转化成十进制数字对应的ASCⅡ码值代表的字符
-
VS的监视功能:调试→窗口→监视→监视①
-
static
- 修饰全局变量时,改变了变量的作用域,让静态的全局变量只能在自己所在的源文件内使用
- 修饰局部变量时,改变生命周期,直至程序结束
- 修饰函数时,改变函数的链接属性,由外部链接转变为内部链接
-
scanf()和getchar()的区别(!!!不理解)
scanf()可输入不含空格的字符串,不读取回车,空格和回车表示输入完毕
getchar()只能读取用户输入缓存区的一个字符,包括回车
ed:如果要在是scanf()使用后若要使用getchar(),则getchar()会读取到留在缓存区中的回车或scanf()读取后,空格后剩余的一串字符串,因此使用getchar()前要清空缓存区中的回车
//a写法 char c_temp; while((c_temp!='\n')&&(c_temp!='EOF')); //b写法 int ch=0; while((ch=getchar())!='\n'); //c写法 fflush(stdin);//清空缓存区
-
if else语句中,esle与最近的if匹配,与对齐方式无关(无括号的情况下)
-
折半查找找算法(!!!)
-
sizeof与strlen()的区别
- sizeof是运算符,是一个关键字,可用于任何类型在字符数组中会统计
\0
所占的空间 - strlen()是一个函数,只能用于
\0
结尾的字符串,在计算字符串大小时不会统计\0
- sizeof是运算符,是一个关键字,可用于任何类型在字符数组中会统计
-
延时函数
- 头文件:
#include<windows>
- 函数:
sleep();//括号内填数字,单位为毫秒
- 头文件:
-
清空屏幕:
- 头文件:
#include<stdlib.h>
- 函数:`system(“cls”);//执行系统命令中的cls
- 头文件:
-
==不能用来比较字符串
- 要用字符串比较函数
- 头文件:
#include<string.h>
- 函数:
strcmp(strA,strB);//返回值结果为0时表示两字符串相等
- 头文件:
- 要用字符串比较函数
-
创建一个C语言项目(VS2013版本)
- 文件→新建→项目→visual C++→空项目
- 源文件(右键)→添加→新建项→C++文件
-
若VS程序执行后屏幕上无法输出内容
- 解决方法:点击项目名称→属性→配置属性→链接器→系统→子系统→改为“控制台”
-
/是斜杠,\是反斜杠
-
VS2013常用快捷键
- 代码自动对齐:Ctrl+K+F
- 撤销:Ctrl+Z
- 反撤销:Ctrl+Y
- 快速隐藏当前代码段:Ctrl+M+M
- 注释:Ctrl+K+C
- 取消注释:Ctrl+K+U
- 查找:Ctrl+F
- 替换:Ctrl+H
- 开始执行不调试:Ctrl+F5
-
开平方函数:
- 库:
#include<math.h>
- 函数:
sqrt();
- eg:
double n=sqrt(4.0);//输出时用%lf占位
- eg:
- 库:
-
-
%2d占位,表示输出占两位,不够两位补空格,右对齐
-
%-2d占位,表示输出占两位,不够两位补空格,左对齐
-
-
生成随机数
- 库:
#include<stdlib.h>
- 函数:
rand();//生成的是随机数
- 需要使用:
srand((unsigned int)time(NULL));//初始化随机数发生器,放在主函数前
- time()函数来自库<time.h>
- 库:
-
函数递归
- 什么是函数递归?
- 程序调用自身的编程技巧称为递归( recursion)。递归作为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归策略只需少量的程序就可以大大减少程序的代码量。递归的主要思考方式:把大事化小
- 递归的两个必要条件:
- 存在限制条件:当满足这个限制条件时,递归便不再继续
- 每次递归调用之后越来越接近这个限制条件
- 什么是函数递归?
-
数组初始化
char arr[5]={'a',97};//两种赋值相同,都可以
char arr[]="abcdef";//默认数组大小为7,包括\0
-
二维数组初始化
int arr[3][4]={1,2,3,4,5};//1234在第一行,5在第二行
int arr[3][4]={{1,2,3},{4,5}};//123在第一行,45在第二行
- 行可以省略,列不能省略
-
数组名是首元素地址,但有两个例外
- sizeof(arr),此时数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名 表示的是整个数组的地址
-
除号/
- 两端如果都是整数,结果也是整数,如果要得到小数,则至少要有一个操作数为浮点数
-
>>右移操作符
& 按位与
| 按位或
^ 按位异或
-
sizeof(int [10]);/值为40
-
char和short的整型提升
-
指针大小
- 在32位平台上时为4字节,在64位平台上为8字节
-
指针类型
-
决定解引用时能够访问空间的大小,还决定解引用时能够访问空间的大小
-
int *p;//*p能访问4个字节 char *p;//*p能访问1个字节 double *p;//*p能访问8个字节
-
-
野指针成因:
- 指针未初始化
- 指针访问越界
- 指针指向的空间释放
-
如何规避野指针:
- 指针初始化
- 小心指针越界
- 指针指向空间释放即放置空指针
- 指针使用前检查有效性
-
指针减去指针
- 同一个数组中,单以指针输出,差值为地址差值,相减时输出结果为下标的差值(即中间元素个数)
-
指针与数组
- arr与&arr[0],都表示首元素地址
- 特殊:
- &arr,取的是整个数组的地址
- sizeof(arr),测的是整个数组大小
-
二级指针
-
int a=10; int *pa=&a; int **ppa=&pa;//二级指针 *ppa;//一次解引用拿到pa的值 **ppa;//两次解引用拿到a的值
-
-
指针数组
int *arr[10]={&a,&b,&c};
-
调试
-
Debug版本(调试版本):可以调试,包含调试信息,不做任何优化
Release版本(发布版本) :进行过各种优化,不包含调试信息
- 程序的Debug版本和Release版本可能不同,例如对变量内存的分配位置
-
调试快捷键:
- F5:启动调试,配合F9使用
- F9:切换断点,可以在任意位置设置断点
- F10:逐过程,不进入函数
- F11:逐语句,进入函数
- Shift+F5:停止调试
- Shift+F11:跳出过程
-
常用:调试→窗口→监视/内存/反汇编
- Shift+Alt+C:调用堆栈
-
结构体
- struct:结构体关键字
- typedef:重命名
- 结构体成员的类型可以时数组、指针、标量、其他结构体
- 结构体传参,传结构体地址性能更好
-
函数栈帧的创建和销毁
-
- 栈区的默认使用:先使用高地址,再使用低地址
- 数组的默认使用:数组元素由高地址向低地址排列
-
1.const放在指针变量的*的左边时,修饰的是*p,也就是说,不能通过p改变*p的值 eg:const int *p=# *p=20;//不可以 p=&n;//可以 2.const放在指针变量*的右边时,修饰的是p本身,即p的地址无法改变 eg:int * const p=# *p=20;//可以 p=&20;//可以 3.const int * const p=#//此时内容和地址都不能更改
-
常见的错误有:
- 编译型错误
- 链接型错误
- 运行时错误
-
二.C语言进阶
1. 数据的存储
- 类型的意义:
- 使用这个类型开辟内存空间的大小
- 如何看待内存空间的视角
-
字节顺序:
-
大端字节序:高字节数据存放在低地址处,低字节数据存放在高地址处
-
小端字节序:高字节数据存放在高地址处,低字节数据存放在低地址处
-
eg:
-
int a=20;// 高字节 ← 0x00 00 00 14 →低字节 小端:低地址 ← 14 00 00 00 → 高地址 大端:低地址 ← 00 00 00 14 → 高地址
-
-
-
-
%u 打印十进制无符号数字
2.指针进阶
1.字符指针
2.数组指针
3.指针数组
4.数组传参和指针传参
5.函数指针
6.函数指针数组
7.指向函数指针数组的指针
8.回调函数
4.自定义数据类型
1.结构体
- 结构体类型的声明
- 结构体的自引用
- 结构体变量的定义和初始化
- 结构体内存对齐
- 结构体传参
- 结构体实现位段(位段的填充&可移植性)
2.枚举
- 枚举类型的定义
- 枚举的优点
- 枚举的使用
3.联合
- 联合类型的定义
- 联合的特点
- 联合大小的计算
5.动态内存分配
-
#include<stdlib.h>
中的四个函数-
void *malloc(int num);
- malloc()函数向内存申请一块连续可用的空间,并返回这块空间的指针
- 如果开辟成功,则返回一个指向开辟空间的指针
- 如果开辟失败,则返回NULL,因此一定要检查malloc的返回值
- 返回值类型是void*,所以使用时要进行强制类型转换
- 如果参数为0,则malloc的行为由编译器决定
- malloc()函数向内存申请一块连续可用的空间,并返回这块空间的指针
-
void free(void *address);
-
void *calloc(int num,int size);
-
在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是 0。
-
int* p=(int*)calloc(10,sizeof(int));
-
与malloc的区别只在于返回地址前把申请的空间的每个字节初始化为0
-
-
void *realloc(void *address,int newsize);
- 调整动态开辟空间的大小
-
int* p=(int*)calloc(20);//使用malloc开辟的20个字节空间//假设不够用,需要40个字节的空间,就可以使用reallo函数 int *ptr=realloc(p,40); if(ptr!=NULL) { p=ptr; }
- realloc函数使用的注意事项:
- 如果p指向的空间之后有足够的空间可以追加,则直接追加,后返回p
- 如果p指向的空间之后没有足够的空间可以追加,则realloc函数会重新找一个新的内存区域开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间
- 用一个新的指针接收realloc函数的返回值
-
-
向内存申请10个整形的空间,用malloc()函数,来自库<stdlib.h>
int *p=(int*)malloc(10*sizeof(int));//malloc函数返回值类型为void类型的指针,需要强制类型转换 if(p==NULL) { printf("%s\n",strerrer(errno));//用来打印错误原因,来自库<errno.h> } else { //正常使用 //当动态申请的空间不再使用时,应该还给操作系统 free(p); p=NULL; }