👉引言💎
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。 热爱写作,愿意让自己成为更好的人............
铭记于心 | ||
🎉✨🎉我唯一知道的,便是我一无所知🎉✨🎉 |
C 避坑指南
一、基础|基本常识类
- 注意逻辑运算符的短路效应
- else只跟最近的if相匹配
- c语言本身不提供输入输出语句,靠标准函数库实现
- putchar 输出的是字符,,c语言函数内部 不能够嵌套定义 函数
- 存储类型 有 auto ,static , register(寄存器) , extern(外部),全局变量默认为 static
- 未指定存储类别的变量,其隐含的存储类别为 auto
1 运算符类型
- 对于%,需要%%才能转义,而不是\
- 逗号运算符优先级最低,整个逗号表达式的值就是 最右边表达式的值
- char类型对于字符串会取最低位字符
- 这个就是 每个字符占一个字节,int4个字节,被赋值'328'后,每个字节位置则是 0 51 50 56, 换成二进制后再转换为int即为3355192
- 只是逗号表达式返回最右边的值,执行还是从左到右
- a+=a-=a*a;C中为-4 ,java中为0
易知Java中对同一变量连续赋值,JVM虚拟机运行结果与c【-8 7 3】不同,而这种写法在python中是不被允许的
2 占位符|格式化问题
需要注意的是 2 转义字符的使用事项:
- %5.2s 指输出字符串前2位,占5个空间,右对齐 %d 是忽略并丢弃一个整型字符
- #的作用 是输出提示(0, 0x)
- 注意对 char类型赋值时一定看清楚是 数字还是字符,6与'6'是完全不同的
- %d%i只在scanf时有区别,且前者 只匹配十进制,不匹配的都输出0,后者据情况判断(0x十六进制,0八进制)转10进制
不匹配直接搞成0
- m.n中 m是宽度,负号表示左对齐;n是(具体跟 后面的 d(至少输出的位数) , s(输出的位数) ,f(输出的精度)有关)
不满足则 用0补齐,高位补0,想想也确实应该 [其实一般用于浮点型]
- 如果是%5d 的话则输出 [空格]-123 其实这些 都是符合逻辑的,因为 总不能 下面输出0-123吧,这也不合适
- 关于 字符串的输出格式化,字符串位数小于“精度”则输出全部字符串,其它不用管,,大于,则只取“精度”规定的前几个字符
- 对于%d来讲,4代表控制精度为4位,所以高位补0;5代表 设置宽度,所以靠右对齐,共占五个宽度
3 输入输出问题
- sprintf(const char * str .....),后面跟printf用法一样,就多了一个指定输出位置str
- printf与scanf返回值都是由 输出输入字符数量决定
- scanf(读取时的格式符不能带 .n这样是无效的,也就是只能是 %3d(读取三位有效数字), 而不能%3.2d),且输入时如果不加 输入项,则会抛出运行时异常,而printf("dadw%d")没有输出项也不会报异常
- 注意如果是 scanf("%c\n"),则输入一个字符回车会陷入死循环,因为回车(空白字符)不会被scanf读取,但是还没有读取\n,也就是他会一直去“读\n”,除非再输入一个 非空白字符
- 包括 lf,f格式字符都是默认输出 小数点后六位(超过则四舍五入),但%g可以将无效位数去掉,只输出有效位
- getchar( 出错返回-1)
二、错题 | 程序语句类
- 1 对于 while(b- -≥0)一定注意,最后b出来的时候是-2,一定要注意这个问题
int s = (b = ++m * m++, --m + b--) + ++b +b--; //相当于b = ++m * m++; s = ( --m + b--) + ++b +b--; 后置++是此运算式子中都不变,运算结束自加(逗号符可以看做结束运算),比如这个m自+后当时是m,但后来m就是m+1,int s = m + m * (m++) * m;
- 2虽然符合c11标准,但各家编译器对其扩展定义不同,比如dev允许变长数组(变长数组不能在定义时初始化),但vs中不允许
三、进阶 | 指针与函数
scanf(),getchar(),getche(),getch()这些函数缓冲区角度的解释
字符串常量
- 相当于 *p = *p+6
- 不能这样给字符数组赋值,另外,上面哪一行在vs中需要加const修饰(指向字符常量)
- 函数指针
int (*t)(int a, int b); 两种调用方式都是可以的 (*t)(2, 1); t(2,1)
数组名是一个地址,也就是一个指针,但不能说是指针变量(不可变),,指针变量的值是一个地址
*p=*q 解引用当左值时是变量,右值时是常量
二维数组传参时局部变量不能用 二级指针,而用数组形式,因为需要指定 一维容量,即 void test(int nums[ ][5] );,字符数组亦是如此
对于数组无法自增,但可以再用个指针指向该数组进行自增移动
P是一个数组指针,指向空间大小为4的数组
printf()多元素输出的时候,存在入栈(同时进行运算),出栈问题,一定注意
strcpy会连 \0结束符 一起复制过去 而strncpy(char *dest, const char *src, size_t len)并不会责添加‘\0’(在字符串长度大于len的情况下,如果小于则会用NULL(\0)填满)
接收含有空格的字符串直接用 gets(str),输出可以puts(str)【且自动换行】
四、进阶 | 结构体及宏定义
- 结构体长度的计算
union的使用受系统大小端的影响
栈的生长方向与内存存放规则
低地址存低位数据 大端模式,低地址存高位数据
int check_cpu() { union{ short a; char b; }c; c.a=1; return c.b == 1; }小端返回1
enum s------------------------------izes { small=7,medium,large=10,humungous }; 默认情况下,整型值从0开始。如果对列表中的某个标识符进行了赋值,那么紧接其后的那个标识符的值就比所赋的值大1,然后类推
宏
- 宏定义是用宏名代替一个字符串,也就是做简单的置换,不做正确性检查。(预编译时不做任何语法检查,只有在编译已被宏展开后的源程序时才会发现语法错误并报错)
- 宏定义不是C语句,不必在行末加分号,不是c语言本身组成部分。如果加了分号则会连分号一起进行替换
- #definde出现在A处,则宏名的有效范围为定义命令之后到本源文件之后到本源文件结尾。
- 可以通过#undef命令终止宏定义的作用域
- c语言不做const char*修饰以及越界检查,并且如果是cpp,那么检不检查也与编译器有关,比如vs2022检查,dev照样不检查
- memchr()如果找数字0,则会返回任意地址,因为它会认为0是NULL
🌹写在最后💖: 路漫漫其修远兮,吾将上下而求索!伙伴们,再见!🌹🌹🌹