puts()
puts()只能输出字符串、Space、Enter、Tab
#include int main() { char token[20] = "12 \n12"; puts(token); return 0; }
结果如下:
注意:使用 puts() 后,系统会在自动在其后添加换行符(’/n’)
putc()
作用是输出一个字符
#include
int main() { putc('a',stdout); putc('\n',stdout); putc(97,stdout); //97的 ASCII码是 a return 0; }
结果如下:
fputs()
fputs()也是用于输出字符串,但是fputs()的函数原型不大一样是:
#include int main() { char token[20] = "12 \n12"; fputs(token,stdout); //第二个参数可以指向任意流,这里写的是输出流 return 0; }
结果如下:
fputs()和 puts()有两个小区别:
1.puts()只能向标准输出流输出,而 fputs()可以向任何流输出
2.使用 puts()时,系统会在自动在其后添加换行符,而使用 fputs()时,系统不会自动添加换行符
fputc()
类似于fgetc和getc之间的关系,fputc()的用法与putc()一样区别在于:putc()是宏定义,fputc()是函数,putc()的调用效率相对会比fputc()高一些(宏高于函数)
六、流程控制
6.1 流程控制
1.概念:
流程控制就是指程序代码执行的顺序
2.流程控制的分类:
①顺序执行:从上往下执行,如果不使用流程控制语句,默认就是顺序执行
②选择执行:有选择地执行某些代码,不执行另一些代码,又称分支结构
③循环执行:多次执行某些代码
6.2 选择执行
6.2.1 if语句
//语法1:如果表达式为真,执行pass,否则不执行 //如果pass中只有1个语句,可以省略{...},这样if只能控制其后的第1个语句 if (<表达式>) { //pass1 } //实例: if (2>1) printf("%d\n",33);//结果:33 if (2) printf("AAA\n");//结果:AAA if (2>3) printf("AAA\n");//结果:[未执行] printf("BBB\n");//结果:BBB if (2>3) { printf("AAA\n");//结果:[未执行] printf("BBB\n");//结果:[未执行] } ----------------------------------------------------------- //语法2:如果表达式为真,则执行pass1,否则执行pass2 //如果不加{...},else也只能控制其后的第1个语句 if (<表达式>) { //pass1 } else { //pass2 } //实例: if (2>3) { printf("AAA\n");//结果:[未执行] } else { printf("BBB\n");//结果:BBB } ----------------------------------------------------------- //语法3:执行第1个为真的表达式对应的pass,如果所有表达式均为假,则执行else对应的pass //注意:else和else if需要配合if使用,不能单独使用;也可以只使用if和else if if (<表达式1>) { //pass1 } else if (<表达式2>) { //pass2 } else {//没有<表达式> //pass3 } ----------------------------------------------------------- //实例: double delta=-1; if (delta>0) { printf("有2个解\n");//结果:[未执行] } else if (delta==0) { printf("有1个解\n");//结果:[未执行] } else { printf("无解\n");//结果:无解 } double alpha=2; if (alpha>0) { printf("大于0\n");//结果:大于0 } else if (alpha>=0) { printf("大于等于0\n");//结果:[未执行]//只执行第1个为真的分支中的语句 } else { printf("其他\n");//结果:[未执行] } double beta=2; if (3 printf("AAA\n");//结果:[未执行] } else if (beta>=0) { printf("BBB\n");//结果:BBB } else { printf("CCC\n");//结果:[未执行] } if (3>2); A; //等价于: if (3>2) ;//这是1个空语句,什么都不执行 A; // if (<表达式>)//合法 A; else B; if (<表达式>);//不合法 A; else B; // if (<表达式1>) A; else if (<表达式2>) B; else (<表达式3>)//不合法 C; // if (<表达式1>) A; else (<表达式2>); B; //等价于: if (<表达式1>) A; else (<表达式2>); B;
6.2.2 switch语句:
//语法:执行表达式的值对应的语句然后退出,如果表达式的值不为任何列出的值,执行default对应的语句然后退出 //如果某行结尾处不加break,则会执行下1个条件的语句;故除非在最后1行,不得省略break //实际上case和default只是程序的入口,进入后从上往下连续执行,直到碰到break就退出 switch (<表达式>) { case <值1>:<语句1>;break; ... default:语句4;break;//其他均不成立时执行;如果在最后1行,可以省略break } ----------------------------------------------------------- //注意: 1.case对应的值即常量表达式,也可称为标签 标签只能是:①枚举常量(枚举元素)②数值常量③字符常量④常变量⑤宏名 //注意:1个枚举常量就是某个枚举中某个可能的值,如enum Boolean(Y,N)中的Y和N 标签不能是:①普通变量②枚举变量 2.switch语句是选择执行,不是循环执行 break表示退出switch语句,switch语句中不能出现continue 3.每行有2个分号(;),1个冒号(:),每行至少是2个语句 4.可以没有default语句 5.每行都可以执行多个语句 ----------------------------------------------------------- //实例:电梯程序 int i scanf("%d",&i);//输入: ①3 ②9 switch (i) { case 1:printf("这是1层\n");break; case 2:printf("这是2层\n");break; case 3:printf("这是3层\n");break;//输入①后执行 case 4:printf("这是4层\n");break; default:printf("没有这1层\n");//输入②后执行 } int j scanf("%d",&i);//输入:2 switch (j) { case 1:printf("这是1层\n");break; case 2:printf("这是2层\n");//执行 case 3:printf("这是3层\n");break;//执行 case 4:printf("这是4层\n");break; default:printf("没有这1层\n"); }
6.3 循环执行
6.3.1 for语句(入口条件循环):
//语法1:先执行初始化,条件为真时执行语句再完成增量,然后重新判断表达式是否为真;条件为假时跳出循环 //初始化只在最开始执行1次;每次循环都是先执行语句再完成增量 //默认只能控制1个语句,如果需要控制多个语句,需要加{...} for (<初始化>;<条件>;<增量>) { <语句1>; <语句2>; ... }//花括号内的称为"循环体" ----------------------------------------------------------- //实例: int i,sum=0; for (i=1;i<=100;++i) { printf("%d\n",i);//结果:1 2 3 ... 100(如果是先完成增量,不应该有1而应该有101) sum+=i; } printf("%d\n",i);//结果:101 printf("%d\n",sum);//结果:5050 ----------------------------------------------------------- //注意: int i; //不要把i设为float(即不要把循环中的"计数变量"设为float),否则可能有误差 //使第100次循环时的i不为100,而为如99.999999/100.000001,使循环提前/延后结束 //还可能造成1/i的结果不准确 float sum=0.0; for (i=1;i<=100;i++) { printf("%d\n",1/i);//结果:1 0 ... 0 printf("%f\n",1/(float)(i));//结果:1.000000 0.500000 ... 0.010000 printf("%f\n",(float)/(1/i));//结果:1.000000 0.000000 ... 0.000000 sum+=1/(float)(i); //相当于:sum+=1.0/i;//1.0默认是double,所以会有警告 printf("%f\n",sum);//结果:1.000000 1.500000 ... 5.187378 } printf("%f\n",sum);//结果:5.187378
//语法2:for可以嵌套任意层 int i,j; for (i=1;i<=100;i++) for (j=1;j<=100;j++) printf("%d",i+j);//结果:2 3 ... 11 3 ... 20 for (;;) for(;;) ; ;
-----------------------------------------------------------
示例:
整个for循环是其所在块的"子块"(Sub-Block),循环体是整个for循环块的子块:
//其他循环和分支语句也类似 #include int main(void) { int i=10;//第1个i(最外层的i) for (i=1;i<5;i++) {//第2个i(循环条件中的i) int i=100;//第3个i(循环体中的i) printf("%d\n",i); i++; } printf("%d\n",i); return 0; } //结果: 100 100 100 100 5
6.3.2 while语句(入口条件循环):
//语法:表达式为真时,执行语句,然后重新判断表达式是否为真;为假时,跳出循环
//默认只能控制1个语句,如果需要控制多个语句,需要加{...} while (<表达式>) { <语句>; } ----------------------------------------------------------- //实例: int i=0; while (i<10) { i++; printf("%d ",i);//结果:1 2 3 4 5 6 7 8 9 10 }
6.3.3 do…while语句(出口条件循环):
//语法:先执行语句,表达式为真时再次执行语句,然后重新判断表达式是否为真;为假时,跳出循环
//注意: ①结尾的分号(;)不能省略,整体是1个语句 ②语句至少会被执行1次
//主要用于人机交互
do { <语句>; } while (<表达式>); ----------------------------------------------------------- //实例: int i=1; do { printf("AAA ");//结果:AAA AAA AAA AAA i++; } while (i<5); int j=10; do { printf("AAA ");//结果:AAA j++; } while (j<5); char m; do { printf("ASD\n");//结果:ASD ASD ASD scanf("%c",&m);//输入:y Y k fflush(stdin); } while (m=='Y'||m=='y')//注意:如果用" "包裹,必须输入y\n或Y\n,因为"y"结尾默认有\n
6.3.4 各种循环间的转换:
for和while等价:
//即任何for循环均可转换成while循环,反之亦然
do while循环和for/while循环不等价
//有些for/while循环无法转换成do while循环(因为do while至少执行1次语句)
for (1;2;3) { A; } //等价于: 1; while (2) { A; 3; } //
6.4终止语句
6.4.1 break:
//语法: break; //功能: 1.跳出for/while循环 2.终止switch语句的执行 //实例: while (1<2) { printf("aaa");//结果:aaa break; } int i; scanf("%d",&i); switch (i) { case 1:printf("这是1层\n");//结果:执行 case 2:printf("这是2层\n");//结果:执行 case 3:printf("这是3层\n");break;//结果:执行 case 4:printf("这是4层\n"); } //注意:break用于for循环时会直接终止,连增量语句一起跳过剩下的所有语句 int c; char ch; printf("input:"); for (c=0;c<10;c++) { ch=getchar(); if (ch=='\n') { break; } putchar(ch); printf("%d ",c); } printf("%d\n",c); //结果: //input:ksjd //k0 s1 j2 d3 4//c最终为d这1轮结束后的值,而没有再自增1次
6.4.2 continue:
//语法: continue; //功能: 终止本次循环,直接开始判断是否需要执行下次循环 //实例: int i=0; while (i<5) { if (i==3) { i++; continue; } printf("%d",i);//结果:0 1 2 4 i++; } //注意:continue用于for循环,终止本轮循环后,会先执行本轮的增量语句再进行下1轮循环 int c; char ch; printf("input:"); for (c=0;c<10;c++) { ch=getchar(); if (ch=='\n') { continue; } putchar(ch); printf("%d ",c); } printf("%d\n",c); //结果: //input:klakd //k0 l1 a2 k3 d4 sdsdfasas //s6 d7 s8 d9 10//c最终是10,并没有跳过任何1次自增//并且第2次输入后c从6而不是5开始
6.5 跳转语句
谨慎使用goto语句,这会使程序的控制流变得难以追踪
6.5.1 语法
//跳转到指定标签处(把控制转移到指定语句处):
goto;
//定义标签:
:{;;...}
//注意:标签只是给相应语句做了标识,这些语句仍会在正常的流程中被执行
//参数说明:
label:指定标签
//标签的命名遵循标识符的命名规则
cmd:指定该标签对应的语句
//如果goto只有1个语句要控制,可以省略{...}
//对Fortran/BASIC来说,不能使用复合语句
//实例: #include int main(void) { int a = 10; LOOP:do { if( a == 15) { a = a + 1; goto LOOP; } printf("%d ",a); a = a + 1; } while( a < 20 ); P:printf("end");//即使被标签标识了,也会在正常的流程中执行 return 0; } //结果: 10 11 12 13 14 16 17 18 19 end
6.5.2 退出嵌套循环:
#include int main(void) { int i; for (i=0;i<10;i++) { int j; for (j=0;j<10;j++) { int k; for (k=0;k<10;k++) { if (i-j+k==11) { printf("goto end "); goto END; } printf("%d ",i-j+k); } printf("\n"); } } END:printf("end"); return 0; } //结果: 0 1 2 3 4 5 6 7 8 9 -1 0 1 2 3 4 5 6 7 8 -2 -1 0 1 2 3 4 5 6 7 -3 -2 -1 0 1 2 3 4 5 6 -4 -3 -2 -1 0 1 2 3 4 5 -5 -4 -3 -2 -1 0 1 2 3 4 -6 -5 -4 -3 -2 -1 0 1 2 3 -7 -6 -5 -4 -3 -2 -1 0 1 2 -8 -7 -6 -5 -4 -3 -2 -1 0 1 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 -1 0 1 2 3 4 5 6 7 8 -2 -1 0 1 2 3 4 5 6 7 -3 -2 -1 0 1 2 3 4 5 6 -4 -3 -2 -1 0 1 2 3 4 5 -5 -4 -3 -2 -1 0 1 2 3 4 -6 -5 -4 -3 -2 -1 0 1 2 3 -7 -6 -5 -4 -3 -2 -1 0 1 2 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 goto end end
6.5.3 与终止语句:
break和continue是goto的特殊形式
七、数组
简介
在 C 语言中,除了整型、实型和字符型等基本数据类型之外,还可以将基本数据类型按照一定的规则组合起来构成较为复杂的数据类型,称为构造数据类型,又称导出数据类型,主要包括数组、结构体、共用体等。数组中顺序存放了一批相同数据类型的数据,这些数据不仅数据类型相同,而且在计算机内存里连续存放,地址编号最低的存储单元存放数组的起始元素,地址编号最高的存储单元存放数组的最后一个元素。通过数组名标识和序号(C 语言称为下标)可以引用这些数组元素。
数组是一组有序的数据的集合--数组中的元素类型相同,并由数组名和下标唯一地确定。
1. 数组名代表数组的起始地址,是地址常量。
2. 数组元素在内存中是连续存储的。
3. 引用数组元素:数组名[下标],下标从0开始
7.1 一维数组
7.1.1 一维数组的定义
语法:type ArrayName[size];
说明:
1. 定义一个含size个type类型元素的数组。int a[10]; /*定义的整型数组a含10个元素*/
2. size必须是大于0的整型常量表达式。
3. C语言不允许对数组的大小作动态定义。
4. 编译时分配连续内存: 字节数=size* sizeof(type)
7.1.2 一维数组的初始化
语法:type ArrayName[size]={value-list};
存储形式:
1.数组如果不初始化,其元素值为随机数
2.可以只初始化部分数组元素,余者自动赋0值。
int a[5]={ 2 , 4 }等价于 int a[5]={ 2 , 4, 0, 0, 0 };
3.当全部数组元素赋初值时,可不指定数组长度
int a[]={1,2,3,4,5,6} ; //编译系统根据初值个数确定数组维数
4.全局数组或static数组元素自动初始化成0值
static int a[5];等价于static int a[5]={ 0 , 0, 0, 0, 0 };
赋初值方法:
(1)利用赋值语句初始化
char str[26],ch; str[0]= 'A'; str[1]= 'B'; … str[25]= 'Z';
(2)利用赋值语句初始化
(3)利用输入语句初始化
char str[26],ch; for(ch='A';ch<='Z';ch++) str[ch-‘A’]=ch; //将26个大写字母依次存放到str[0] 、 str[1]、 ……str[25]中
char str[26]; int i ; for(i=0; i<26; i++) scanf("%c",&str[i]); …...
7.1.3 一维数组的引用
1.数组必须先定义,后使用
2.只能逐个引用数组元素,不能一次引用整个数组
例 int a[10]; printf(“%d”,a); (错) 必须 for(j=0;j<10;j++) printf(“%d\t”,a[j]); (对)
3.数组元素表示形式:数组名[下标] 数组元素的下标:0~size-1。4.数组名a代表的是数组a在内存中的首地址,也即数组元素a[0]的地址。
scanf(“%d”,&a[0]); 等价于 scanf(“%d”,a); #include void main(void) { int a[5]={1,2,3,4,5}; printf("%d, %d, %d\n",a[0],a[1],a[2]); printf("%d, %d, %d,%d\n",a,&a[0],&a[1],&a[2]); }
运行结果:
7.2 二维数组
7.2.1 二维数组的定义
类型标识符 数组名[常量表达式SIZE 1][常量表达式 SIZE2];例 int a[3][4];
数组在内存中按行列顺序存放,最右下标变化最快。
字节数=sizeof(type) * size1* size2
若二维数组 Num共有 R行 C列;则 Num[i][j]之前有 i*C+ j个元素。
size必须是大于0的整型常量表达式。例如 int a[0][3]; (错)
不允许对数组的大小作动态定义。int i=3 , j=4 ;int a[i][j] ;(错)
7.2.3 二维数组的初始化
1、分行初始化:
部分初始化
第一维长度省略初始化
注意:第二维不能省略
例. 下列数组定义错误的是 D 。
A)int Score[3][3];
B)int Score[][3]={undefined{1},{2},{3}};
C)int Score[3][3]={1};
D)int Score[3][]= {undefined{1},{2},{3}};
7.2.4 二维数组的引用
表示形式:数组名[行下标][列下标]
行下标、列下标可以是常量、变量、函数或表达式。如果下标带有小数,C 编译系统将自动对其取整。
例如,若有定义:float Num[3][10];则Num[2][1]、Num[i][j]、Num[j][i++]都是对数组 Num中数组元素的合法引用。
C 语言对数组元素的引用有以下几种规定:
行下标、 列下标均从 0 开始计算。数组下标表示了数组元素在数组中的顺序号。因此,如果定义:
float Num[2][4]={ 0,1,2,3,4,5,6,7 };
则可以访问的行下标为 0、1,可以访问的列下标为 0、1、2、3。不能越界引用数组元素Num[1][8]、Num[5][3],因为 C 语言从不检查数组下标是否越界,程序员要自己确认数组元素的正确引用,不能越界访问。
在 C 语言中只能逐个引用数组元素,而不能一次引用整个数组。
例如,不能用一个语句输入整个数组:scanf("%f",Num);
必须使用循环语句逐个输入数据:
for(i=0; i<2; i++) for(j=0; j<4; j++) scanf("%f", &Num[i][j]);
数组元素和普通变量一样,可以出现在任何合法的 C 语言表达式中。
例如,数组元素可以出现在函数调用:printf(“%d”, Num[i][j]);中。
定义数组时,方括号中出现的是某一维的长度,只能是常量或常量表达式;
引用数组元素时,方括号中出现的是该元素在数组中的位置标识,可以是常量,变量或任何合法的C 语言表达式。
程序举例:
#include void main(void) { int a[2][3]={{1,2,3},{4,5,6}}; printf("%d, %d\n",a[0][0],a[1][0]); printf("%d, %d, %d, %d\n",a,a+1,&a[0][0],&a[1][0]); }
输出:
7.3 多维数组
7.3.1 定义
多维数组:可以理解为存放数组的数组…以宿舍或公寓为例:元素:就是一个一个房间 1001一层:相当于是一维数组一栋楼:就相当于二维数组一个园区:就相当于三维数组一个高教区:就相当于四位数组一个城市:就相当于五维数组
7.3.2 示例
//多维数组定义演示 int arr[10][10][10][10][10][10]; printf("%d\n", arr[3][4][5][6][7][8]) -------------------------------------------------------------------------- //越靠数组名的定义维数越高 反之越低 int arr[3][4] = {0}; 注:内存当中的存储方式 多个低维数组存储空间组合成高维数组 --------------------------------------------------------------------------- // 如果不指定低维数组的大小,数组不知道应该存储多少个元素(不知道怎么开辟内存) // 对于省略数组大小,只适用于最高维 int arr[][4] = {0};
7.3.3 多维数组
// 多维数组遍历,使用嵌套循环方式,外层循环对应高维,内层 for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("arr[%d][%d] = %d\n", i, j, arr[i][j]); } }
7.3.4 字符数组
1 标题字符型数据的概念与存储
字符串常量:
用一对双引号括起来的若干字符序列。
字符串长度:字符串中字符的个数。
字符串的结束标志:NULL ‘\0’,ASCII值为0。
“ ”表示空字符串,长度为0,占一个字节,即NULL。
2 字符数组的定义与初始化
字符数组的定义与一维数组、二维数组基本相同,类型标识符为 char。说明:
1.字符数组的每一个元素都是字符变量
2.处理n个字符,size必须≥n+1
#include "stdio.h" void main() { char a[5]={65, ' ',98}; printf("%c, %d,%d, %d,", a[0],a[1],a[2],a[3]); }
初始化:
1.逐个数值赋给字符数组的元素
char str[10]={112,114,111,103,114,97,109,0};
2.逐个字符赋给字符数组的元素
char str[10]={‘p’,‘r’,‘o’,‘g’,‘r’,‘a’,‘m’,’\0’};
3.用字符串常量直接初始化数组
char str[10]={“program”}; char str[10]=“program”;
4.字符数组定义初始化时可省略第一维长度,由系统编译时根据初值个数自动确定数组长度:
char Str[ ]=“program”; // Str数组长度自动确定为 8
5.通常一维字符数组用于存放一个字符串,二维字符数组用于存放多个字符串,而且至少要按最长的字符串长度加1设定第二维长度。二维字符数组定义初始化时同样只能省略第一维长度
char Subject[3][15]={“C programming”,“Java”,“Authorware”}; char Subject[][15] ={“C programming”,“Java”,“Authorware”};
3 字符数组的输入输出
1.逐个字符输入输出
用格式符“%c”输入或输出一个字符
#include void main(void ) { char a[5]; int i=0; while(i<=4) scanf("%c",&a[i++]); for(i=0;i<5;i++) printf("%c", a[i]); }
2.将整个字符串一次输入或输出。
用格式符“%s”输入输出字符串。
void main(void ) { char a[5]; scanf("%s",a); //scanf中%s输入时,遇空格、TAB或回车结束 printf("%s",a); }
3.用“%s”格式符输入字符串时,字符数组名前不要再加地址符&,因为数组名代表该数组的起始地址。
4.用“%s”格式符输入字符串时,输入的字符串应≤字符数组的长度-1。系统自动在后面加 个‘\0’结束符。
void main( void ) { char a[]={'h','e','l','\0','l','o','\0'}; printf("%s",a); }
输出:hel
原因:数组中有多个‘\0’时,遇第一个结束