C语言数据类型、运算符,控制流

一、函数注意事项

标准输入函数:

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、整型:

  1. 规定short int 占用字节数不大于int,long int 占用字节数不小于int,但是没有规定具体一定要多少字节,这依靠于你的操作系统是多少位的。在64位中,int 4个字节,short 2个字节,long 和long long都是8个字节。

  2. 负数在计算机中以补码的形式存储,原码取反+1为补码。负数的反码:符号位不变,其他位取反

  3. 原码=>补码:原码取反加一;补码=>原码:补码减一取反。

  4. 八进制输出用%#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
++自加1a++, ++b
自减1a–, --b
  • 关注点:

    • 减号也是负号,比如 -a 是取变量 a 的相反数。

    • 取模运算要求左右两边操作数必须是整型数据。

    • 自加和自减运算不仅可以对整型操作,也可以对浮点数、指针操作

  • 前后缀运算:

    1. 前缀自加自减运算:先进行自加自减,再参与表达式运算

    2. 后缀自加自减运算:先参与表达式运算,在进行自加自减

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

    • 移位运算:移出去的不要,空出来的补零。移位运算都是针对无符号数的运算。(有符号位的右移运算:移出去的丢掉,补原来的符号位)

左移运算

image-20240311175752623

右移运算

有符号类型数据按位取反
 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效率高
  • 当左右两边有相同的操作数时,采用复合赋值符不仅直观,且能提高运算效率

3、
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值