一、函数注意事项
标准输入函数:
1、scanf函数:格式输入函数
只能输入一个单词,意思是中间不能有空格,若输入空格则默认输入结束
注意1:
-
scanf() 与 printf() 不同,scanf() 的格式控制串不可乱写,尤其是结尾处的 \n
-
用户必须完全按照 scanf() 中描述的输入格式控制串来输入数据,否则将出错。
错误示例:
// 此处输入时必须带逗号 scanf("%d,%d", &a, &b); // 此处必须先输入a=,然后才能输入整数 scanf("a=%d", &a); // 此处结束输入时按下的回车符将被scanf()误以为格式控制符,无法正常结束输入 scanf("%d\n", &a); //此处输入两个整型数据,用空格隔开 scanf("%d%d",&a,&b);
第三种情况原因:在C语言中,scanf
函数用于从标准输入流(通常是键盘)读取输入。格式控制符(format specifier)用于指定输入的数据类型,比如 %d
表示读取一个整数。
scanf
函数的格式控制符为 %d\n
,它表示读取一个整数,并且期望输入的整数后面跟着一个换行符 \n
。然而,这里的问题在于,scanf
函数会跳过输入中的空白字符,包括空格、制表符和换行符。因此,当你输入一个整数后按下回车键时,回车符会被scanf
函数误以为是格式控制符中的换行符,而不是作为输入的一部分。
这样可能导致程序出现问题,因为scanf
函数会等待下一个非空白字符的输入,而不是立即读取回车符。这可能会导致程序在输入时出现意外的行为或结果。
简而言之,这样子导致scanf期望输入的整数后面跟着一个换行符 \n
,但是换行符始终读不进去,导致异常退出
注意2:
-
scanf() 的返回值,代表成功从键盘读取的数据的个数
-
无法匹配 scanf() 指定格式的数据,将被遗留在输入缓冲区中,不会消失
-
-
示例:
-
// scanf() 试图从键盘读取两个整数 // 返回值 n 代表成功读取的个数,比如: // 输入100 200,则 n 将等于2 // 输入100 abc,则 n 将等于1 // 输入abc xyz,则 n 将等于0;输入abc 200,n也将等于0 int n = scanf("%d%d", &a, &b); // 根据 scanf() 的返回值,判断用户是否输入了正确的格式 while(n != 2) { // 需要清空缓冲区并提示用户重新输入 char s[50]; fgets(s, 50, stdin);//读取输入缓冲区中的剩余字符,直到遇到换行符为止 printf("请重新输入两个整数\n"); n = scanf("%d%d", &a, &b); }
scanf(); // 格式化输入函数 fgets(); // 字符串输入函数 getchar() ; // 从标准输入中获取一个字符
2、getchar()函数
是C语言中的标准库函数,用于从标准输入流(通常是键盘)中获取一个字符。
函数原型:
int getchar(void);
返回值:如果成功读取字符,则返回该字符的ASCII码值(0到255之间的整数)。 如果到达文件结尾(EOF),则返回常量EOF(通常为-1)。
功能: getchar()函数从标准输入流中读取一个字符,并返回其ASCII码值。 如果成功读取字符,则输入流的指针会向前移动一个位置。 getchar()函数通常与循环结构一起使用,用于逐个读取输入的字符。
示例:
int c; while ((c = getchar()) != EOF) { // 处理读取到的字符 printf("读取到的字符为: %c\n", c); }
在上面的示例中,getchar()函数被用于循环读取输入的字符,直到遇到文件结尾(EOF)为止。
清空缓冲区:
1、fgets(s, 50, stdin);
会读取输入缓冲区中的剩余字符,直到遇到换行符为止。
2、while( getchar() != '\n' );
测试:
编程实现如下功能: ·如果用户输入大小写字母,则输出字母对应的ASCII码值。
·如果用户输入ASCII码值,则输出对应的大小写字母
失败示例: char c; int a; int l=scanf("%c",&c); printf("返回值:%d\n",l); if(l != 1){ scanf("%d",&a); printf("获取的数字对应的字母:%c\n",a); }else{ printf("获取的字母:%c\n",c); printf("获取的字母对应的数字:%d\n",c); } return 0;
运行结果:
因为输入的数字97,用%c读取,会认为是字符‘9’‘7’,而只读取一个%c,则只读取了‘9’。
char c,e1,e2,d; printf("请输入字母或字母对应的数字:\n"); scanf("%c",&c); printf("输入的:%d+%c\n", c, c); if ((97 <= c && c <= 122) || (65 <= c && c <= 90)){ • printf("输入的字母对应的ascii码:%d\n",c); }else{ • int i = 0; • d = getchar(); • while (d!='\n') • { • i++; • if (i==1){ • e1 = d; • }else if (i==2){ • e2 = d; • }else{ • break; • } • d = getchar(); • } • if (i==1){ • printf("获取的数字对应的字母:%c\n",(c-48)*10+(e1-48)); • }else if(i==2){ • printf("获取的数字对应的字母:%c\n",(c-48)*100+(e1-48)*10+(e2-48)); • } } return 0;
// if (97 <= c <= 122 || 65 <= c <= 90) //这样子的关系表达式是与预期效果不符的,因为该关系表达式会被执行为:(97<=c)<=122,而关系表达式(97<=c)的计算结果为布尔值,即0或1.那么最后要么变成(0<=122),要么变成(1<=122),两个关系表达式结果都为真。所以这个判断执行结果一定是真,没有判断作用。
2、fgets()函数
fgets(s,10,stdin); //从键盘(stdin)读取一行字符串,可以包括空格,在该示例中,由于‘\0’存在,该示例只能读取9个数。
二、数据类型
1、整型:
-
规定short int 占用字节数不大于int,long int 占用字节数不小于int,但是没有规定具体一定要多少字节,这依靠于你的操作系统是多少位的。在64位中,int 4个字节,short 2个字节,long 和long long都是8个字节。
-
负数在计算机中以补码的形式存储,原码取反+1为补码。负数的反码:符号位不变,其他位取反
-
原码=>补码:原码取反加一;补码=>原码:补码减一取反。
-
八进制输出用%#o,十六进制输出用%#x。
tips:
宏:INT_MAX在头文件limits.h中
2、浮点型:
64位操作系统下:
输出格式%f(float单精度),%lf(double双精度),%Lf(long double长双精度),
默认输出格式为小数点后6位,要四舍五入的方式保留两位小数输出则用%.2f格式。
3、字符型
char,字符型,本质是单字节整型,支持整型所有的运算
char ch=‘a’和char ch=97在内存中是一模一样的
要打印a则用%c格式,打印97则用%d格式
4、字符串(C语言没有字符串类型,但是时刻离不开字符串)
ch a[]="abcd";
字符串在内存中默认会加上结束标记: \0,所以以上示例中,四个字符加一个结束符,共占用5个字节空间。定义字符串时,若有定义字节数,则需要定义的字节数为:要存储的数据的最大位数+1。
5、布尔型
布尔类型需要引入stdbool.h库函数
非零即真
6、类型转换
-
概念:不一致但相互兼容的数据类型,在同一表达式中将会发生类型转换。
-
转换模式:
-
隐式转换:系统按照隐式规则自动进行的转换
-
强制转换:用户显式自定义进行的转换
-
-
隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度
隐式转换示例代码
char a = 'a'; int b = 12; float c = 3.14; float x = a + b - c; // 在该表达式中将发生隐式转换,所有操作数被提升为float
-
强制转换:用户强行将某类型的数据转换为另一种类型,此过程可能丢失精度
char a = 'a'; int b = 12; float c = 3.14; float x = a + b - (int)c; // 在该表达式中a隐式自动转换为int,c被强制转为int
不管是隐式转换,还是强制转换,变换的都是操作数在运算过程中的类型,是临时的,操作数本身的类型不会改变,也无法改变。
注意:
在同一表达式中如果出现了无符号整型与有符号整型,那么系统会默认转换为无符号整型,因此对于负数来说被转换为无符号后他的值是非常大的(因为最高位如果为一的话该值必定大于21亿多...)
int Num1 = 123 ; unsigned Num2 = -1 ; if (Num1 > Num2) { • printf("%d>%d\n" , Num1 , Num2); } else{ • printf("%d>%d\n" , Num2 , Num1); }
数据类型的本质
-
概念:各种不同的数据类型,本质上是用户与系统对某一块内存数据的解释方式的约定。
-
-
17620828909
-
-
推论:
-
-
类型转换,实际上是对先前定义时候的约定,做了一个临时的打破。
-
理论上,可以对任意的数据做任意的类型转换,但转换之后的数据解释不一定有意义。
-
-
##
三、运算符
1. 算术运算符
运算符 | 功能说明 | 举例 |
---|---|---|
+ | 加法,一目取正 | a+b |
- | 减法,一目取负 | a-b |
* | 乘法 | a*b |
/ | 除法 | a/b |
% | 取模(求余) | a%b |
++ | 自加1 | a++, ++b |
– | 自减1 | a–, --b |
-
关注点:
-
减号也是负号,比如 -a 是取变量 a 的相反数。
-
取模运算要求左右两边操作数必须是整型数据。
-
自加和自减运算不仅可以对整型操作,也可以对浮点数、指针操作。
-
-
前后缀运算:
-
前缀自加自减运算:先进行自加自减,再参与表达式运算
-
后缀自加自减运算:先参与表达式运算,在进行自加自减
-
int a = 100; int b = 200; int c = ++a; // a先自加1,变成101;然后再赋值给c,因此c等于101。则此时a=101,c=101。 int d = b++; // b先赋值给d,因此d等于200;然后b自加1,变成201。则此时b=201,d=200。
取余运算可以用来控制整数的变化范围
eg:
for(int i=0;i<100;i++){ printf("%d",i%10);//范围取到0-9 printf("%d",i%11);//范围取到0-11 printf("%d",10+i%11);//范围取到10-20 }
. 关系运算符
运算符 | 功能 | 举例 | 说明 |
---|---|---|---|
> | 大于 | a > b | 判断a是否大于b |
>= | 大于或等于 | a >= 5 | 判断a是否大于或等于5 |
< | 小于 | 3 < x | 判断3是否小于x |
<= | 小于或等于 | x <= (y+1) | 判断x是否小于或等于y+1 |
== | 等于 | (x+1) == 0 | 判断x+1是否等于0 |
!= | 不等于 | c != ‘\0’ | 判断c是否不等于’\0’ |
-
关注点:
-
关系运算符用于判断运算符两边的表达式是否满足给定的大小条件。
-
由关系运算符组成的表达式称为关系表达式,其值为布尔型。
-
判断是否相等是双等号==,而不是一个等号。
-
3. 逻辑运算符
运算符 | 功能说明 | 举例 |
---|---|---|
! | 逻辑反 | !(x==0) |
&& | 逻辑与 | x>0 && x<10 |
|| | 逻辑或 | y<10 || x>10 |
-
运算规则:
-
逻辑反:将逻辑真、假翻转,即真变假,假变真。
-
逻辑与:将两个关系表达式串联起来,当且仅当左右两个表达式都为真时,结果为真。
-
逻辑或:将两个关系表达式并联起来,当且仅当左右两个表达式都为假时,结果为假。
-
-
特殊规则:
-
在逻辑与运算中,如果左边表达式的值为假,那么右边表达式将不被执行。
-
在逻辑或运算中,如果左边表达式的值为真,那么右边表达式将不被执行。
-
4. 位运算符
运算符 | 名称 | 举例 | 功能说明 |
---|---|---|---|
~ | 位逻辑反 | ~a | 将变量 a 中的每一位取反 |
& | 位逻辑与 | a & b | 将变量 a 和 b 逐位进行与操作 |
| | 位逻辑或 | a | b | 将变量 a 和 b 逐位进行或操作 |
^ | 位逻辑异或 | a ^ b | 将变量 a 和 b 逐位进行异或操作 |
<< | 左移 | a << 4 | 将变量 a 中的每一位向左移动4位 |
>> | 右移 | x >> n | 将变量 x 中的每一位向右移动4位 |
-
位运算符操作的对象是数据中的每一位
-
运算规则:
-
位逻辑反、位逻辑与、位逻辑或拥有与逻辑运算相似的规则和一样的真值表
-
异或运算:相同为0,不同为1
-
移位运算:移出去的不要,空出来的补零。移位运算都是针对无符号数的运算。(有符号位的右移运算:移出去的丢掉,补原来的符号位)
-
左移运算
右移运算
有符号类型数据按位取反
char w=3;printf("%d",~w); 结果:-4
在C语言中,char 类型是有符号的,因此在进行按位取反操作时,会将结果表示为补码形式。当打印带符号整数时,计算机会将补码转换为原码形式进行显示。对于-4这个补码值 1111 1100,计算机会将其转换为原码形式,即 1000 0011,对应的十进制值为-4。这就是为什么打印出来的结果是-4。
(0000 0011取反后1111 1100符号位位1,表示该值为负数,所以在计算机中会将这串1111 1100解释为补码,然后由于打印出来的整型数据,是原码的数值,所以打印出来的是1111 1100减一取反的值)
逻辑与和按位与两者区别
有符号数的左右位移
上图,一般编译器会按照原来是1就补1,原来是9就补0。
位移测试
假设有一个无符号32位整型数据
unsigned int data=0x12ff0045
请编写一个程序用位运算把data的第14、15位修改1,把22、23位修改为0, 并且输出修改后的数据。
unsigned int data = 0x12ff0045; unsigned int data1 = 0x6000; unsigned int data2 = 0xff9fffff; unsigned int data3 = data & data2| data1 ; printf("%#x", data3);
输出:0x129f6045
注意
按位与和按位或同时出现的计算,要注意优先级问题
此时我们用unsigned int data3 = data & data2| data1 ;是对的 但是如果用unsigned int data3 = data | data1 & data2;是错的,因为按位与的优先级高于按位或,先计算data1&data2会导致结果不是我们需要的数据。错误结果为:0x12ff6045
5. 特殊运算符
-
赋值运算符
-
不能对常量赋值,只能对变量赋值
-
不能对数组赋值
-
可以连续赋值,顺序从右到左
-
int a, b; int x[5]; a = 100; // 对变量 a 赋值,正确 3 = 100; // 对常量 3 赋值,错误! x = 123; // 对数组 b 赋值,错误! // 连续赋值 a = b = 50; // 先将 50 赋给 b,再将 b 的值赋给 a,正确
-
复合赋值符
-
当左右两边有相同的操作数时,采用复合赋值符不仅直观,且能提高运算效率
-
除了下述10个复合运算符之外,生造别的复合运算符是非法的
-
// 加减乘除: a += n; // 等价于 a = a+n; a -= n; // 等价于 a = a-n; a *= n; // 等价于 a = a*n; a /= n; // 等价于 a = a/n; // 求余: a %= n; // 等价于 a = a%n; // 位运算: a &= n; // 等价于 a = a&n; a |= n; // 等价于 a = a|n; a ^= n; // 等价于 a = a^n; a >>= n; // 等价于 a = a>>n; a <<= n; // 等价于 a = a<<n;
6. 条件运算符
-
唯一需要三个操作数的运算符
-
语法:表达式1 ? 表达式2 : 表达式3
-
释义:当表达式1为真时,取表达式2,否则取表达式3
-
举例:
int a = 100; int b = 200; int m = (a>b) ? a : b; // 如果 a>b 为真,则 m 取 a 的值,否则取 b 的值
7. sizeof 运算符
-
含义:计算指定数据类型或者变量所占据内存的字节数
-
语法:sizeof(类型) 、sizeof(变量) ,计算变量的字节数时圆括号可以省略
-
举例:
printf("%d\n", sizeof(int)); printf("%d\n", sizeof(long double)); int a[5]; printf("%d\n", sizeof(a)); printf("%d\n", sizeof a);
sizeof返回值类型
double l1=2.98797; printf("%lf\t,占用字节数:%d\n",l1,sizeof(l1)); 警告:format specifies type 'int' but the argument has type 'unsigned long
答:因为sizeof
函数返回的是size_t
类型,而printf
中%d
是用来打印int
类型的,所以会报错。正确的做法是使用%zu
来打印size_t
类型的数据
8. return运算符
-
含义:退出某个函数(如果退出的是主函数main,那么整个程序也就退出了)
-
语法:必须出现在函数体内,可以带函数对应类型的数据
-
举例:
int main() { return 0; }
9. 优先级与结合性
-
当表达式中出现不同的运算符时,根据优先级来决定谁先执行,比如先乘除,后加减
-
当表达式中出现多个相同优先级的运算符时,根据结合性来决定谁先运行,比如从左到右
四、运算效率问题
1、float数据类型和int数据类型比较:
float类型的运算效率大概是int类型的%20-%30左右(编写操作系统的时候需要考虑)
2、a+=b比a=a+b效率高
-
当左右两边有相同的操作数时,采用复合赋值符不仅直观,且能提高运算效率