C程序设计语言 第一章
只记录个人想记录的。
clion 这个编辑器 在debug 模式,使用按键 ctrl+d可以输出终止符
第一章
C语言程序,都是有函数和变量组成。函数中包含的语句是要执行的计算机操作,变量是用于储存计算过程中使用的值。通常情况下,函数名没有限制,但是main是一个特殊函数名,每个程序都从main函数开始执行。main函数通常会用调用其他函数来完成工作。
制表符\t 回退符\b 双引号\" 表示斜杠本身\\
练习1-1
#include <stdio.h>
int main()
{
printf("hello, world");
}
练习1-2
可以编译完成,并且出现 hello,worldc的字样。
1.2 变量与算术表达式
在C语言中,所有变量都是必须先声明后使用。声明用于说明变量的属性,由一个类型名和一个变量表组成。
例如: int fahr int表示后面的变量为整数,float表示变量为浮点数。
还包括 char 字符(一个字节)
short 短整型
long 长整型
double 双精度浮点型
这些数据类型对象的大小取决于具体的机器。还存在这些类型的数组,结构,联合,指向这些类型的指针以及返回这些类型值的函数。
变量设置初值。各条语句均为分号结束。
在C语言及许多其他语言中,整数除法操作将执行舍位,结果中的任何小数部分都会被抛弃。
printf函数%分别对应每个参数的数目和类型都必须匹配。该函数不是C语言的本身的一部分,是标准函数中一个有用的函数而已。
print(" %3d %6d\n", fahr, celsius);这句话指明在打印时的宽度,fahr占3个数字宽,celsius占6个数字宽。
此外printf 还支持%o表示8进制,%x表示十六进制,%c表示字符,%s表示字符串,%%表示百分号本身。
练习1-4 编写一个程序打印摄氏温度转换为相应华氏温度的转换表
void tem()
{
float fahr, celsius;
float lower, upper, step;
upper = 300;
step = 20;
lower = -50;
celsius = upper;
while ( celsius>lower )
{
fahr = celsius*9.0/5.0+32.0;
printf("华氏温度是%.2f\t摄氏温度%.2f\n",fahr, celsius);
celsius = celsius - step;
}
C语言中一个通用规则,在允许使用某种类型变量值的任何场合,都可以使用该类型的更复杂的表达式。
for循环的例子:
for(初始化部分fahr = 0;条件部分fahr <= 300;执行操作部分可以是任何表达式)
for语句比较适合初始化和增加步长都是单条语句并且逻辑相关的情形,因为他将循环控制语句集中放在一起,比while语句更紧凑。
1.4 符号常量
#define指令可以把符号名(或成为符号常量)定义为一个特定的字符串
#define 名字 替换文本 在该定义之后,程序中出现的所有在#define中定义的名字(既没有用引号引起来,也不是其他名字的一部分)都将用相应的替换文本替换。
其中,名字与普通变量名的形式相同:他们都是以字母打头的字母和数字序列;替换文本可以是仍和字节序列,而不仅限于数字。
#define LOWER 0
#define UPPER 300
#define STEP 20
main()
{
int fahr;
for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32);
}
其中,LOWER,UPPER和STEP 都是符号常量,非变量,因此不需要出现在声明中。 符号常量通常用大写字母拼写,这样可以容易与小写作字母拼写的变量名相区别。
注意,#define指令行的末尾没有分号。
1.5 字符输入/输出
无论文本从何处输入,输出到何处,其输入/输出都是按照字节流的方式处理。文本流是由多行字符构成的字符序列,每行字符由0个或多个字符组成,行末是一个换行符。
标准库提供一次读/写一个字符的函数,其中最简单的是getchar和putchar两个函数。每次调用时,getcahr函数从文本流中读入下一个输入字符,并将其作为结果值返回。
C = getchar() 变量C中将包含输入流中的夏一个字符。这种字符通常是通过键盘输入的。 putchar() 调用该函数时会把整型变量C的内容以字符的形式打印出来
通常是现实在屏幕上,putchar与printf 这两个函数可以交替调用,输出的次序与调用的次序一致。
1.5.1 文件复制
根据上面两个函数可以读取文件,方法如下:
读一个字符
while (该字符不是文件结束指示符)
读取刚读入的字符
读下一个字符
转换为C语言为下:
#include <stdio.h>
main()
{
int c;
c = getchar()
while (c != EOF) {
putchar(c);
c = getchar();
}
}
字符在内部使用位模式存储的。char 类型专门用于存储这种字符型数据。区分文件数据是否有效和输入结束的方法是getchar函数将返回一个特殊值,这个特殊
值与任何实际字符都不同。值为EOF(end of file,文件结束)。在声明变量c的时候,必须让他大到足以存放getchar函数返回的任何值。所以将它声明为int类型
EOF定义为在头文件<stdio.h>中,是个整型数,它与仍和char类型的值都不相同。这里使用符号常量,可以确保程序不需要依赖于其他对应的任何特定数值。
!=(不等于)的优先级比=(等于)要高。 例子 c = getchar() != EOF 等价于 c = (getchar() != EOF)
练习 1-6 验证表达式getchar() != EOF的值是0还是1。
int c;
printf("请输入字符:\n")
c = (getchar() != EOF);
printf("%d\n",c);
return 0;
如果使用while (c = getchar() != EOF)
printf("%d\n",c); 这样得到的结果是每个字符对应的ascii值
练习 编写一个打印EOF值的程序。
printf("hex=%x U=%u D=%d\n",EOF,EOF,EOF);
return 0;
按16进制,无符号十进制整型,有符号十进制整型输出值:
Hex=ffffffff U=4294967295 D=-1
1.5.2 字符计数
例子:
nc++ 等于 nc = nc + 1 有两种形式 ++nc nc++都是值增加1。该字符技术程序使用long类型的变量存放计数值,转换说明
%ld告诉printf函数对应参数是long整型。
例子2:
1.5.3 行计数
int main() { //编写一个统计空格/制表符/与换行符的程序。 int ch,blank,tabs,line_break; //四个变量,第一个储存读取到的字节,后三个用来存储空格,制表符,换行符的数量 blank = tabs = line_break =0; //初始化用来存储数量的值 while ((ch=getchar()) != EOF) //定义循环,ch(读取到的字节) 不等于 EOF 结束符 就一直循环 { if(ch == ' ') //如果读取到的是空格 blank +1 ++blank; else if(ch == '\t') //如果读取到的是制表符 tabs +1 ++tabs; else if(ch == '\n') //如果读取到的是换行符 line_break +1 ++line_break; } printf("space:%d\ntabs:%d\nnenter:%d\n",blank,tabs,line_break); return 0; }
自己不细心,刚找到插入代码的 选项.......
int main() { // 练习1-9 编写一个将输入复制带输出的程序,并将其中练习的多个空格用一个空格代替 char ch; //定义字符变量 int count = 0; // 初始计数变量 while((ch = getchar()) != EOF) //定义循环,读取到的字符不等于EOF结束符就继续读取 { if(ch != ' ') //如果读取到的字符不是空格则执行 { count = 0; //赋值给变量count putchar(ch); //输出读取到的字符 } else if(count == 0) //如果读取到的字符是空格且count变量为0 { putchar(ch); //输出空格 ++count; //变量count +1 } //如果两个或以上的空格出现时,那count会出现变化,也就是不会执行两个IF语句。不执行输出操作,继续循环直到下一个不为空格才重新定义count和输出字符 } }
第三题
1 int main() 2 { 3 // 练习1-10 编写一个将输入复制到输出的程序,并将其中的制表符替换为\t,把回退符替换为\b,把反斜杠替换为\\。这样可以将制表符和回退符以可见方式显示出来。 4 char ch; 5 while((ch = getchar()) != EOF) 6 { 7 if(ch == '\t') 8 printf("\\t"); //如果等于制表符就输出 \t 9 if(ch == '\b') 10 printf("\\b"); 11 if(ch == '\\') 12 printf("\\\\"); 13 if((ch != '\t')&&(ch != 'b')&&(ch != '\\')) // 如果不等于上面那些 就正常输出字符 14 putchar(ch); 15 } 16 return 0; 17 }
1.5.4 单词计数
统计行数,单词数,与字符数
1 int main() 2 { 3 // 统计行数,单词数与字符数 4 int c,nl,nw,nc,state; //定义一些变量,字节,行,单词,状态 5 state = OUT; // 初始状态为0 OUT在上面定义为0 6 nl = nw = nc = 0; //初始数值为0 7 while ((c=getchar()) != EOF) //和之前循环一样 不出现停止 就一直循环 8 { 9 ++nc; // 只要进循环说明有字节,那字节+1 10 if (c == '\n') //等于换行符执行 11 ++nl; //行+1 12 if (c == ' ' || c == '\n' || c =='\t') //等于其中任何一个执行以下操作 13 state = OUT; 14 else if (state == OUT){ //这里是除了以上情况如果状态为0 那执行 15 state = IN; 16 ++nw; //出现空格 就算一个单词,如果没空格就不算单词 17 } 18 } 19 printf("%d %d %d\n",nl,nw,nc);
练习题1-11 测试该程序,换行符算一个字符,输入EOF并不会终止程序
1 练习题1-12 编写一个程序,以每行一个单词的形式打印输入。 2 int main() 3 { 4 //练习1-12 编写一个程序,以每行一个单词的形式打印其输入。 5 int c; 6 while ((c = getchar()) != EOF) 7 { 8 if (c == ' ') 9 putchar('\n'); //尝试使用printf 是无效的 使用putchar有效 10 else 11 putchar(c); 12 } 13 return 0; 14 }
1.6 数组
1 int main() 2 { 3 int c, i, nwhite, nother; //定义变量 4 int ndigit[10]; //定义数组,占用10个int空间的,4个字节每个字节8位为int,一共40个字节 5 6 nwhite = nother = 0; //初始化数值 7 for (i=0; i < 10; ++i) // for循环(初始化;条件;动作执行) 8 ndigit[i] = 0; // 让数组里面每个数都等于0 也就是这个数组里面有10个0 9 10 while ((c=getchar()) != EOF) //while循环 11 if (c >= '0' && c<= '9') //if 语句查看输入的是否为数字 12 ++ndigit[c-'0']; //这个我一下就没看懂,使用debug才发现,这句话意思是让数组中第C-0个数字加1操作,我感觉很厉害,
比如输入数字2那就让数组中2-0(ndigit[2-0])的那个数+1,之前里面10个数都是0,现在里面第3个数是1。 13 else if (c == ' ' || c == '\n' || c == '\t') //如果出现这些就下面的+1 14 ++nwhite; 15 else //其他情况 16 ++nother; 17 printf("digits ="); //这里输出和python不一样,这样输出不换行 18 for (i = 0; i < 10; ++i) 19 printf("%d",ndigit[i]); //继续到这里循环输出数组里面的每个数依然不换行 20 printf(", white space = %d, other = %d\n", nwhite, nother); 21 }
char类型的字符是小整型,因此char类型的变量和常量在算术表达式中等价于int类型的变量和常量。例如 上面代码的c-'0'。
switch语句提供编写多路分支程序的另一种方式,特别适合于判定某个整型或字符表达式是否与一个常量集合中的某个元素相匹配的情况。
1 练习题1-13 打印输入单词的直方图 2 int main() 3 { 4 char c,i,z; //定义分别代表用来存储单字符的c,用来存储多少个字符的i,和一个用来初始化的z 5 i = z = 0; //初始值 6 char w[20]; //没用到 尴尬 7 while ((c = getchar()) != EOF) //不解释 8 { 9 i++; //每次出现字符就计数 10 printf("%c",c); //打印字符 11 if (c == ' ' || c == '\t' || c == '\n') //查看是否有换行符或者空格 12 { 13 if (i<12) //这个查看是不是够12个字符 14 { 15 for (z=0;z<=12-i;z++) 16 { 17 printf("%s"," "); //如果不够12个字符用空格补齐,主要是为了对齐 18 } 19 } 20 for (z = 0;z<i-1;z++) //查看多少个字符 21 { 22 printf("%s","*"); //输出*号 23 } 24 printf("\n"); //输出换行符 25 i = 0; //让i归0 计数下一次的值 26 } 27 } 28 }

1 1-13 垂直方向写法,重点在于 char a[50][50]二维数组的表达方式,第一个数组是行,第二个数组是列,比如第一行的第一列是是*号。循环读取过程中如果出现字母,那就在那个位置添加一个*号,
如果读取到输入不是字母,那就在那个位置添加空格,在循环时,要循环单词的字母的个数,这个数是个固定值是最长的那个单词个数,然后循环行,以上图来说,循环是第一行的第三格子,打印空格,
第二行的第三格子,也是空格,第三行的第三个格子是*号,然后第一行的第二个格子是空格,依次类推,感觉有点别扭,下面这段代码是这样执行的。我使用的是clion这个编辑器,使用debug执行,
大部分编辑器都有这样的功能。
2 int nc, ns, nw, i, m, n; /*定义字符个数,单词结束判断条件,单词个数,循环变量*/ 3 int max; /*定义最长单词的字符数*/ 4 int p[50]; /*定义单词长度数组*/ 5 char a[50][50]; /*定义单词字符数组*/ 6 char c; /*定义输入字符*/ 7 max=0; /*最长单词字符数初始为0*/ 8 i=1; /*单词长度数组下标初始值为1*/ 9 ns=nc=nw=0; /*单词判断初始条件,字符个数初始值,单词个数初始值都为0*/ 10 m=n=1; /*单词数组下标初始值为1*/ 11 while ((c=getchar())!=EOF){ /*当输入字符不是文件结束符*/ 12 if ((c>='A'&&c<='Z')||(c>='a'&&c<='z')){ /*当输入字符是字母时*/ 13 ++nc; /*字符个数增加1个*/ 14 a[m][n]='*'; /*单词数组赋值为星号*/ 15 ++n; /*本单词数组的二元下标增加1位*/ 16 ns=1; /*单词判断为单词内*/ 17 } 18 else if (ns==1){ /*如果输入字符不是字母并且单词判断条件是在单词内*/ 19 ++nw; /*单词个数增加1位*/ 20 21 p[i]=nc; /*字符个数赋值给单词长度数组*/ 22 if (nc>=max) /*如果字符个数大于目前单词长度最大值*/ 23 max=nc; /*当前字符个数赋值给单词长度最大值*/ 24 ++i; /*单词长度数组下标增加1位*/ 25 n=1; /*单词数组二元下标回到初始值*/ 26 ns=nc=0; /*单词判断条件和字符个数回到初始值*/ 27 } 28 } 29 for(n=max; n>=1; --n){ /*以下循环体是打印单词的垂直直方图,如果单词数组没有赋值星号,则打印空格*/ 30 for(m=1; m<=nw; ++m){ 31 if(a[m][n]=='*'){ 32 printf("%c ", a[m][n]); 33 } 34 else 35 printf(" "); 36 } 37 printf("\n"); 38 } 39 for(i=1; i<=nw; ++i) /*以下循环体是打印单词个数*/ 40 printf("%d ", p[i]); 41 printf("\n"); 42 system("pause"); 43 return 0;
1 练习题1-14 编写一个程序,打印输入中各个字符出现频率的直方图。 2 int main() 3 { 4 char c,a[25][25]; //定义一个二维数组,想象成一个25厘米方框,横竖各25个1里面小方框。 5 int m,n,i,s,x; //定义一些变量,有点随意,下面用到详细讲解。 6 m=n=s=x=0; 7 i = 'A'; 8 for (;i<='Z';i++) //第一步要先把26个字母写进二维数组当中,m和n都是0 也就是大方框的最底层 是 26个字母 9 { 10 a[m][n] = i; //这一步是循环赋值, 11 m++; //列+1 我感觉这样理解更好 虽然在网上看到a[][]的第一个参数是行,但是很难受,所以现在它是列。 12 } 13 while ((c=getchar()) != EOF) //这是读取参数了 14 { 15 if (c>='a'&&c<='z') //这里如果是小写就转换为大写 16 { 17 c = c - 32; //小写-32就是大写,百度的,C如果是一个小写字母,对应的ASCII的数字-32,就是大写的 18 } //这一步主要是为了匹配上面的,因为在上面存入的时候是大写,所以必须大写才对的上 19 printf("%c",c); //试验输出 20 m=0; //这一步好像没用,应该没用 21 for (x='A';x<=c;x++) //循环26个字母 22 { 23 if (c==a[m][n]) //如果发现和二维数组中的字母一样 24 { 25 if (a[m][n+1] != '*') //如果二维数组中字母所对应的列的第一个不是* ,如果这句话不好理解,那就自己画个方框图。 26 { 27 a[m][n+1] = '*'; //那就把二维数组的那个位置放上*号 28 } 29 else //如果字母对应的列的第一个是*号 30 { 31 for (n=2; a[m][n] == '*'; n++) //那循环查询 初始化字母对应的列的第二个2,调节为是否等于*号,满足就n++。 32 { 33 s = n; // 这里保存最高出现的字母次数。 34 } 35 a[m][n] = '*'; //这里是个小技巧,好吧,这只是我认为的我发现的菜鸟级别的,如果上面循环不满足了,那就让a[m][n]=*号, 36 } //意思是发现这一列的第一个已经有*号了,那就继续寻找,找到没有*号的那一个位置,添加*号。如果没理解,自行脑补。 37 n=0; //让行重新等于0,方便下一次查找。 38 } 39 else{ 40 m++; //如果没找到列就+1,寻找下一列 41 } 42 } 43 } 44 for (n=s;n>=0;--n) //循环打印,行等于出现频率最高的那一行。 45 { 46 for(m=0;m<26;++m) //列一共是26列。 47 { 48 if(a[m][n]=='*' ) //如果出现*才打印 49 { 50 printf("%c ", a[m][n]); //每个隔两个空格 51 } 52 else 53 printf(" "); //如果没有就打三个空格 54 } 55 printf("\n"); //最高列的那一行打完了,就换行。 56 } 57 for (m=0;m<26;m++) //打印26个字母,起初尝试在上面循环一起打印了,发现出现乱码,不知道是什么东西,很尴尬。 58 printf("%c ",a[m][0]); 59 }
效果如下

1.7函数
函数为计算的封装提供一种简便的方法,使用函数时不需要去考虑他是如何实现的。在出现main函数之前要声明语句,下面少些了
,如下,表明函数power函数有两个int类型的参数,并返回一个int类型的值,这种声明称为函数原型,它必须与power函数定义和用
法一致。如果函数的定义/用法与函数原型不一致,将出现错误。函数原型与函数声明中参数名不要求相同。事实上,函数原型中的参数名
是可选的,例如:int power(int, int);
1 int main() 2 { 3 int i; 4 for (i = 0; i < 10; ++i) 5 printf("%d %d %d\n", i, power(2,i), power(-3,i)); //调用函数 6 return 0; 7 } 8 int power(int base, int n) 9 { 10 int i, p; 11 p = 1; 12 for (i = 1; i<= n; ++i) //累加相乘 13 p = p * base; 14 return p; //返回结果 15 }
函数定义的一般形式为:
返回值类型 函数名(0个或多个参数声明)
{
声明部分
语句序列
}
power函数的参数使用的名字只在power函数内部有效,对其他任何函数都是不可见的,其他函数可以使用相同的参数名不会引起冲突。函数定义中圆括号
内列表中出现的变量称为形式参数,对应的值为实际参数。power函数通过return返回结果,形式为 return 表达式;
函数不一定有返回值。没有return语句的表达式将控制权返回给调用者,不返回有用的值。等同于函数到达右终结花括号时,函数就“到达尽头”。
主函数可以忽略函数返回值。
main函数末尾的return语句,是向其调用者返回一个值,该调用者实际上是程序的执行环境。一般来说,返回值为0表示正常终止,返回非0表示出现
异常情况,或出错结束调节。
练习1-15函数版本的温度转换。在main函数中调用即可。
1 void tem() 2 { 3 float fahr, celsius; 4 float lower, upper, step; 5 upper = 300; 6 step = 20; 7 lower = -50; 8 celsius = upper; 9 while ( celsius>lower ) 10 { 11 fahr = celsius*9.0/5.0+32.0; 12 printf("华氏温度是%.2f\t摄氏温度%.2f\n",fahr, celsius); 13 celsius = celsius - step; 14 }
1.8 参数--传值调用
在C语言中,被调用函数不能直接修改主调函数中变量的值,而只能修改其私有的临时副本的值。传值调用利大于弊。在被调用函数中,
参数可以看作是便于初始化的局部变量,因此额外使用的变量更少。程序更紧凑简洁。必要时,也可以让函数能够修改主调函数中的变量。
这种情况需要调用者想被调用函数提供设置变量的地址(地址就是指向变量的指针),被调用函数需要将对应的参数声明为指针类型,并
通过它简洁访问变量。如果是数组参数,当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址--它并不复制数组元素本身。
在被调用函数中,勀通过数组下标访问或修改数组元素的值。
1.9 字符数组
1 #define MAXLINE 1000 2 int getline(char line[], int maxline); 3 void copy(char to[], char from[]); 4 int main() 5 { 6 int len; 7 int max; 8 char line[MAXLINE]; 9 char longest[MAXLINE]; 10 max = 0; 11 while ((len = getline(line, MAXLINE)) > 0) 12 if (len > max) 13 { 14 max = len; 15 copy(longest, line); 16 } 17 if (max > 0) 18 printf("%s", longest); 19 return 0; 20 } 21 22 int getline(char s[],int lim) 23 { 24 int c, i; 25 for (i=0; i < lim-1 && (c=getchar()) != EOF && c!='\n'; ++i) 26 s[i] = c; 27 if (c == '\n') 28 { 29 s[i] = c; 30 ++i; 31 } 32 s[i] = '\0'; 33 return i; 34 } 35 void copy(char to[], char from[]) 36 { 37 int i; 38 i = 0; 39 while ((to[i] = from[i]) != '\0') 40 ++i; 41 }
上面copy函数返回值类型为void,说明该函数不返回任何值。getline函数把字符'\0'(即空字符,其值为0)插入到她创建的数组的末尾,以标记字符串
的结束。这一约定已被C语言采用:当在C语言程序中出现类似于"hello\0"的字符串常量时,它将以字符数组的形式存储,数组的各元素分别存储字符串的
各个字符并以'\0'标志字符串的结束。printf函数中格式规范%s规定,对应的参数必须是以这种形式表示的字符串。copy函数的实现正是依赖于输入参数
由'\0'结束这一事实,它将'\0'拷贝到输出参数中。(也就是说空字符'\0'不是普通文本的一部分.)
1 练习题1-16 修改打印最长文本的程序的主程序main,使之可以打印任意长度的输入行的长度,并尽可能多的打印文本。在执行这程序时发现,printf("%3d:%s",len,line);就不在执行剩下main函数
里面的其他代码拉,这个原因到目前为止我不知道先写下来。
2 int main() 3 { 4 int len; 5 int max; 6 char line[MAXLINE]; 7 char longest[MAXLINE]; 8 max = 0; 9 while ((len = getline(line, MAXLINE)) > 0) 10 printf("%3d:%s",len,line); //输出最多三位数的数字,和字符串 11 if (len > max) 12 { 13 max = len; 14 copy(longest, line); 15 } 16 if (max > 0) 17 printf("%s", longest); 18 //getchar(); //继续获得输入的字符信息 19 }
1 1-17打印长度大于80个字符的所有输入行 2 int main() 3 { 4 int len; 5 int max; 6 char line[MAXLINE]; 7 char longest[MAXLINE]; 8 max = 0; 9 while ((len = getline(line, MAXLINE)) > 0) 10 //printf("aaaa"); //输出最多三位数的数字,和字符串 11 if (len > max ) 12 { 13 max = len; 14 copy(longest, line); 15 if (len > 80) //如果大于80就打印 16 printf("%d:%s",len,line); 17 18 } 19 if (max > 0) 20 printf("%s", longest); 21 //getchar(); //继续获得输入的字符信息 22 }
练习1-18 编写删除行末尾的空格及制表符,并删除完全是空格的行。
1 #define MAXLINE 100 2 int getline(char line[], int maxline); 3 void copy(char to[], char from[], int max); 4 int main() 5 { 6 int len; 7 int max; 8 char line[MAXLINE]; 9 char longest[MAXLINE]; 10 max = 0; 11 while ((len = getline(line, MAXLINE)) > 0) 12 { 13 max = len; //这里不再去判读 14 copy(longest, line, max); //都进行copy操作,并且传入新的参数,字符数量 15 printf("%s", longest); //这里输出 16 } 17 } 18 int getline(char s[],int lim) 19 { 20 int c, i; 21 for (i=0; i < lim-1 && (c=getchar()) != EOF && c!='\n'; ++i) 22 s[i] = c; 23 if (c == '\n') 24 { 25 s[i] = c; 26 ++i; 27 } 28 s[i] = '\0'; 29 return i; 30 } 31 void copy(char to[], char from[], int max) 32 { 33 int i,x; //定义两个变量 34 i = x = max-2; //获得最大数,也就是存储的字母的个数,-2操作是因为在输入时,做了添加换行和结束符的最后两位 35 for (;from[i] == ' ' || from[i] == '\t'; i--) //在末尾循环查找是不是有空格和制表符 36 {} 37 if (i==-1) //如果发现到最后都没有,这里注意,是-1的原因是上面循环发现from[0]依然是空格,然后继续操作-1,那就说明一整行都是空格 38 { 39 to[0] = 0; //这里意思就是是个null,啥都没有,也不会打出什么来 40 } 41 else 42 { 43 x = i; //如果有字符存在 那就从字母的下标开始 44 to[x+2] = '\0'; //因为上面的原因这里要先给to[]这个字符数组加上换行 45 to[x+1] = '\n'; // 加上结束符 46 while (to[x]=from[i]) //循环写入 47 { 48 i--; //因为是倒序 所有两个数字--操作,好像有点冗余了,冗余一个变量无所谓了。 49 x--; 50 } 51 } 52 }
练习1-19
1 #define MAXLINE 100 2 int getline(char line[], int maxline); 3 void copy(char to[], char from[], int max); 4 void reverse(char to[],char s[]); 5 int main() 6 { 7 int len; 8 int max; 9 char line[MAXLINE]; 10 char longest[MAXLINE]; 11 max = 0; 12 int z; 13 z=1; 14 while ((len = getline(line, MAXLINE)) > 0) 15 { 16 max = len; //这里不再去判读 17 //copy(longest, line, max); //都进行copy操作,并且传入新的参数,字符数量 18 if (z%2==0) //如果对2取余数为0 19 { 20 reverse(longest, line); //反正操作 21 } 22 else 23 { 24 copy(longest,line,max); //不然就复制 25 } 26 z++; 27 printf("%s", longest); //这里输出 28 } 29 } 30 int getline(char s[],int lim) 31 { 32 int c, i; 33 for (i=0; i < lim-1 && (c=getchar()) != EOF && c!='\n'; ++i) 34 s[i] = c; 35 if (c == '\n') 36 { 37 s[i] = c; 38 ++i; 39 } 40 s[i] = '\0'; 41 return i; 42 } 43 void copy(char to[], char from[], int max) 44 { 45 int i,x; //定义两个变量 46 i = x = max-2; //获得最大数,也就是存储的字母的个数,-2操作是因为在输入时,做了添加换行和结束符的最后两位 47 for (;from[i] == ' ' || from[i] == '\t'; i--) //在末尾循环查找是不是有空格和制表符 48 {} 49 if (i==-1) //如果发现到最后都没有,这里注意,是-1的原因是上面循环发现from[0]依然是空格,然后继续操作-1,那就说明一整行都是空格 50 { 51 to[0] = 0; //这里意思就是是个null,啥都没有,也不会打出什么来 52 } 53 else 54 { 55 x = i; //如果有字符存在 那就从字母的下标开始 56 to[x+2] = '\0'; //因为上面的原因这里要先给to[]这个字符数组加上换行 57 to[x+1] = '\n'; // 加上结束符 58 while (to[x]=from[i]) //循环写入 59 { 60 i--; //因为是倒序 所有两个数字--操作,好像有点冗余了,冗余一个变量无所谓了。 61 x--; 62 } 63 } 64 } 65 void reverse(char to[], char s[]) 66 { 67 int i,x; 68 i = x = 0; 69 for (i=0;s[i] != '\0';i++) 70 {} 71 i = i-2; //-2是因为后面有'\n','\0' 72 for (;i>=0;i--) 73 { 74 to[x] = s[i]; //to字符集正向操作,s字符集反向 基本和一个题不差什么 75 x++; 76 } 77 to[x+1] = '\n'; 78 to[x+2] = '\0'; 79 }
1.10 外部变量与作用域
局部变量只在函数内有作用,或称为自动变量。自动变量只在函数调用执行期间存在,如果两次调用,不保留前次调用时的赋值,且每次进入函数时都要显式赋值。如果没有复制,其中存放是无效值。
外部变量,所有函数都可以访问,函数间可以通过外部变量交换数据,不必使用参数表。外部变量在程序执行期间一直存在,即使在对外部变量赋值的函数返回后,这些变量仍然保持原的值不变。外部变量必须定义在所有函数之外,且只能定义一次,定义后编译程序将为它分配存储单元。每个需要访问外部变量的函数中,必须声明外部变量,说明其类型,声明时可以用extern语句显式声明,也可以通过上下文隐式声明。某些情况下可以省略extern声明。在源文件中,如果外部变量的定义出现在使用它的函数之前,那么就不需要使用extern声明。通常的做法是所有的外部变量都放在源文件中的开始处。如果程序包含在多个源文件中,例如在file1中定义在file2和file3中使用,那么在file2和file3中使用exter声明来建立该变量与其定义之间的联系。人们通常把变量和函数的extern声明放在一个单独的文件中(头文件),并在每个源文件的开头使用#include语句把所要用的头文件包含进来。后缀名.h约定为头文件名的扩展名。
这一节关于外部变量使用定义(define)与声明(declaration)这两个词。”定义“表示创建变量或分配存储单元,而”声明“指的是说明变量的性质,但并不分配存储单元。
过分依赖外部变量会导致值一定的风险,因为它会使程序中的数据关系模糊不清--外部变量的值可能会被意外地或不经意地修改,而程序修改又变得十分困难。
练习1-20 编写程序detab,将输入中的制表符替换成适当数目的空格,比如出现制表符,那就用8个空格代替,当然是连字符一起的意思,比如有三个字符,那就打5个空格,要是有8个字符,那就打8个空格,
要是有9个字符,那就打7个空格,原因是已8为分界,9呢被分成8+1,那1个字符后面跟7个空格,10字符呢,就打6个空格,也就是保证一件事,字符加上空格一定是8的倍数.这里有个重要的问题,如何在键盘上输入
'\t'制表符 ,使用键盘的Tab键 2 void tetab() 3 { 4 int c,pos,i,nbs; 5 pos = 1; 6 nbs = 0; 7 while((c=getchar())!=EOF){ 8 if(c == '\t'){ 9 nbs = (DETAB-pos%DETAB)%DETAB+1; //这个公式是整段代码的重点,小于8的个数或者大于8的个数都会补齐应有空格. 10 i = 0; 11 for(;i<nbs;i++){ 12 putchar(' '); 13 } 14 pos += nbs; 15 }else if(c == '\n'){ 16 putchar(c); 17 pos = 1; 18 nbs = 0; 19 }else{ 20 pos++; 21 putchar(c); 22 } 23 } 24 }
1 1-20 尝试新的代码,更容易理解,测试是可行的. 2 void tetab() 3 { 4 int c,pos,i,nbs; 5 pos = 0; 6 nbs = 0; 7 while((c=getchar())!=EOF){ 8 if(c == '\t'){ 9 nbs = DETAB-pos%DETAB; //8- 字母个数取余8,得到的是补足8的个数的空格.尝试是同样的效果. 10 i = 0; 11 for(;i<nbs;i++){ 12 putchar(' '); 13 } 14 pos += nbs; 15 }else if(c == '\n'){ 16 putchar(c); 17 pos = 0; 18 nbs = 0; 19 }else{ 20 pos++; 21 putchar(c); 22 } 23 } 24 }
练习1-21,我的理解是如果出现多个空格或者一个制表符,那就把它变成一个空格.
1 void entab(){ 2 int i,x,z; 3 char c[50],s[50],q[50]; 4 i = x = z = 0; 5 while ((c[i] = getchar()) != EOF) 6 { 7 i++; //记录字符的个数 8 } 9 for (;x<=i;x++) //循环查看字符是什么类型 10 { 11 if (c[x] == '\t') //如果发现有制表符,那就替换为空格 12 { 13 s[z] = ' '; 14 z++; 15 } 16 else 17 { 18 s[z] = c[x]; //其他不做操作复制到新的字符集中. 19 z++; 20 } 21 } 22 x = z = 0; 23 for (;x<=i;x++) //二次循环目的是把所有空格变成一个空格 24 { 25 if (s[x] == ' ') //如果发现空格 26 { 27 for (; s[x] == ' '; ++x) {} //查看空格的后面是否依然是空格 28 x--; //这一步是因为上一步已经加到不是空格的字符了如果补-1,那就一个空格都没有了. 29 q[z] = s[x]; //这个和上一步对应,也就是出现多个空格只留下一个,如果不能很好的理解,就去debug看一下 30 z++; 31 } 32 else 33 { 34 q[z] = s[x]; //和上面循环中操作一样 35 z++; 36 } 37 } 38 q[z-1] = '\0'; //这里操作是为了添加结束符,为什么-1,debug看一下. 39 printf("%s",q); //输出打印 40 }
这个1-22的效果。

练习1-22 编写一个可以把过长的行分为多行的程序,要求是每行就N个数字,并且分割是要在N个数字之前的空格,因为这样分割出来比较正常。我在上面那贴图,方便了解。
1 void multi_line() 2 { 3 char c,s[300]; //定义数组来储存字符 4 int a,b,x,z,j; //定义一些零时变量 5 a = b = x = z = j = 0; //初始化 6 while ((c = getchar()) != EOF) //获得字符 7 { 8 s[a] = c; //储存字符 9 a++; 10 } 11 b = a/10; //根据题意要求我设计的是10个字符就换行,这里除以10得到可以分几组,比如40个字符,那就四组 12 for (;x <= b;x++) //循环,次数等于分组的次数,实际上比分组多一次。 13 { 14 for (;s[j] == ' ' && j<a;j++) //如果是[0]等于空格,并且小于最大字符数,j++ 15 {} //这一步的目的是消除每一行最前前面的所有空格 16 z = j + 10; //这一步目的是获得从每行第一个字母开始,数10个字符的下标 17 if (z>=a) //这一步做判断如果上面的数+10大于最大字符数了,那就让他等于最大字符数。 18 { //这里的原因是在这样的比如我有53个字符,53/10得到5,因为要消除一些可能存在的头尾的空格 19 z = a; //而且循环是6次,所以z的数字,一定会大于53;所以在这里做限制 20 } 21 if (s[z] == ' ') //判断分组的最后是不是有空格,如果有消除掉 22 { 23 for (;s[z] != ' ';z--) 24 {} 25 for (;j<z;j++) //消除完成循环输出 26 { 27 printf("%c",s[j]); 28 } 29 printf("\n"); //输出完以10个数为分组的行之后打印换行 30 } 31 else //如果分组的末尾没有空格 32 { 33 if (z < a) //如果z小于最大字符数,就查看这一分组的末尾是否有空格 34 { //这里的目的是担心到最后那几个字母末尾没有空格,如果执行下面去空格操作的话会去掉最后整行的字符 35 for (; s[z] != ' '; z--) //所以在上面做判断操作,如果有点迷惑,可以使用debug把if去掉剩下for循环这一行,效果就会出来 36 {} //上面for循环的主要目的是查找每一行固定列数的最近的空格之前的位置来达到题目效果要求。 37 } 38 for (;j<z;j++) //和上面一样处理完毕之后循环输出 39 { 40 printf("%c",s[j]); 41 } 42 printf("\n"); 43 } 44 if (a - z > 10 && b - x == 1) //这里是担心循环次数不够完全输出完所有行,所以在这里做一个判断,判断最大数减去当前数是不是 45 { //大于10,和剩下的循环次数是不是1,如果是就加循环次数。 46 b++; 47 } 48 //下面这一行没有用,但是写下了,说不定谁看到会有需要。 49 // if ( j >= a ) //如果j > a 也就是循环用的下标大于最大字符数了,我不知大为什么它会大于最大数字,很奇怪 50 // { 51 // break; //终止程序 52 // } 53 } 54 }
练习1-23 去掉代码中的注释,包括多行注释
1 void eliminate_then_annotation() 2 { 3 char c[10000],s[10000]; 4 int i,z,x; 5 i = z = x = 0; 6 while ((c[i]=getchar()) != EOF) 7 { 8 i++; 9 } 10 for (;z<i;z++) 11 { 12 if (c[z] == '/' && c[z+1] == '/') //查看是否有两个斜杠存在 13 { 14 for (;c[z] != '\n';z++) 15 {} 16 } 17 if (c[z] == '/' && c[z+1] == '*') /*是否有这种注释的存在*/ 18 { 19 for (;z<i;z++) //循环 20 { 21 if (c[z] == '\n' && c[z-1] == '/' && c[z-2] == '*') //查找是否存在这种情况 22 { 23 break; //存在就终止 24 } 25 } 26 } 27 s[x] = c[z]; 28 x++; 29 } 30 //s[x] = '\0'; 31 printf("%s",s); 32 }
1-24 符号那边是有错误的,可以查看有多少个括号不够
1 void find_grammatical_errors() 2 { 3 char c[MAX]; 4 char string[10]; 5 int a,s,d,i,z; 6 a = s = d = i = z = 0; 7 while ((c[i]=getchar()) != EOF) 8 {i++;} 9 for (;a<i;a++) 10 { 11 if (c[a] == '(') 12 {s++;} //小括号 13 if (c[a] == '[') 14 {d++;} //中括号 15 if (c[a] == '{') 16 {z++;} //大括号 17 if (c[a] == ')') 18 {s--;} 19 if (c[a] == ']') 20 {d--;} 21 if (c[a] == '}') 22 {z--;} 23 if (c[a] == "‘") 24 {c[a] = "'";} 25 if (c[a] == "’") 26 {c[a] = "'";} 27 if (c[a] == "“") 28 {c[a] = '"';} 29 if (c[a] == "”") 30 {c[a] = '"';} 31 } 32 printf("%s",c); 33 a = 0; 34 if (s >= 1) 35 {printf("少拉后半部小括号%d个\n",s);} 36 if (d >= 1) 37 {printf("少拉后半部中括号%d个\n",d);} 38 if (z >= 1) 39 {printf("少拉后半部大括号%d个\n",z);} 40 if (s < 0) 41 {printf("少拉前半部小括号%d个\n",-s);} 42 if (d < 0) 43 {printf("少拉前半部中括号%d个\n",-d);} 44 if (z < 0) 45 {printf("少拉前半部大括号%d个\n",-z);} 46 }
第一章到此结束


浙公网安备 33010602011771号