当前位置: 首页 > news >正文

游标卡尺 东莞网站建设建网站权威机构

游标卡尺 东莞网站建设,建网站权威机构,代码网站怎么制作,深圳 企业 网站建设文章目录 前言基础知识main函数防BUG注释#xff08;重要#xff09;关键字标识符命名#xff08;驼峰命名#xff09;常量类型变量printf1.输出不同类型数据2.输出不同宽度数据3.不同类型数据长度归类 scanf函数运算符sizeof#xff08;运算符#xff0c;优先级2#x… 文章目录 前言基础知识main函数防BUG注释重要关键字标识符命名驼峰命名常量类型变量printf1.输出不同类型数据2.输出不同宽度数据3.不同类型数据长度归类 scanf函数运算符sizeof运算符优先级2逗号运算符关系运算符逻辑运算符三目运算符 强制类型转换流程控制if语句switch case循环结构for循环 跳转语句嵌套循环函数main函数了解递归函数了解 进制exit函数 位运算符char类型转义字符数组数组名作为参数二维数组 字符串指针二级指针数组指针指针字符串函数指针 结构体结构体成员的访问结构体成员的初始化结构体数组结构体指针结构体内存分配结构体嵌套 共用体枚举其他杂乱知识点补充预处理指令宏定义普通宏定义带参宏定义条件编译 typedef关键字const关键字内存管理mallocfreecallocrealloc 链表文件操作记录的比较潦草 STM32 常用数据类型 前言 在嵌入式开发的时候发现由于没有系统学习过C语言时导致编程时只会一些简单语法既导致开发效率低又导致程序结构很差于是打算重新学习一下C语言。 摘抄自c语言入门这一篇就够了-学习笔记(一万字) 基础知识 main函数 主函数main会由系统自动调用其他的函数不会一个程序只能有一个main函数。 main函数前面的int可以换成void或者不写不会报错甚至return 0都可以省略。因为产生多种C语言标准 当然还是要写最标准的 嵌入式C语言main.c文件中 int main(void) {//初始化等操作while(1){//裸机程序循环执行} }防BUG 我喜欢的一个防BUG神器 ━━━━━━神兽出没━━━━━━┏┓    ┏┓┏┛┻━━━━━━┛┻┓┃    ┃┃   ━ ┃┃ ┳┛  ┗┳ ┃┃    ┃┃   ┻ ┃┃ ┃┗━┓    ┏━┛Code is far away from bug with the animal protecting┃    ┃ 神兽保佑,代码无bug┃    ┃┃    ┗━━━┓┃    ┣┓┃    ┏━━┛┛┗┓┓┏━┳┓┏┛┃┫┫ ┃┫┫┗┻┛ ┗┻┛━━━━━━感觉萌萌哒━━━━━━注释重要 简单列出我觉得有用的两种类型 //第一种注释函数 /*** brief printMap* param map 需要打印的二维数组* param row 二维数组的行数* param col 二维数组的列数*/ void printMap(char map[6][7] , int row, int col) {........//第二种思路分析 /*R代表一个人#代表一堵墙 // 0123456####### // 0# # // 1#R ## # // 2# # # // 3## # // 4####### // 5分析:1.保存地图(二维数组)2.输出地图3.操作R前进(控制小人行走)3.1.接收用户输入(scanf/getchar)w(向上走) s(向下走) a(向左走) d(向右走)3.2.判断用户的输入,控制小人行走3.2.1.替换二维数组中保存的数据(1.判断是否可以修改(如果不是#就可以修改)2.修改现有位置为空白3.修改下一步为R)3.3.输出修改后的二维数组4.判断用户是否走出出口 */ 关键字 图片来自c语言入门这一篇就够了-学习笔记(一万字) 标识符命名驼峰命名 当变量名或函数名是由多个单词连接在一起构成标识符时第一个单词以小写字母开始之后的单词的首字母大写。 例如: myFirstName 常量类型 科学计数法例1230001.23e5或1.23E5E前后必须有数字不能有空格后面的必须是整数前面的可以是小数也可以是类似90这样的可以约的数字 字符常量单引号括起来的‘a’单引号里面只能有一个字符转义字符特殊情况’\n’换行、’\t’跳格 字符串常量双引号括起来的“aba”、“a”末尾会自动加一个字符‘\0’为结束标志 只有小数位.3 单精度小数0.5f或0.5F6位小数 双精度小数默认3.1415位小数 八进制01230开头 十六进制0x开头 二进制0b开头 变量 变量初始化不推荐的 int a, b 10; //部分初始化 int c, d, e; c d e 0;变量占用储存空间 变量存储的过程 根据定义变量时声明的类型和当前编译环境确定需要开辟多大存储空间 在内存中开辟一块存储空间开辟时从内存地址大的开始开辟内存寻址从大到小 将数据保存到已经开辟好的对应内存空间中 printf 1.输出不同类型数据 在嵌入式中经常将串口数据发送重定向为printf确实方便很多 printf(a %类型, a);int b -10; printf(b %u\n, b); // 429496786 //无符号int2^32,所以输出的2^32-10 printf(b %o\n, b); // 37777777766 printf(b %x\n, b); // fffffff6float c 6.6f; double d 3.1415926; // 单、双精度浮点数(默认保留6位小数) printf(c %f\n, c); // 6.600000f是单精度 printf(d %lf\n, d); // 3.141593,lf是双精度但默认都是6位所以6位下用f也行也可以%.15lf测试%.15f也行 //一般情况下 printf是兼容f和lf的所以一般用f就行可能某些编译器没遇到过不兼容 //当然在scanf函数输入的时候还是要规范但嵌入式里面没有double e 10.10; // 以指数形式输出单、双精度浮点数 printf(e %e\n, e); // 1.010000e001 printf(e %E\n, e); // 1.010000E001 2.输出不同宽度数据 定义输出宽度在十进制下实际位数多于指定宽度,则按照实际位数输出, 如果实际位数少于指定宽度则以空格补位。 对于小数按照.宽度的格式用于定义输出的小数位数多出来的用0补齐。会四舍五入 printf(a %[宽度]类型, a); printf(a %.[位数]f, a);printf(d %.0f\n, c); //四舍五入为整数动态指定保留小数 double a 3.1415926; printf(a %.*f, 2, a); // 3.14 printf(a %.4f, a); // 3.1415对于小数来说注意整数部分也是算在有效数字里面的 对于单精度数,使用%f格式符输出时,仅前6~7位是有效数字 对于双精度数,使用%lf格式符输出时,前15~16位是有效数字 float c 5546.62222222222f; printf(c %.15f\n, c); // c 5546.622070312500000整数不同宽度 int a 1;printf(a |%5d|\n, a); // | 1|int b 1234567;printf(b |%3d|\n, b); // |1234567|不同标志 int a 1; int b -1; // -号标志 printf(a |%5d|\n, a); // | 1| printf(a |%-5d|\n, a);// |1 | // 号标志 printf(a |%d|\n, a);// |1| printf(b |%d|\n, b);// |-1| // 0标志 printf(a |%5d|\n, a); // | 1| printf(a |%05d|\n, a); // |00001| // 空格标志 printf(a |% d|\n, a); // | 1| printf(b |% d|\n, b); // |-1| // #号 int c 10; printf(c %o\n, c); // 12 printf(c %#o\n, c); // 012 printf(c %x\n, c); // a printf(c %#x\n, c); // 0xa3.不同类型数据长度归类 printf(a %[长度]类型, a);char a a; short int b 123; int c 123; long int d 123; long long int e 123; printf(a %hhd\n, a); // 97 printf(b %hd\n, b); // 123 printf(c %d\n, c); // 123 printf(d %ld\n, d); // 123 printf(e %lld\n, e); // 123数据类型如果不匹配会警告数据进行转换如果没超范围没什么超出范围数据就会错误 float 1bit符号位 8bits指数位 23bits尾数位 float的指数范围为 -127 ~ 129 float的范围为-2^128 ~ 2^128也即-3.40E38 ~ 3.40E38 double 1bit符号位 11bits指数位 52bits尾数位 double的指数范围为 -1023 ~ 1024 double的范围为-2^1024 ~ 2^1024也即-1.79E308 ~ 1.79E308。 scanf函数 由于嵌入式里面没有这个所以就简单记录一下 scanf接收键盘数据用法和printf类似接收非字符和字符串类型时, 空格、Tab和回车会被忽略 int number; int value; // 可以输入 数字 空格 数字, 或者 数字 回车 数字 scanf(%d%d, number, value);\n是scanf函数的结束符号所以格式化字符串中不能出现\n // 输入完毕之后按下回车无法结束输入 scanf(%d\n, number);系统输入时放在缓冲区内部的输入缓冲区不为空的时候scanf会一直从缓冲区中获取例如 #include stdio.h int main(){int num1;int num2;char ch1;scanf(%d%c%d, num1, ch1, num2);printf(num1 %d, ch1 %c, num2 %d\n, num1, ch1, num2);char ch2;int num3;scanf(%c%d,ch2, num3);printf(ch2 %c, num3 %d\n, ch2, num3); }系统输入时放在缓冲区内部的输入一次就把两次的函数scanf调用解决了。 可以利用下面函数清空缓冲区所有平台有效 setbuf(stdin, NULL);向屏幕输出一个字符的putchar和从键盘获取一个字符的getchar char ch a; putchar(ch); // 输出achar ch; ch getchar();// 获取一个字符运算符 在加、减、乘、除这些双目运算符中如果参与运算的两个操作数其中一个是浮点数那么结果一定是浮点数。 求余运算符中两个数必须都是整数运算结果正负取决于被除数与除数无关。 result 10 % -3; //1 result -10 % 3;//-1还有一些复合运算符/、*、%、、-具有右结合性 自增和自减都是只能用于单个变量不能用于常量和表达式且在企业开发中尽量单独出现不和其他运算符混用。因为不同编译器下是不一样的自增或自减后如何运算。 sizeof运算符优先级2 sizeof用来计算一个变量或常量、数据类型所占的内存字节数 有几种写法 sizeof( 变量\常量 ); sizeof 变量\常量; sizeof( 数据类型); sizeof(10); char c a; sizeof(c); char c a; sizeof c; sizeof(float); //数据类型不能省略括号sizeof是运算符 int a 10; double b 3.14; double res sizeof ab; //先计算sizeof a4再加b最后为7.14逗号运算符 把多个表达式连接起来组成一个表达式 逗号运算符会从左至右依次取出每个表达式的值, 最后整个逗号表达式的值等于最后一个表达式的值 int a 10, b 20, c;//这不是c (a 1, b 1); //21关系运算符 C语言中任何非零值都为“真” 避免浮点数的判断因为float和double有精度问题 float a 0.1; float b a * 10 0.00000000001; //超精度了 double c 1.0 0.00000000001; //b和c是不相等的double a 0.1; double c 0.1; //a和c是相等的float a 0.1; double c 0.1; //a和c是不相等的逻辑运算符 A BA为假B不执行 A || BA为真B不执行 三目运算符 格式: 表达式1表达式2(结果A)表达式3(结果B) int res ab?a:cd?c:d; //先计算ab?a:(cd?c:d); 具有右结合性强制类型转换 // 将double转换为int int a (int)10.5;// 当前表达式用1.0占用8个字节, 2占用4个字节 // 系统会自动对占用内存较少的类型做一个“自动类型提升”的操作, 先将其转换为当前算数表达式中占用内存高的类型, 然后再参与运算 double b 1.0 / 2; //2转换为double// 赋值时左边是什么类型,就会自动将右边转换为什么类型再保存 int a 10.6;// 结果为0, 因为参与运算的都是整型 double a (double)(1 / 2); // 结果为0.5, 因为1被强制转换为了double类型, 2也会被自动提升为double类型 double b (double)1 / 2;//类型转换不影响与原变量的值流程控制 if语句 当我们省略大括号的时候后面就不能定义变量了。 一般if里面的判断语句中常量放在前面if10a这样漏写的时候会报错好习惯 switch case switch表达式 {case 常量表达式1....break;case 常量表达式2....break;case 常量表达式3....break;default:.....break; }switch的条件表达式必须是整型或者是可以被提升为整型的值char类型、short类型的值。 case的值也只能是常量并且还必须是整型, 或者可以被提升为整型的值(char、short)并且case后面不能一样同样case后面要定义变量也要加大括号。警惕无break导致的穿透问题。 switch中default可以省略switch中default放到哪都会等到所有case都不匹配才会执行(穿透问题除外) switch(1.1)// 报错case a://可以相当case 97case num: // 报错case 4.0: // 报错case 1: // 报错,两个case后面不能一样.....break; case 1: // 报错.....break;循环结构 while和do while如果while省略了大括号, 那么后面不能定义变量 while循环条件 { }//不管while中的条件是否成立, 都会执行一次循环体 do{}while循环条件for循环 while能做的for都能做, 所以企业开发中能用for就用for, 因为for更为灵活for更节约内存空间 for(初始化表达式循环条件表达式循环后的操作表达式) {循环体中的语句; }//最简单的死循环 for(;;);//for循环里面初始化可以放在里面也可以放在外面 for (int count 0; count 10; count) {int count 0; for (; count 10; count) { 跳转语句 break 跳出switch或者各种循环 while(...) {............... if(...){......break;//立刻跳出while} }//break离开应用范围存在是没有意义的 if(1) {break; // 会报错 }在多层循环中一个break语句只向外跳一层 while(1) {while(2) {break;// 只对while2有效, 不会影响while1} }return 结束当前函数将结果返回给调用者continue 结束本轮循环进入下一轮 while1 {.....if(...){.....continue; //跳过本轮while循环进入下一轮} }//不能离开应用范围 if(1) {continue; // 会报错 }goto 破坏程序结构不利于维护阅读但是该用还是要用 goto 语句仅能在本函数内实现跳转不能实现跨函数跳转(短跳转)。但是他在跳出多重循环的时候效率还是蛮高的 //例1 // loop:是定义的标记 loop:if(num 10){printf(num %d\n, num);num;// goto loop代表跳转到标记的位置goto loop;} //例2while (1) {while(2){goto lnj;}}lnj:printf(跳过了所有循环); 嵌套循环 应当将最长的循环放在最内层最短的循环放在最外层以减少 CPU 跨切循环层的次数 例如多个for循环嵌套 函数 返回值类型 函数名(参数类型 形式参数1参数类型 形式参数2…) {函数体;返回值; }函数名后面小括号()中定义的变量称为形式参数简称形参形参变量只有在被调用时才分配内存单元在调用结束时,即刻释放所分配的内存单元。形参只有在函数内部有效函数调用结束返回主调函数后则不能再使用该形参变量。 在调用函数时, 传入的值称为实际参数简称实参形参实参类型不一致, 会自动转换为形参类型实参和形参之间只是值传递修改形参的值并不影响到实参函数可以没有形参。 如果没有写返回值类型默认是int max(int number1, int number2) {// 形式参数return number1 number2 ? number1 : number2; }一个函数内部可以多次使用return语句但是return语句后面的代码就不再被执行。 函数声明很熟悉了有几个注意点 函数的实现不能重复, 而函数的声明可以重复。 函数声明可以写在函数外面,也可以写在函数里面, 只要在调用之前被声明即可比如在main函数里面声明后再调用。不过感觉有些乱这样 如果被调函数的返回值是整型时可以不对被调函数作说明而直接调用真的但是会有警告还是不知道为好啥用没有 main函数了解 关于main函数了解 int main(int argc,const char * argv[]) {}int argc : 系统在启动程序时调用main函数时传递给argv的值的个数 const char * argv[] : 系统在启动程序时传入的的值, 默认情况下系统只会传入一个值, 这个值就是main函数执行文件的路径 也可以通过命令行或项目设置传入其它参数 递归函数了解 自己嵌套自己能用循环实现的功能,用递归都可以实现但代码理解难度大内存消耗大(易导致栈溢出), 所以考虑到代码理解难度和内存消耗问题, 在企业开发中一般能用循环都不会使用递归。 进制 二进制0b… 八进制0… 十六进制0x… 十进制正常 exit函数 exit(0) 表示程序正常退出 exit⑴/exit(-1)表示程序异常退出。 只要一调用整个程序都会立马结束 位运算符 位运算只用于所有的整型(charshortintlong intlong longunsigned charunsigned short…)浮点值均不适用 可以对多位数操作 按位与 | 按位或 ^ 按位异或 ~ 按位取反 其中~有些特殊相当单目运算例如怪怪的 ~9 - -10 0000 0000 0000 0000 0000 1001 // 取反前 9的补码 1111 1111 1111 1111 1111 0110 // 取反后// 根据负数补码得出结果 1111 1111 1111 1111 1111 0110 // 补码反码加1计算机内部运算是补码形式 1111 1111 1111 1111 1111 0101 // 反码负数的源码除了第一位符号位剩下的全部取反 1000 0000 0000 0000 0000 1010 // 源码 -10//但是可以应用在其他方面 char a0x89; //即a1000 1001 char ch~a; //则ch0111 0110但a的值仍为1000 1001char类型转义字符 数组 数组初始化没有初始化数值是随机的不一定是0 int ages[3] {4, 6, 9}; //普通初始化int nums[5] {[4] 3,[1] 2}; //部分初始化int nums[3]; //先定义后初始化 nums[0] 1; nums[1] 2; nums[2] 3;数组长度计算 int ages[4] {19, 22, 33, 13}; int length sizeof(ages)/sizeof(int); //4数组名指向的是整个数据存储空间最小的地址即[0]的地址。 定义数组的时候 []里面可以写整型常量或者常量表达式 int ages4[A] {19, 22, 33}; //奇奇怪怪会有这样写的场景吗 printf(ages4[0] %d\n, ages4[0]);int ages5[5 5] {19, 22, 33}; printf(ages5[0] %d\n, ages5[0]);int ages5[A 5] {19, 22, 33}; printf(ages5[0] %d\n, ages5[0]);//错误的写法 // 没有指定元素个数错误 int a[];// []中不能放变量 int number 10; int ages[number]; // 老版本的C语言规范不支持int number 10; int ages2[number] {19, 22, 33} // 直接报错// 只能在定义数组的时候进行一次性全部赋值的初始化 int ages3[5]; ages10 {19, 22, 33};//不能这样赋值只能单个赋值数组元素作为实参还是值传递 void change(int val)// int val number {val 55; } change(ages[0]);数组名作为参数 数组名作为参数是地址传递数组名代表了该数组在内存中的起始地址实参数组名将该数组的起始地址传递给形参数组,两个数组共享一段内存单元, 系统不再为形参数组分配存储单元两个数组共享一段内存单元, 所以形参数组修改时实参数组也同时被修改了 void change2(int array[3])// int array 0ffd1 {array[0] 88; }int ages[3] {1, 5, 8};change(ages);printf(ages[0] %d, ages[0]);// 88 在函数形参表中,允许不给出形参数组的长度 void change(int array[]) {array[0] 88; }形参数组和实参数组的类型必须一致,否则将引起错误 当数组名作为函数参数时, 因为自动转换为了指针类型所以在函数中无法动态计算数组的元素个数 void printArray(int array[]) {printf(printArray size %lu\n, sizeof(array)); // 8int length sizeof(array)/ sizeof(int); // 2printf(length %d, length); }二维数组 //分段赋值 int a[2][3]{ {80,75,92}, {61,65,71}}; //按行连续赋值 int a[2][3]{ 80,75,92,61,65,71};//省略第一维长度 int a[][3]{{1,2,3},{4,5,6}}; int a[][3]{1,2,3,4,5,6};int a[2][] {1, 2, 3, 4, 5, 6}; // 错误写法 //这样二维数组的列数不确定可以是任意个//指定元素初始化 int a[2][3]{[1][2]10}; int a[2][3]{[1]{1,2,3}}值传递和地址传递看的是函数形参的类型如果是数组就是地址传递 void change(char ch){ch n; }change(cs[0][0]);//值传递不改变原数组的值void change(char ch[]){ch[0] n; }change(cs[0]);//地址传递改变cs[0][0]为’n‘void change(char ch[][3]){ch[0][0] n; } char cs[2][3] {{a, b, c},{d, e, f}}; change(cs); //地址传递改变cs[0][0]为’n‘void test(char cs[2][]) // 错误写法 {}二维数组作为函数参数在被调函数中不能获得其有多少行可以计算出二维数组有多少列 void test(char cs[2][3]) {size_t col sizeof(cs[0]); // 输出3printf(col %zd\n, col); }字符串 char name[9] jhj; //在内存中以“\0”结束 \0ASCII码值是0 char name[] c\0ool; //中间不能包含\0, 因为\0是字符串的结束标志这个字符串就是“c” char name3[9] {j,n,j}; //其他元素默认是0字符串输出 char chs[] jhj; printf(%s\n, chs); //根据传入的地址逐个取出输出直到遇到\0当定义一个char xxx[10]的时候最多存放9个字符留一个给\0。 用scanf函数输入字符串时,字符串中不能含有空格。 C语言常有函数puts、gets输出和输入 sizeof判断字符串长度结束符\0也是算的 strlen判断字符串长度结束符\0不算 字符串连接函数strcatstring catenate格式: strcat(字符数组名1,字符数组名2) 将两个字符串连接在一起并且删除字符串1的结束符\0 字符串拷贝函数:strcpy格式: strcpy(字符数组名1,字符数组名2)把字符数组2中的字符串拷贝到字符数组1中。串结束标志“\0”也一同拷贝 字符串比较函数:strcmp格式: strcmp(字符数组名1,字符数组名2) 按照ASCII码顺序比较两个数组中的字符串,并由函数返回值返回比较结果。顺序比较一旦遇到不同就停下比较了 字符串1字符串2,返回值0; 字符串1字符串2,返回值1; 字符串1字符串2,返回值-1。 char oldStr[100] asd; char newStr[50] asaaa; printf(%d, strcmp(oldStr, newStr)); //输出结果:-1 char oldStr[100] 1; char newStr[50] 1; printf(%d, strcmp(oldStr, newStr)); //输出结果:0 char oldStr[100] asd; char newStr[50] asd; printf(%d, strcmp(oldStr, newStr)); //输出结果:0字符串数组 char names[2][10] { {j,h,j,\0}, {w,j,\0} }; char names2[2][10] { {wj}, {jhj} }; char names3[2][10] { wj, jhj };指针 有内存地址和存储地址是编号存储空间是放数据的每个内存都有对应的地址。 指针变量就是存放其他变量的地址。格式指针指向数据的类型 * 指针变量名 *表示这是一个指针变量 char ch a; char *p; // 一个用于指向字符型变量的指针 p ch; int num 666; int *q; // 一个用于指向整型变量的指针 q num; int *pNULL; // 定义指针变量int *p; *pa; //错误写法 前面不能加* p 250; // 错误写法多个指针可以指向同一地址指针的指向可以改变指针没有初始化里面就是一个垃圾值称为野指针可能会导致程序崩溃。 取地址变量名 取得是其他变量的地址 *只是用来说明这是一个指针变量在不是定义的好时候是一个操作符表示访问指针的指向的存储 int a 5; int *p a; printf(a %d, *p); // 访问指针变量p的指向的空间的数据关于占用空间的问题一个int占4个字节一个char占1个字节一个double占8个字节 所以对于指针变量来说会自动根据指针变量前面的类型名判断要访问多少个字节的存储空间 二级指针 如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。也称为“二级指针” char c a; char *cp; cp c; char **cp2; cp2 cp; printf(c %c, **cp2);数组指针 指针变量保存数组元素的地址 int a{123456}int *p; pa;//a是数据首元素的地址int *pa; //等价int *pa[0]; //等价数组名a不代表整个数组,只代表数组首元素的地址。 当指针变量指向数组的第一个数据的时候允许进行的运算 整数、整数p1 -整数、-整数p-1 p –p- - 加法指向数据后面的元素的地址 减法向前指向 访问数据的时候可以a[1]下标访问或者*p1这样指针变量形式访问。 数组名固定指向数组的第一个数据的地址它是不能运算操作的例如a错误的 指针字符串 char string[]”I love wj!”; //字符串名指向的也是第一个元素的地址即string[0]的地址char *str abc// 数组名保存的是数组第0个元素的地址, 指针也可以保存第0个元素的地址char *str wj; //str是一个指针和上面的string一样字符串指针名字指向的是第一个元素的地址 for(int i 0; i strlen(str);i) //就像strlen(string) {printf(%c-, *(stri)); // 输出结果:w-j }不能修改字符串内容测试一下 //用字符数组来保存的字符串是保存栈里的,保存栈里面东西是可读可写,所有可以修改字符串中的的字符 //使用字符指针来保存字符串,它保存的是字符串常量地址,常量区是只读的,所以我们不可以修改字符串中的字符//但是我实际测试两种都能改 char *str wj; *(str2) y; // 可以char string[] wj; string[1] y; // 可以printf(%s,string)// 错误的原因是:str是一个野指针,他并没有指向某一块内存空间 // 所以不允许这样写如果给str分配内存空间是可以这样用 char *str; scanf(%s, str);函数指针 函数在内存中也占据部分存储空间也是有起始地址的因此可以用指针指向一个函数 格式返回值类型 (*指针变量名)(形参1, 形参2, …); int sum(int a,int b) {return ab; } int main() {int (*p)(int,int);psum;printf(%d,(*p)(1,2)); //测试p(1,2)也是可以的 }有一些格式注意点函数名称使用小括号括起来并在前面加上* 应用在一些调用函数或者将函数作为参数在函数间传递这里的*当作是一种表示符号 结构体 和数组一样是构造类型结构体是相当用来保存一组不同数据类型的数组 定义结构体类型制定好要存储的类型 struct 结构体名{类型名1 成员名1;类型名2 成员名2;……类型名n 成员名n;};定义结构体变量 格式: struct 结构体名 结构体变量名 //先定义结构体类型再定义变量 struct Student {char *name;int age;}; struct Student stu;//定义结构体类型的同时定义结构体变量 struct Student {char *name;int age; } stu;//匿名结构体定义定义变量只能定义这一次无法复用 struct {char *name;int age; } stu;结构体成员的访问 格式结构体变量名.成员名 struct Student {char *name;int age;};struct Student stu;// 访问stu的age成员stu.age 27;printf(age %d, stu.age);结构体成员的初始化 //定义按顺序初始化 struct Student {char *name;int age;}; struct Student stu {“wj, 27};//定义的同时调出内部成员初始化 struct Student stu {.age 35, .name “wj};//定义后再逐个初始化 stu.name wj; stu.age 35;//定义后一次性初始化 stu2 (struct Student){wj, 35};结构体数组 数组的元素全是结构体 格式struct 结构体类型名称 数组名称[元素个数] struct Student {char *name;int age; }; struct Student stu[2]; 初始化 //定义的同时初始化 struct Student {char *name;int age; }; struct Student stu[2] {{jhj, 35},{wj, 18}}; //先定义之后再初始化 struct Student {char *name;int age; }; struct Student stu[2]; stu[0] {wj, 35}; stu[1] {jhj, 18};结构体指针 格式: struct 结构名 *结构指针变量名 // 定义一个结构体类型struct Student {char *name;int age;};// 定义一个结构体变量struct Student stu {wj, 18};// 定义一个指向结构体的指针变量struct Student *p; // 指向结构体变量stup stu;// 可以用3种方式访问结构体的成员// 方式1结构体变量名.成员名printf(name%s, age %d \n, stu.name, stu.age);// 方式2(*指针变量名).成员名printf(name%s, age %d \n, (*p).name, (*p).age);// 方式3指针变量名-成员名printf(name%s, age %d \n, p-name, p-age);(*结构指针变量).成员名 (括号不能省略成员符“.”的优先级高于“ * ”) 结构指针变量-成员名(常用) 结构体内存分配 给结构体变量和普通的开辟空间一样会从内存地址大的位置开始开辟 结构体成员则从占用内存地址小的开始 内存对齐是占用内存最大成员的整数倍 按照最大成员进行申请然后按顺序分配内存 struct Person{int age; // 4char ch; // 1double score; // 8};struct Person p;printf(sizeof %i\n, sizeof(p)); // 16 //按照最大的double类型的8字节分配先第一个8字节给int4给char1之后还剩3不够了再申请8字节给doublestruct Person{int age; // 4double score; // 8char ch; // 1};struct Person p;printf(sizeof %i\n, sizeof(p)); // 24 //同理申请8字节给 int 4之后还剩4不够给double再申请8给它之后再申请8给char 1个字节。 结构体嵌套 struct Date{int month;int day;int year; } struct stu{int num;char *name;char sex;struct Date birthday;Float score; }注意不能嵌套自己类型的变量但可以嵌套指向自己这种类型的指针 struct office{int chair;int computer;struct office *of1; } ; //感觉就像变成了链表//结构体嵌套访问stu.birthday.year 1986; //结构体变量名.结构体变量名.结构体成员结构体变量之间的操作作为函数参数等都是值传递当然不是指作为全局变量直接操作 int main() {struct Person p1 {lnj, 35};printf(p1.name %s\n, p1.name); // lnjtest(p1);printf(p1.name %s\n, p1.name); // lnjreturn 0; } void test(struct Person per){per.name zs; }共用体 共用体每一个成员都共用一块存储空间共用体在使用之前必须先定义共用体类型, 再定义共用体变量 定义类型 union 共用体名{数据类型 属性名称;数据类型 属性名称;... .... };定义变量 union 共用体名 共用体变量名称;由于所有属性共享同一块内存空间, 所以只要其中一个属性发生了改变, 其它的属性都会受到影响 所以当里面的成员ch‘a’的时候age的值就是97ASIIC union Test{int age;char ch;};union Test t;printf(sizeof(p) %i\n, sizeof(t)); //按int来是4t.age 33;printf(t.age %i\n, t.age); // 33t.ch a;printf(t.ch %c\n, t.ch); // aprintf(t.age %i\n, t.age); // 971通信中的数据包会用到共用体因为不知道对方会发送什么样的数据包过来用共用体的话就简单了定义几种格式的包收到包之后就可以根据包的格式取出数据。 2节约内存。如果有2个很长的数据结构但不会同时使用比如一个表示老师一个表示学生要统计老师和学生的情况用结构体就比较浪费内存这时就可以考虑用共用体来设计。 3某些应用需要大量的临时变量这些变量类型不同而且会随时更换。而你的堆栈空间有限不能同时分配那么多临时变量。这时可以使用共用体让这些变量共享同一个内存空间这些临时变量不用长期保存用完即丢和寄存器差不多不用维护。 枚举 有些变量的取值只能再一个范围内在“枚举”类型的定义中列举出所有可能的取值, 被说明为该“枚举”类型的变量取值不能超过定义的范围。 enum 枚举名 {枚举元素1,枚举元素2,…… };// 表示一年四季 enum Season {Spring,Summer,Autumn,Winter }; 定义方式和结构体类似 //先定义类型再定义变量 enum Season {Spring,Summer,Autumn,Winter }; enum Season s;//定义类型的同时定义变量 enum Season {Spring,Summer,Autumn,Winter } s;//定义类型省略类型名直接定义变量 enum {Spring,Summer,Autumn,Winter } s; 使用方式C语言编译器会将枚举元素(spring、summer等)作为整型常量处理称为枚举常量。 枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下第一个枚举元素的值为0第二个为1依次顺序加1。 也可以在定义枚举类型时改变枚举元素的值 enum Season {Spring, //0Summer, //1Autumn, //2Winter //3 } s; s Spring; // 等价于 s 0; s 3; // 等价于 s winter; printf(%d, s); //3enum Season { //当然也可以自己把全部都重新赋值如果没有手动赋值就在前面一个赋值的量自加Spring 9,Summer 5,Autumn, //6Winter //7 }; // 也就是说spring的值为9summer的值为10autumn的值为11winter的值为12其他杂乱知识点补充 局部变量的存储位置再内存的堆栈中 全局变量存储再静态存储区中 auto和register都用来修饰局部变量 auto int num; // 等价于 int num;默认都是auto用完销毁随用随开 register int num; //内存中变量提升到CPU寄存器中存储, 这样访问速度会更快实际可能编译器自动优化为autostatic除了常用静态变量还可以用来修饰全局变量 static int num; // 这样num将不能在其他文件中共享嵌入式C有时常跨到main.c文件访问其他文件的全局变量 //其他文件中也可以定义num了但两个是不一样的不共享的相对的extern常用的就是告诉编译器这个变量在其他文件中定义的 extern int num; static修饰函数也是类似的作用在定义和声明的时候都要加只能在本文件中使用 static int sum(int num1,int num2); //声明static int sum(int num1,int num2) //定义 {return num1 num2; }extern修饰函数就是可以在外部文件使用但是一般默认都是这样的省略了extern 预处理指令 在对源程序进行编译之前会先对一些特殊的预处理指令作解释#include这个东西 结尾不需要加分号 可以出现在程序的任何位置作用范围是出现的位置到文件尾通常功能是宏定义、文件包含、条件编译 宏定义 普通宏定义 格式#define 标识符 字符串 以“#”开头的为预处理命令“define”为宏定义命令“标识符”为所定义的宏名“字符串”可以是常数、表达式、格式串等。 宏名一般用大写字母以便与变量名区别开来但用小写也没有语法错误。 对程序中用双引号扩起来的字符串内的字符不会进行宏的替换操作 #define R 10 char *s Radio; // 在第1行定义了一个叫R的宏但是第4行中Radio里面的R并不会被替换成10可以用#undef终止宏定义的作用域 #define PI 3.14 .............. #undef PI定义一个宏时可以引用已经定义的宏名 #define R 3.0 #define PI 3.14 #define L 2*PI*R //但是要注意你这里最好要加括号否则后续程序中替换后可能出现运算符优先级的问题用宏定义表示数据类型,使书写方便 #define String char * String str This is a string!;带参宏定义 有点像函数的用法 格式#define 宏名(形参表) 字符串 // 第1行中定义了一个带有2个参数的宏average #define average(a, b) (ab)/2 // 会被替换成int a (10 4)/2; int a average(10, 4);宏名和参数列表之间不能有空格否则空格后面的所有字符串都作为替换的字符串. #define average (a, b) (ab)/2 int a average(10, 4); //注意第1行的宏定义宏名average跟(a, b)之间是有空格的于是 //int a (a, b) (ab)/2(10, 4); //这个肯定是编译不通过的一般宏定义要用括号括住不管是整体计算结果还是参数部分否则 // 下面定义一个宏D(a)作用是返回a的2倍数值 #define D(a) 2*a // 如果定义宏的时候不用小括号括住参数// 将被替换成int b 2*34;输出结果10如果定义宏的时候用小括号括住参数把上面的第3行改成#define D(a) 2*(a)注意右边的a是有括号的第7行将被替换成int b 2*(34);输出结果14 int b D(34);//最好写成这样 #define D(a) (2*(a))//否则 D(1)/D(2) 就变成2*1/2*2而不是2*1/2*2条件编译 程序的其中一部分代码只有在满足一定条件时才进行编译否则不参与编译(只有参与编译的代码最终才能被执行)这就是条件编译。嵌入式常把调试用的printf啥的设置个条件编译当然也有运行不同方案时采用这个方法。 使用if else这样也能实现但是结构不清晰而且全部都会编译也不利于阅读维护。 #if 常量表达式..code1... #else..code2... #endif//例子 #define SCORE 67 #if SCORE 90printf(优秀\n); #elseprintf(不及格\n); #endif#define SCORE 67 #if SCORE 90printf(优秀\n); #elif SCORE 60printf(良好\n); #elseprintf(不及格\n); #endif#define SCORE 1 #if SCORE printf(优秀\n); #elseprintf(不及格\n); #endiftypedef关键字 用户自己定义类型说明符 格式: typedef 原类型名 新类型名; typedef int INTEGER INTEGER a; // 等价于 int a;//在别名的基础上再起别名 typedef int Integer; typedef Integer MyInteger;用typedef定义数组、指针、结构等类型将带来很大的方便 typedef char NAME[20]; // 表示NAME是字符数组类型,数组长度为20。然后可用NAME 说明变量, NAME a; // 等价于 char a[20];用来应用在结构体上也能让代码更简洁可读性增强 //第一种 struct Person{int age;char *name; }; typedef struct Person PersonType;//第二种 typedef struct Person{int age;char *name; } PersonType;//第三种 typedef struct {int age;char *name; } PersonType; 应用在枚举 //第一种 enum Sex{SexMan,SexWoman,SexOther }; typedef enum Sex SexType;//第二种 typedef enum Sex{SexMan,SexWoman,SexOther } SexType;//第三种 typedef enum{SexMan,SexWoman,SexOther } SexType; 应用在指针 指向结构体的指针 // 定义一个结构体并起别名 typedef struct {float x;float y; } Point; // 起别名 typedef Point *PP;指向函数的指针 // 定义一个sum函数计算a跟b的和int sum(int a, int b) {...........................}typedef int (*MySum)(int, int); // 定义一个指向sum函数的指针变量pMySum p sum;宏定义在编译预处理阶段进行比函数具有更高的执行效率。 typedef是在编译时处理的相比宏定义不是作简单的代换而是对类型说明符重新命名 typedef char *String1; // 给char *起了个别名String1 #define String2 char * // 定义了宏String2 // 由于String1就是char *所以上面的两行代码等于:char *str1;char *str2; String1 str1, str2; // 宏定义只是简单替换, 所以相当于char *str3, str4;*号只对最近的一个有效, 所以相当于 char *str3; char str4; String2 str3, str4;const关键字 使用const修饰变量则可以让变量的值不能改变 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表 中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。 const int Max100; int const Max100;void f(const int i) { i10;//error! }// const对于基本数据类型, 无论写在左边还是右边, 变量中的值不能改变 const int a 5; // a 666; // 直接修改会报错 // 偷梁换柱, 利用指针指向变量 int *p; p a; // 利用指针间接修改变量中的值所以要注意这些指针的问题 *p 10;//修饰指针 // 先看“*”的位置 // 如果const 在 *的左侧 表示值不能修改,但是指向可以改。 // 如果const 在 *的右侧 表示指向不能改,但是值可以改 // 如果在“*”的两侧都有const 标识指向和值都不能改。 const int *A; //const修饰指针,A指向的地址可以变*A不能变 int const *A; //A指向的地址可以变*A不能变 int *const A; //const修饰指针A, A指向的地址不可以变*A能变 const int *const A;//A指向的地址不可以变*A不能变内存管理 栈内存存放的是任意类型的变量随用随开用完即销栈的最大尺寸固定超出则引起栈溢出 int ages[10240*10240]; // 程序会崩溃, 栈溢出堆内存可以存放任意类型的数据但需要自己申请与释放堆大小想像中的无穷大但实际使用中受限于实际内存的大小和内存是否连续性。 int *p (int *)malloc(10240 * 1024); // 不一定会崩溃 10240*1024是申请多大的字节 int *p1 malloc(4); //申请4字节的大小malloc #include stdlib.h//malloc,第一个参数: 需要申请多少个字节空间,返回值类型: void * int *p (int *)malloc(sizeof(int)); printf(p %i\n, *p); // 保存垃圾数据 //第一个参数: 需要初始化的内存地址,第二个参数: 需要初始化的值,第三个参数: 需要初始化对少个字节 memset(p, 0, sizeof(int)); // 对申请的内存空间进行初始化或者*p0; printf(p %i\n, *p); // 初始化为0free malloc申请的存储空间一定要用free释放他们成对出现 // 1.申请4个字节存储空间 int *p (int *)malloc(sizeof(int)); // 2.初始化4个字节存储空间为0 memset(p, 0, sizeof(int)); // 3.释放申请的存储空间 free(p);calloc /* // 1.申请3块4个字节存储空间 int *p (int *)malloc(sizeof(int) * 3); // 2.使用申请好的3块存储空间 p[0] 1; p[1] 3; p[2] 5; printf(p[0] %i\n, p[0]); printf(p[1] %i\n, p[1]); printf(p[2] %i\n, p[2]); // 3.释放空间 free(p); */// 1.申请3块4个字节存储空间 int *p calloc(3, sizeof(int)); // 2.使用申请好的3块存储空间 p[0] 1; p[1] 3; p[2] 5; printf(p[0] %i\n, p[0]); //对这个[x]不太理解这不是数组的格式吗 printf(p[1] %i\n, p[1]); printf(p[2] %i\n, p[2]); // 3.释放空间 free(p);realloc 若参数ptrNULL则该函数等同于 malloc 返回的指针可能与 ptr 的值相同也有可能不同。若相同则说明在原空间后面申请否则则可能后续空间不足重新申请的新的连续空间原数据拷贝到新空间 原有空间自动释放 // 1.申请4个字节存储空间 int *p malloc(sizeof(int)); printf(p %p\n, p); // 如果能在传入存储空间地址后面扩容, 返回传入存储空间地址 // 如果不能在传入存储空间地址后面扩容, 返回一个新的存储空间地址 p realloc(p, sizeof(int) * 2); *p 666; // 3.释放空间 free(p);链表 可以创建静态链表就是所有节点提前初始化但是这样没有什么意义 // 1.定义链表节点 typedef struct node{int data;struct node *next; //结构体种有指向自己的指针 }Node;动态链表就需要对新创建的节点动态插入 目前我还没有如何在嵌入式里面应用链表所以之后如果学会怎么用再补充记录。 文件操作记录的比较潦草 文件操作虽然在嵌入式里面用不到但是在QT上位机中常常用到虽然函数名不太一样可以类比一下 以 ASCII 码格式存放一个字节存放一个字符。文本文件的每一个字节存放一个 ASCII 码代表一个字符。这便于对字符的逐个处理但占用存储空间较多而且要花费时间转换。(.c) 以补码格式存放。二进制文件是把数据以二进制数的格式存放在文件中的其占用存储空间较少。数据按其内存中的存储形式原样存放.exe //以文本形式存储会将每个字符先转换为对应的ASCII,然后再将ASCII码的二进制存储到计算机中 int num 666; FILE *fa fopen(ascii.txt, w); fprintf(fa, %d, num); fclose(fa);//以二进制形式存储会将666的二进制直接存储到文件中 FILE *fb fopen(bin.txt, w); fwrite(num, 4, 1, fb); fclose(fb);通常文本打开的时候默认会按照ASCII码逐个直接解码文件ASIIC码格式会正常显示二进制文件会乱码需要利用一些软件打开转换 文件打开关闭的FILE结构体 struct _iobuf {char *_ptr; //文件输入的下一个位置int _cnt; //当前缓冲区的相对位置char *_base; //文件的起始位置)int _flag; //文件标志int _file; //文件的有效性验证int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取int _bufsiz; // 缓冲区大小char *_tmpfname; //临时文件名};typedef struct _iobuf FILE;常用函数 #include stdlib.h//以 mode 的方式打开一个 filename 命名的文件返回一个指向该文件缓冲的 FILE 结构体指针。 //char*filaname :要打开或是创建文件的路径。char*mode :打开文件的方式。 FILE * fopen ( const char * filename, const char * mode ); Windows如果读写的是二进制文件则还要加 b,比如 rb, rb 等。 unix/linux 不区分文本和二进制文件 //fclose()用来关闭先前 fopen()打开的文件. //int 成功返回 0 失败返回 EOF(-1)。 int fclose ( FILE * stream );//将 ch 字符写入文件。FILE* stream :指向文件缓冲的指针。 int fputc (int ch, FILE * stream );//从文件流中读取一个字符并返回。FILE* stream :指向文件缓冲的指针。 int fgetc ( FILE * stream );//判断文件是否读到文件结尾,FILE* stream :指向文件缓冲的指针。 int feof( FILE * stream );windows 平台在写入’\n’是会体现为’\r\n’linux 平台在写入’\n’时会体现为’\n’。windows 平台在读入’\r\n’时体现为一个字符’\n’linux 平台在读入’\n’时体现为一个字符’\n’ linux 读 windows 中的换行则会多读一个字符windows 读 linux 中的换行则没有问题 //写入一行 //把 str 指向的字符串写入 fp 指向的文件中。char * str : 表示指向的字符串的指针。char * str : 表示指向的字符串的指针。 int fputs(char *str,FILE *fp)//读取一行 //从 fp 所指向的文件中至多读 length-1 个字符送入字符数组 str 中 如果在读入 length-1 个字符结束前遇\n 或 EOF读入即结束字符串读入后在最后加一个‘\0’字符。 //char * str :指向需要读入数据的缓冲区。int length :每一次读数字符的字数。FILE* fp :文件流指针。 char *fgets(char *str,int length,FILE *fp)遇到\n自动结束读取到EOF自动结束 //写入一块数据 //把buffer 指向的数据写入fp 指向的文件中 //char * buffer : 指向要写入数据存储区的首地址的指针char * buffer : 指向要写入数据存储区的首地址的指针int count : 要写的字段的个数int count : 要写的字段的个数 int fwrite(void *buffer, int num_bytes, int count, FILE *fp)//读取一块数据 //把fp 指向的文件中的数据读到 buffer 中。 //char * buffer : 指向要读入数据存储区的首地址的指针int num_bytes: 每个要读的字段的字节数countint num_bytes: 每个要读的字段的字节数countint num_bytes: 每个要读的字段的字节数count int fread(void *buffer, int num_bytes, int count, FILE *fp)还有读写结构体啥的如果用到这部分再把参考文章里面的东西详细学一下。 STM32 常用数据类型
http://www.zqtcl.cn/news/742603/

