本文是C语言的基础知识,主要讲解输入、输出以及 运算符。
输入输出
输入—处理—输出:这就是程序
输入输出(Input and Output, IO)是用户和程序"交流"的过程。在控制台程序中:
- 输出一般是指将数据(包括数字、字符等)显示在屏幕上。
- 输入一般是指获取用户在键盘上输入的数据。
在C语言程序中,几乎没有一个程序不需要这两个函数,尤其是输出函数(printf)。所以这两个函数必须要掌握。
而如果在程序中要使用printf()或者scanf(),那么就必须要包含头文件stdio.h。因为这两个函数就是包含在该头文件中的。我们在前面讲编程时输入的框架中就有"#include",原因就是"几乎没有一个程序不需要这两个函数"。
输出
在C语言中,有三个函数可以用来在显示器上输出数据,它们分别是:
- puts():是 output string 的缩写,意思是"输出字符串"。只能输出字符串,并且输出结束后会自动换行。
- putchar():函数每次只能输出一个字符,输出多个字符需要调用多次。
- printf():可以输出各种类型的数据。今天咱们就重点了解这个。
printf() 是最灵活、最复杂、最常用的输出函数,完全可以替代puts()和 putchar(),大家一定要掌握。 注意 printf()函数只能在控制台程序中使用,在Windows系统中,有窗口界面的程序无法通过printf()函数在窗口中显示数据。
输出控制符
常用的输出控制符主要有以下几个:
- 1)%d:按十进制整型数据的实际长度输出。
- 2)%ld:输出长整型数据。
- 3)%md: m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出。
- 4)%u:输出无符号整型(unsigned)。输出无符号整型时也可以用%d,这时是将无符号转换成有符号数,然后输出。但编程的时候最好不要这么写,因为这样要进行一次转换,使CPU多做一次无用功。
- 5)%c:用来输出一个字符。
- 6)%f:用来输出实数,包括单精度和双精度,以小数形式输出。不指定字段宽度,由系统自动指定,整数部分全部输出,小数部分输出6位,超过6位的四舍五入。
- 7)%.mf:输出实数时小数点后保留m位,注意m前面有个点。
- 8)%o:以八进制整数形式输出,这个就用得很少了,了解一下就行了。
- 9)%s:用来输出字符串。用%s输出字符串同前面直接输出字符串是一样的。但是此时要先定义字符数组或字符指针存储或指向字符串,这个稍后再讲。
- 10)%x(或%X或%#x或%#X)︰以十六进制形式输出整数,这个很重要。
printf()用法
printf()格式控制符的完整形式如下:
%[flag][width][.precision]type
1)[] 表示此处的内容可有可无,是可以省略的。
2)type 表示输出类型,比如 %d、%f、%c、%lf,type 就分别对应 d、f、c、lf,type 这一项必须有,这意味着输出时必须要知道是什么类型。
3)width 表示输出最小宽度,也就是至少占用几个字符的位置;例如,%-9d中 width 对应9,表示输出结果最少占用9个字符的宽度。当输出结果的宽度不足width时,以空格补齐(如果没有指定对齐方式,默认会在左边补齐空格);当输出结果的宽度超过 width 时,width不再起作用,按照数据本身的宽度来输出。
4)flag 表示标志字符。例如,%-9d中 flag 对应-。
5).precision 表示精度,精度对于实数来说,表示小数位数。%.0f表示保留 0 位小数
下面咱来搞几个例子更好的理解printf()的用法
1、输出一个 4×4 的整数矩阵。
#include <stdio.h> int main() { int a1=2021, a2=666, a3=520, a4=1314; int b1=6666, b2=7777, b3=8888, b4=9999; int c1=2021, c2=07, c3=14, c4=1544; int d1=29, d2=57, d3=33, d4=285747; printf("%-9d %-9d %-9d %-9d\n", a1, a2, a3, a4); printf("%-9d %-9d %-9d %-9d\n", b1, b2, b3, b4); printf("%-9d %-9d %-9d %-9d\n", c1, c2, c3, c4); printf("%-9d %-9d %-9d %-9d\n", d1, d2, d3, d4); return 0; }
运行结果:
2021 666 520 1314 6666 7777 8888 9999 2021 7 14 1544 29 57 33 285747
输出结果说明:
%-9d中,d表示以十进制输出,9表示最少占9个字符的宽度,宽度不足以空格补齐,-表示左对齐。综合起来,%-9d表示以十进制输出,左对齐,宽度最小为9个字符。
2、width 的用法。
#include <stdio.h> int main() { int e1=2021, e2=666, e3=520, e4=1314; int f1=6666666, f2=7777, f3=888888, f4=9999; printf("%-4d %-4d %-4d %-4d\n", e1, e2, e3, e4); printf("%-4d %-4d %-4d %-4d\n", f1, f2, f3, f4); return 0; }
运行结果:
2021 666 520 1314 6666666 7777 888888 9999
输出结果说明:
指定输出宽度为4,6666666和888888的宽度6,超过了 4,所以指定输出宽度不再起作用,而是按照f1和f3的实际宽度输出。
3、%x、%X、%#x、%#X的区别
#include <stdio.h> int main() { int i = 42; printf("%x |%X |%#x |%#X ", i, i, i, i); return 0; }
运行结果:
2a |2A |0x2a |0X2A
输出结果说明:
- 如果是小写的x,输出的字母就是小写的;
- 如果是大写的X,输出的字母就是大写的;
- 如果加一个"#",就以标准的十六进制形式输出。
最好是加一个"#",否则如果输出的十六进制数正好没有字母的话会误认为是一个十进制数呢!总之,不加"#"容易造成误解。但是如果输出0x2a或0x2a,那么人家一看就知道是十六进制。而且%#x和%#X中,笔者觉得大写的比较好,因为大写是绝对标准的十六进制写法。所以在输出十六进制的时候使用"%#X"比较好。
标志字符
flag 是标志字符。例如,%#X中 flag 对应 #,%-9d中 flags 对应-。printf()可以用的 flag有下面几个:
- 1)-:表示左对齐。如果没有,就按照默认的对齐方式,默认一般为右对齐。
- 2)+:用于整数或者小数,表示输出符号(正负号)。如果没有,那么只有负数才会输出符号。
- 3)空格:用于整数或者小数,输出值为正时冠以空格,为负时冠以负号。
- 4)#:
- 对于八进制(%o)和十六进制(%x/%X)整数,#表示在输出时添加前缀;八进制的前缀是0,十六进制的前缀是 0x/0X。
- 对于小数(%f/%e/%g),#表示强迫输出小数点。如果没有小数部分,默认是不输出小数点的,加上 #以后,即使没有小数部分也会带上小数点。
举个栗子熟悉一下标志字符
#include <stdio.h> int main(){ int m = 422, n = -1009; float f = 520.1314; printf("m=%6d, m=%-6d\n", m, m); //演示-的用法 printf("m=%+d, n=%+d\n", m, n); //演示+的用法 printf("m=% d, n=% d\n", m, n); //演示空格的用法 printf("f=%.0f, f=%#.0f\n", f, f); //演示#的用法 return 0; }
运行结果:
m= 422, m=422 m=+422, n=-1009 m= 422, n=-1009 f=520, f=520.
输出结果说明:
- 当以%6d输出 m 时,是右对齐,所以在422前面补3个空格;当以%-6d输出 m 时,是左对齐,所以在422后面补3个空格。
- m 是正数,以%+d输出时要带上正号;n 是负数,以%+d输出时要带上负号。
- m 是正数,以% d输出时要在前面加空格;n 是负数,以% d输出时要在前面加负号。
- %.0f表示保留 0 位小数,也就是只输出整数部分,不输出小数部分。默认情况下,这种输出形式是不带小数点的,但是如果有了#标志,那么就要在整数的后面"硬加上"一个小数点,以和纯整数区分开。
printf()不能立即输出的问题
先看栗子
#include<stdio.h> #include<unistd.h> int main() { printf("帅次真的帅吗:"); sleep(5); //程序暂停5秒钟 printf("上下五千年的帅"); return 0; }
这段代码使用了两个 printf() 语句,它们之间有一个 sleep() 函数,该函数的作用是让程序暂停 5 秒,然后再继续执行。sleep() 是 Linux 和 Mac OS 下特有的函数,不能用于 Windows。当然,Windows 下也有功能相同的暂停函数,叫做 Sleep()。
运行该程序,会发现第一个 printf() 并没有立即输出,而是等待 5 秒以后,和第二个 printf() 一起输出了。
有种情况在第一个 printf() 的最后添加一个换行符,如下所示:
printf("帅次真的帅吗\n");
效果就是编译并运行程序,发现第一个 printf() 首先输出(程序运行后立即输出),等待 5 秒以后,第二个 printf() 才输出。目的一下达到了。
因为我用的是Windows版在线编译,所以两种情况都是等待5秒一起输出。这个你们可以亲测一下。
为什么不能立即输出?
这一切都是输出缓冲区(缓存)在作怪!从本质上讲,printf() 执行结束以后数据并没有直接输出到显示器上,而是放入了缓冲区,直到遇见换行符"\n"才将缓冲区中的数据输出到显示器上。
输出小结
1)转换说明符在某些系统中对大小写敏感,请注意不同系统的要求。如%f不能写成%F。
2)格式控制字符串中,可以有转义字符,如"\n"、"\b"、"\xab"等。
3)格式符以%开头,以格式转换符结束,中间有一些修饰作用的其他字符。
4)输出字符"%"时,可以用连续的两个%。
5)不同系统中实现格式输出时,输出结果的精度可能有差别。
6)printf()函数由于要解析格式控制字符串,比较耗费资源。如果是打印一个字符,可以用putchar()函数;如果仅仅是简单地打印字符串,可以用puts()函数。
7)对于printf()函数的输出表列中的求值顺序,不同的编译系统不一定相同,可以从左到右,也可从右到左。LCC编译系统就是从左到右,而TC是从右到左。
对于初学者的咱们来说,上面讲到的 printf() 用法已经比较复杂了,基本满足了实际开发的需求,相信大家也需要一段时间才能熟悉。受到所学知识的限制,本文也未能讲解 printf() 的所有功能,后续咱们还能不能深入,就看自己的了。
输入
在C语言中,有多个函数可以从键盘获得用户输入:
- scanf():和 printf() 类似,scanf() 可以输入多种类型的数据。
- getchar()、getche()、getch():这三个函数都用于输入单个字符。
- gets():获取一行数据,并作为字符串处理。
scanf()是最灵活、最复杂、最常用的输入函数,但它不能完全取代其他函数。
scanf()函数
scanf 是 scan format 的缩写,意思是格式化扫描,也就是从键盘获得用户输入,和 printf 的功能正好相反。scanf的功能用一句话来概括就是通过键盘给程序中的变量赋值。
下面面咱们先看一个栗子:
#include <stdio.h> int main() { int a = 0, b = 0, c = 0, d = 0; scanf("%d", &a); //输入整数并赋值给变量a scanf("%d", &b); //输入整数并赋值给变量b printf("a+b=%d\n", a+b); //计算a+b的值并输出 scanf("%d %d", &c, &d); //输入两个整数并分别赋值给c、d printf("c*d=%d\n", c*d); //计算c*d的值并输出 return 0; }
输入:
- 输入一个整数按回车连续四次;
- 将四个变量的值一次性输入:27 30 18 10
运行结果:
a+b=57 c*d=180
输出结果说明:
- 输入一个整数按回车:从键盘输入27,按下回车键,scanf() 就会读取输入数据并赋值给变量 a;本次输入结束,接着执行下一个 scanf() 函数,再从键盘30,按下回车键,就会将30赋值给变量 b,都是同样的道理。
- 四个变量的值一次性输入:27 30 18 10按回车。
- 为什么这两种方式都行呢?联系输入赋值有误吗?
从本质上讲,我们从键盘输入的数据并没有直接交给 scanf(),而是放入了缓冲区中,直到我们按下回车键,scanf() 才到缓冲区中读取数据。
如果缓冲区中的数据符合scanf()的要求,那么就读取结束;
如果不符合要求,那么就继续等待用户输入,或者干脆读取失败。
其实 scanf 和 printf 非常相似,只是功能相反罢了:
scanf("%d %d", &a, &b); // 获取用户输入的两个整数,分别赋值给变量 a 和 b printf("%d %d", a, b); // 将变量 a 和 b 的值在显示器上输出
它们都有格式控制字符串,都有变量列表。不同的是,scanf 的变量前要带一个&符号。&称为取地址符,也就是获取变量在内存中的地址。
数据是以二进制的形式保存在内存中的,字节(Byte)是最小的可操作单位。为了便于管理,我们给每个字节分配了一个编号,使用该字节时,只要知道编号就可以。
下图是4G内存中每个字节的编号(以十六进制表示):
这个编号,就叫做地址(Address)。int a;会在内存中分配四个字节的空间,我们将第一个字节的地址称为变量 a 的地址,也就是&a的值。对于前面讲到的整数、浮点数、字符,都要使用 & 获取它们的地址,scanf 会根据地址把读取到的数据写入内存。
下面咱输出一下他们的地址:
#include <stdio.h> int main() { int a=7; int b=15; printf("&a=%p, &b=%p\n", &a, &b); return 0; }
运行结果:
&a=0x7fffc349f32c, &b=0x7fffc349f328
输出结果说明:
这里看到的地址都是假的,是虚拟地址,并不等于数据在物理内存中的地址。虚拟地址是现代计算机因内存管理的需要才提出的概念。
输入其它数据
除了输入整数,scanf() 还可以输入单个字符、字符串、小数等。 下面咱们先看个栗子:
#include <stdio.h> int main() { char name[10]; int age; float money; scanf("%s", &name); scanf("%d", &age); scanf("%f", &money); printf("我叫:%s,今年 %d,兜里有%g刀。\n", name, age, money); return 0; }
输入内容:
SC 18 88.2
运行结果:
我叫:SC,今年 18,兜里有88.2刀。
输出结果说明:
- scanf() 和 printf() 虽然功能相反,但是格式控制符是一样的,字符串、整数、小数对应的格式控制符分别是 %s、%d、%f。
- 字符串,在[]里面要指明字符串的最大长度。
运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。
算术运算符
下表显示了 C 语言支持的所有算术运算符。假设变量 a的值为 10,变量 b 的值为 5,则:
下面咱跑个实例,了解一下算术运算符:
#include <stdio.h> int main() { int a = 10; int b = 5; int c ; c = a + b; printf("1 - c 的值是 %d\n", c ); c = a - b; printf("2 - c 的值是 %d\n", c ); c = a * b; printf("3 - c 的值是 %d\n", c ); c = a / b; printf("4 - c 的值是 %d\n", c ); c = a % b; printf("5 - c 的值是 %d\n", c ); c = a++; // 赋值后再加 1 ,c 为 10,a 为 11 printf("6 - c 的值是 %d a 的值是 %d\n", c , a ); c = a--; // 赋值后再减 1 ,c 为 11 ,a 为 10 printf("7 - c 的值是 %d a 的值是 %d\n", c , a ); }
运行结果:
1 - c 的值是 15 2 - c 的值是 5 3 - c 的值是 50 4 - c 的值是 2 5 - c 的值是 0 6 - c 的值是 10 a 的值是 11 7 - c 的值是 11 a 的值是 10
关系运算符
下表显示了 C 语言支持的所有关系运算符。假设变量 a 的值为 10,变量 b 的值5,则:
下面咱跑个实例,了解一下关系运算符:
#include <stdio.h> int main() { int a = 10; int b = 5; if( a == b ) { printf("1 - a 等于 b\n" ); } else { printf("1 - a 不等于 b\n" ); } if ( a < b ) { printf("2 - a 小于 b\n" ); } else { printf("2 - a 不小于 b\n" ); } if ( a > b ) { printf("3 - a 大于 b\n" ); } else { printf("3 - a 不大于 b\n" ); } if ( a <= b ) { printf("4 - a 小于或等于 b\n" ); } if ( a >= b ) { printf("5 - a大于或等于 b\n" ); } }
运行结果:
1 - a 不等于 b 2 - a 不小于 b 3 - a 大于 b 5 - a大于或等于 b
逻辑运算符
下表显示了 C 语言支持的所有关系逻辑运算符。假设变量 a 的值为 1,变量 b 的值为 0,则:
下面咱跑个实例,了解一下逻辑运算符:
#include <stdio.h> int main() { int a = 5; int b = 20; int c ; if ( a && b ) { printf("1 - 条件为真\n" ); } if ( a || b ) { printf("2 - 条件为真\n" ); } /* 改变 a 和 b 的值 */ a = 0; b = 10; if ( a && b ) { printf("3 - 条件为真\n" ); } else { printf("3 - 条件为假\n" ); } if ( !(a && b) ) { printf("4 - 条件为真\n" ); } }
运行结果:
1 - 条件为真 2 - 条件为真 3 - 条件为假 4 - 条件为真
位运算符
下表显示了 C 语言支持的位运算符。假设变量 a 的值为 60,变量 b 的值为 13,则:
下面咱跑个实例,了解一下位运算符:
#include <stdio.h> int main() { unsigned int a = 60; // 60 = 0011 1100 unsigned int b = 13; // 13 = 0000 1101 int c = 0; c = a & b;//12 = 0000 1100 printf("1 - c 的值是 %d\n", c ); c = a | b;//61 = 0011 1101 printf("2 - c 的值是 %d\n", c ); c = a ^ b;//49 = 0011 0001 printf("3 - c 的值是 %d\n", c ); c = ~a;//-61 = 1100 0011 printf("4 - c 的值是 %d\n", c ); c = a << 2; //40 = 1111 0000 printf("5 - c 的值是 %d\n", c ); c = a >> 2;//15 = 0000 1111 printf("6 - c 的值是 %d\n", c ); }
运行结果:
1 - c 的值是 12 2 - c 的值是 61 3 - c 的值是 49 4 - c 的值是 -61 5 - c 的值是 240 6 - c 的值是 15
赋值运算符
下表列出了 C 语言支持的赋值运算符:
下面咱跑个实例,了解一下赋值运算符:
#include <stdio.h> int main() { int a = 21; int c ; c = a; printf("1 - = 运算符实例,c 的值 = %d\n", c ); c += a; printf("2 - += 运算符实例,c 的值 = %d\n", c ); c -= a; printf("3 - -= 运算符实例,c 的值 = %d\n", c ); c *= a; printf("4 - *= 运算符实例,c 的值 = %d\n", c ); c /= a; printf("5 - /= 运算符实例,c 的值 = %d\n", c ); c = 200; c %= a; printf("6 - %%= 运算符实例,c 的值 = %d\n", c ); c <<= 2; printf("7 - <<= 运算符实例,c 的值 = %d\n", c ); c >>= 2; printf("8 - >>= 运算符实例,c 的值 = %d\n", c ); c &= 2; printf("9 - &= 运算符实例,c 的值 = %d\n", c ); c ^= 2; printf("10 - ^= 运算符实例,c 的值 = %d\n", c ); c |= 2; printf("11 - |= 运算符实例,c 的值 = %d\n", c ); }
运行结果:
1.1 - = 运算符实例,c 的值 = 21 2 - += 运算符实例,c 的值 = 42 3 - -= 运算符实例,c 的值 = 21 4 - *= 运算符实例,c 的值 = 441 5 - /= 运算符实例,c 的值 = 21 6 - %= 运算符实例,c 的值 = 11 7 - <<= 运算符实例,c 的值 = 44 8 - >>= 运算符实例,c 的值 = 11 9 - &= 运算符实例,c 的值 = 2 10 - ^= 运算符实例,c 的值 = 0 11 - |= 运算符实例,c 的值 = 2
杂项运算符
下表列出了 C 语言支持的其他一些重要的运算符,包括 sizeof 和 ? :
下面咱跑个实例,了解一下杂项运算符:
#include <stdio.h> int main()= { int a = 4; short b; double c; int* ptr; /* sizeof 运算符实例 */ printf("1 - 变量 a 的大小 = %lu\n", sizeof(a) ); printf("2 - 变量 b 的大小 = %lu\n", sizeof(b) ); printf("3 - 变量 c 的大小 = %lu\n", sizeof(c) ); /* & 和 * 运算符实例 */ ptr = &a; /* 'ptr' 现在包含 'a' 的地址 */ printf("4 - a 的值是 %d\n", a); printf("5 - *ptr 是 %d\n", *ptr); /* 三元运算符实例 */ a = 10; b = (a == 1) ? 20: 30; printf("6 - b 的值是 %d\n", b ); b = (a == 10) ? 20: 30; printf("7 - b 的值是 %d\n", b ); }
运行结果:
1 - 变量 a 的大小 = 4 2 - 变量 b 的大小 = 2 3 - 变量 c 的大小 = 8 4 - a 的值是 4 5 - *ptr 是 4 6 - b 的值是 30 7 - b 的值是 20
如有错误,烦请斧正