相关文章:

  • 万网空间最多放几个网站好的网站首页的特点
  • .net做网站安全吗wordpress取消邮件验证
  • 沈阳做网站推广唐山网站怎么做seo
  • 网站备案说主体已注销刷关键词指数
  • 学做网站教学百度网盘动软代码生成器 做网站
  • 长辛店网站建设手机评测网站
  • 网站建设公司选哪个好软件开发
  • 隐形眼镜网站开发的经济效益莘县网站开发
  • 开创集团网站建设如何在学校网站上做链接
  • 上海优秀网站设计百度投诉中心人工电话号码
  • 卖建材的网站有哪些跨境电商工具类产品的网站
  • 做毕业网站的周记网站开发项目书
  • 门户网站价格仿站工具下载后咋做网站
  • 国外优秀ui设计网站常州网站建设电话
  • 大连手机网站建设做外贸无网站如何做
  • 做旅游门票网站需要什么材料人工智能培训机构哪个好
  • 免费的网站程序个人网站可以做论坛么
  • ps中网站页面做多大的wordpress cdn 阿里
  • 深圳整站创意设计方法有哪些
  • 浙江做网站多少钱江门市网站开发
  • 保定建站价格dw软件免费安装
  • 在建设部网站上的举报凡科网怎么建网站
  • wordpress做小说网站工作期间员工花钱做的网站
  • 婚介网站方案小说网站架构
  • 英文在线购物网站建设湖北建设厅举报网站
  • 漯河网络推广哪家好宁波网站seo公司
  • 网站设计ppt案例做物流用哪个网站好
  • 做网站官网需多少钱天元建设集团有限公司财务分析
  • 一般网站建设用什么语言网络规划设计师历年考点
  • 做网站卖菜刀需要什么手续江苏网站优化