C语言程序设计学习笔记
文章平均质量分 56
作为工作八年的程序员重新系统学习C语言程序设计
学习书籍:C程序设计语言(第二版),Brian W.Kernighan Dennis M.Ritchie 著 徐宝文 李志 译
对书中的知识点进行总结,思考和分享
哲思天下
星星之火,未必燎原,即使能点亮一个火把,也能照亮一片天
展开
-
C程序设计语言(K&R第二版):练习5-8
题目:用指针方式代替数组下标方式,改写函数day_of_year和month_day。自我解答:day_of_year和month_day的原型如下:int day_of_year(int year, int month, int day){ int i, leap; leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0; for(i = 1; i < month; i++原创 2022-01-29 15:36:47 · 923 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-8
题目:函数day_of_year和month_day中没有进行错误检查,请解决该问题。书中关于这两个函数的定义如下:#include <stdio.h>static char daytab[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};int day_of_year(int原创 2022-01-29 14:44:40 · 379 阅读 · 0 评论 -
C语言中的数组和指针
首先看一维数组的定义和使用int array[5] = {1, 2, 3, 4, 5};array是具有5个整型元素的一维数组,5个元素连续存放在以数组名array为起始的内存空间中。用数组名访问:访问数组array中的元素,可以直接用数组名访问,例如array[0]代表数组中的第一个元素,array[3]代表数组中的第4个元素;array和array[0]的地址是一样的,都代表这个数组的起始地址。#include <stdio.h>int main(){原创 2022-01-23 23:48:58 · 492 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-7
题目:重写函数readlines,将输入的文本行存储到由main函数提供的一个数组中,而不是存储到调用alloc分配的存储空间中。该函数的运行速度比改写前快多少?自我解答:首先来看书中对文本排序功能实现的程序:排序过程分为了三个步骤:1. 读取所有输入行 2. 对文本行进行排序 3. 按次序打印文本行源码如下:#include <stdio.h>#include <string.h>#define MAXLINES 5000 /* ma..原创 2022-01-14 13:58:56 · 405 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-6
题目:采用指针而非数组索引的方式改写前面章节和练习中的某些程序,例如getline(第1、4章),atoi、itoa以及它们的变体形式(第2,3,4章),reverse(第3章),strindex、getop(第4章)等等。自我解答:4.1中的getline函数如下#define MAXLINE 1000/* get a line and return the length of the line */int getline(char s[], int lim){原创 2022-01-12 09:53:03 · 398 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-5
题目:实现库函数strncpy、strncat和strncmp,它们最多对参数字符串中的前n个字符进行操作。例如,函数strccpy(s, t, n)将t中最多n个字符复制到s中。更详细的说明详见附录B。自我解答:对于strncpy的实现编程思路:把字符串t中的前n个字符拷贝到s中,如果t的字符数小于n,则拷贝到\0为止。void strncpy(char *s, char *t, int n){ while(n--) if(*t) *原创 2022-01-11 20:50:46 · 536 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-4
题目:编写函数strend(s,t)。如果字符串t出现在字符串s的尾部,则函数返回1;否则返回0.自我解答:编程思想:先获得s和t的结尾位置,并得到t的长度,在s中按照从后向前的顺序依次进行比较。int strend(char *s, char *t){ int tLen; while(*s) s++; tLen = 0; while(*t) { t++; tLen++; } whil原创 2022-01-10 09:31:42 · 636 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-3
题目:用指针方式实现第2章中的函数strcat。函数strcat(s,t)将t指向的字符串复制到s指向的字符串的尾部。自我解答:编程思想是先找到字符串s的结尾,然后依次复制字符串t的内容到s的结尾void strcat(char *s, char *t){ while(*s++); /* find the end of the s */ s--; while(*s++ = *t++); /* copy t to the end of s */}原创 2022-01-09 22:12:42 · 444 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-2
题目:模仿函数getint的实现方法,编写一个读取浮点数的getfloat函数。getfloat函数的返回值应该是什么类型?自我解答:书本中getint的实现方法如下:int getint(int *pn){ int c, sign; while(isspace(c = getch())); if(!isdigit(c) && c != EOF && c != '+' && c != '-') {原创 2022-01-08 23:21:32 · 472 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习5-1
题目:在上面的例子中,如果符号+或-的后面紧跟的不是数字,getint函数将把符号视为数字0的有效表达式。修改该函数,将这种形式的+或-符号重新写回到输入流中。自我解答:书本中的getint程序如下:int getint(int *pn){ int c, sign; while(isspace(c = getch())); if(!isdigit(c) && c != EOF && c != '+' && c !=原创 2022-01-08 22:27:53 · 599 阅读 · 0 评论 -
C程序设计语言(K&R第二版):5.2关于getint函数的理解
#include <stdio.h>#define SIZE 10int main(){ int n, array[SIZE] = {0}, getint(int *); for(n = 0; n < SIZE && getint(&array[n]) != EOF; n++) { printf("%d\n", array[n]); } }#include <cty.原创 2022-01-05 23:15:55 · 2748 阅读 · 3 评论 -
C程序设计语言(K&R 第二版)chapter4-小知识点
1. 书中写到:如果函数定义中省略了返回类型,则默认返回int型。 在使用gcc编译是,如果省略了返回类型,则默认返回void无返回类型,而不是int类型2. 在gcc中,return后面不跟表达式的情况只能用在void函数中,并且在void类型的函数中只能用return语句,而不能再return之后加任何表达式。3. 如果函数之间需要共享大量的变量,使用外部变量要比使用一个很长的参数列表更方便、有效。但是,在第一章中已经指出,这样做必须非常谨慎,因为这种方式可能对程序结构产生不良的影响,..原创 2021-12-27 17:38:15 · 417 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-14
题目:定义宏swap(t, x, y)以交换t类型的两个参数。(使用程序块结构会对你有所帮助)自我解答:#include <stdio.h>#define swap(t, x, y) t temp = x;\ x = y;\ y = temp;#define dprint(expr) printf(#expr " = %g\n", expr)int main(){原创 2021-12-27 17:37:59 · 414 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-13
题目:编写一个递归版本reverse函数,以将字符串s倒置。自我解答:书中的reverse函数为#include <string.h>void reverse(char s[]){ int c, i, j; for(i = 0, j = strlen(s) - 1; i < j; i++, j--) { c = s[i]; s[i] = s[j]; s[j] = c; }}采用递原创 2021-12-27 15:36:47 · 542 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-12
题目:运用printd函数的设计思想编写一个递归版本的itoa函数,即通过递归调用把整数转换成字符串。自我解答:3.6节中itoa函数的定义如下:void itoa(int n, char s[]){ int i, sign; if((sign = n) < 0) n = -n; i = 0; do { s[i++] = n % 10 + '0'; }while((n / 10) > 0) if(原创 2021-12-27 12:42:22 · 507 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-11
题目:修改getop函数,使其不必使用ungetch函数。提示:可以使用一个static类型的内部变量解决该问题自我解答:先看书中的getop函数:#include <stdio.h>#include <ctype.h>#include "calc.h"int getop(char s[]){ int i, c; while((s[0] = c = getch()) == ' ' || c == '\t'); s[1] = '\0原创 2021-12-26 22:32:19 · 476 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-10
题目:另一种方法是通过getline函数读入整个输入行,这种情况下可以不使用getch和ungetch函数。请运用这一方法修改计算器程序。自我解答:getline函数可以一次读入一行,然后把这一行的内容保存到数组中,这样就可以通过控制数组的下标来获取输入中的运算符或操作数。源程序如下:#include <stdio.h>#include <stdlib.h> /* to use atof() */#define MAXOP 10原创 2021-12-26 01:31:01 · 454 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-9
题目:以上介绍的getch与ungetch函数不能正确地处理压回的EOF。考虑压回EOF时应该如何处理?请实现你的方案。自我解答:getch和ungetch不能正确处理EOF的原因是,EOF的值是-1,而buf定义的类型是char型,在某些类型的计算机中,char定义的unsigned类型。所以一个简单的解决方法是把char buf[BUFSIZE] 数组的类型定义为int型。参考答案:解决方法亦是把数组buf的类型声明为int型(程序省略)在原先的getch和ungetch函数原创 2021-12-25 14:34:57 · 298 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-8
题目:假定最多只压回一个字符。请相应修改getch与ungetch这两个函数。自我解答:一种简单的做法是把BUFSIZE的大小定义为1即可。另外一种做法是不定义数组buf[BUFSIZE],而定义一个char型变量。4-8 假定最多只压回一个字符。请相应修改getch与ungetch这两个函数。4-9 以上介绍的getch与ungetch函数不能正确地处理压回的EOF。考虑压回EOF时应该如何处理?请实现你的方案。4-10 另一种方法是通过getline函数读入整个输入行原创 2021-12-24 23:00:57 · 411 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-7
题目:编写一个函数ungets(s), 将整个字符串s压回到输入中。ungets函数需要使用buf和bufp吗?它能否仅用ungetch函数?自我解答:ungetch函数实现了把一个字符压回到输入中,所以把字符串s压入到输入中可以选择调用ungetch函数而避开直接操作buf和bufp。void ungets(char s[]){ int i; for(i = 0; s[i] != '\0'; i++) ungetch(s[i]);}参考答案:原创 2021-12-24 14:33:38 · 527 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-6
题目:给计算器程序增加处理变量的命令(提供26个具有英文字母变量名的变量很容易)。增加一个变量存放最近打印的值。没理解题目具体含义,直接上参考答案4-7 编写一个函数ungets(s), 将整个字符串s压回到输入中。ungets函数需要使用buf和bufp吗?它能否仅用ungetch函数?4-8 假定最多只压回一个字符。请相应修改getch与ungetch这两个函数。4-9 以上介绍的getch与ungetch函数不能正确地处理压回的EOF。考虑压回EOF时应该如何处理?原创 2021-12-20 17:49:53 · 266 阅读 · 0 评论 -
C程序设计语言(K&R第二版):练习4-5
题目给计算器程序增加访问sin、exp与pow等库函数的操作。有关这些库函数的详细信息,参见附录B.4节中的头文件<math.h>自我解答:从附录B.4节中得知sin和exp需要一个操作数,pow需要两个操作数。所以如果用逆波兰表达式:1 2 + 2 sin + 代表的是1 + 2 + sin2....原创 2021-12-14 09:38:37 · 1319 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习4-4
题目:在栈操作中添加几个命令,分别用于在不弹出元素的情况下打印栈顶元素;复制栈顶元素;交换栈顶两个元素的值。另外增加一个命令用于清空栈。自我解答:void printTop(void){ if(sp > 0) printf("the top element of the stack is %f\n", val[sp - 1]); else printf("error: stack empty");}double copyTop(void原创 2021-11-30 09:16:52 · 590 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习4-3
题目: 在有了基本框架后,对计算器程序进行扩展就比较容易了。在该程序中加入取模(%)运算符,并注意考虑负数的情况。自我解答:本题的关键在于:1. 怎么区分“-”是操作数还是符号。在逆波兰表达式中,如果-后面紧跟一个数字,则“-”代表负号;如果“-”之后没有紧跟一个数字,则“-”代表减号。例如:1 -1 + 中的“-”代表负号,1 1 -中的“-”代表减号。2. 怎么对浮点数求%,%运算符只能用于int型数据,不能用于浮点数。参考答案中是使用了标准库math.h中的fmod函数进行的。主要原创 2021-11-29 10:28:07 · 320 阅读 · 0 评论 -
C程序设计语言(K&R 第二版)之计算器程序
书中实现了一个逆波兰表示法的计算器程序,把要点整理如下。程序思想是:每个操作数都被依次压入到栈中,当一个运算符到达时,从栈中弹出相应数目的操作数(对二目运算符来说是两个操作数),把该运算符作用于弹出的操作数,并把运算结果再压入到栈中。程序结构:while(下一个运算符或操作数不是文件结束指示符) if(是数) 将该数压入到栈中 else if(is运算符) 弹出所需数目的操作数 ...原创 2021-11-28 22:20:01 · 563 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习4-2
题目:对atof函数进行扩充,使它可以处理形如 123.45e-6的科学表示法,其中,浮点数后面可能会紧跟一个e或者E以及一个指数(可能有正负号)自我解答:首先看下书本中的atof函数:#include <stdio.h>#include <ctype.h>double atof(char s[]){ int i, sign; double var, power; for(i = 0; isspace(s[i]); i++)原创 2021-11-25 23:35:51 · 210 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习4-1
题目:编写函数strrindex(s,t),它返回字符串t在s中最右边出现的位置。如果s中不包含t,则返回-1.自我解答:先看4.1中和第一章中的两个getline函数:第1章中的getline函数如下:#define MAXLINE 1000/* get a line and return the length of the line */int getline(char s[], int lim){ int c, i; for(i =原创 2021-11-24 18:06:44 · 832 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习3-6
题目:修改itoa函数,使得该函数可以接收三个参数。其中,第三个参数为最小字符宽度。为了保证转换后所得的结果至少具有第三个参数指定的最小宽度,在必要时在所得结果的左边填充一定的空格。书中的itoa函数为:#include <stdio.h>#include <string.h> void itoa(int n, char s[]){ int i, sign; if((sign = n) < 0) n = -n;原创 2021-11-21 22:20:13 · 328 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习3-3
题目:编写函数expand(s1, s2),将字符串s1中类似于a-z一类的速记符号在字符串s2中扩展为等价的完整列表abc...xyz。该函数可以处理大小写字母和数字,并可以处理a-b-c、a-z0-9 与 -a-z等类似的情况。作为前导和尾随的-字符原样排印。...原创 2021-11-21 21:59:18 · 441 阅读 · 2 评论 -
C程序设计语言(K&R 第二版):练习3-5
题目 编写函数itob(n, s, b),将整数n转换为以b为底的数,并将转换结果以字符的形式保存到字符串s中。例如itob(n, s, 16)把整数n格式化成十六进制整数保存在s中。自我解答:参照练习3-5中的思路,以b为底即代表不断对b求余数,需要注意的一点是考虑十六进制的情况,当余数大于9是用字母a~f来替代。解答如下:#include <stdio.h>#include <string.h>#define abs(x) ((x) < .原创 2021-11-21 21:57:08 · 612 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习3-4
题目:在数对二的补码表示中,我们编写的itoa函数不能处理最大的负数,即n等于-(2^(字长-1))的情况。请解释原因。修改该函数,使它在任何机器上运行时都能打印出正确的值。书本中itoa函数如下:#include <stdio.h>#include <string.h> void itoa(int n, char s[]){ int i, sign; if((sign = n) < 0) n = -n;原创 2021-11-21 13:49:39 · 267 阅读 · 0 评论 -
排序算法之shell排序
关于shell排序算法的介绍参见:图解排序算法(二)之希尔排序 - dreamcatcher-cx - 博客园书中3.5节中关于shell排序的实现如下:void shellsort(int v[], int n){ int gap, i, j, temp; for(gap = n/2; gap > 0; gap /= 2) for(i = gap; i < n; i++) for(j = i - ga原创 2021-11-18 17:39:16 · 360 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习3-2
题目:编写一个函数escape(s,t),将字符串t复制到字符串s中,并在复制过程中将换行符制表符等不可见字符分别转换为\n、\t等相应的可见的转义字符序列。要求使用switch语句。再编写一个具有相反功能的函数,在复制过程中将转义字符序列转换为实际字符。自我解答:#include <stdio.h> void escape(char s[], const char t[]){ int i = 0, j = 0; while(t[j] != '\0')原创 2021-11-18 00:07:41 · 391 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习3-1
题目:在上面有关折半查找的例子中kwhile循环语句内共执行了两次测试,其实只要一次就足够(代价是将更多的测试在循环外执行)。重写该函数,使得在循环内部只执行一次测试。比较两种版本函数的运行时间。书中源代码为:int binsearch(int x, int v[], int n){ int low, high, mid; low = 0; high = n - 1; while(low <= high) { mid = (l原创 2021-11-17 16:31:05 · 313 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习2-10
题目:重新编写将大写字母转换为小写字母的函数lower,并用条件表达式替代其中的if-else结构。自我解答:书中的if-esle原程序为int lower(int c){ if(c >= 'A' && c <= 'Z') return c + 'a' - 'A'; else return c;}用条件表达式改写后为:int lower(int c){ return (c > 'A'原创 2021-11-12 15:50:20 · 458 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习2-9
题目:在求对二的补码时,表达式x &= (x - 1)可以删除x中最右边值为1的一个二进制位。请解释这样做的道理。用这一方法重写bitcount函数,以加快其执行速度。自我解答:二进制只有0和1,一个二进制数在减1时,如果最低位是1,则减1后这一位变为0,而其余位不变;当最低位是0时,由于要借位,这时需要找到最右边的1进行借位,导致这一位变为0,这一位之后的位都变为0,例如:1010100 减1之后变为 1010011,然后再和原数据与得到1010000,这样就能看到新的数把最右边的1.原创 2021-11-12 09:27:52 · 548 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习2-8
题目:编写一个函数rightrot(x, n),该函数返回将x循环右移(即从最右端移出的位将从最左端移入)n(二进制)位后所得到的值。自我解答:解题思想:循环实现,循环次数为n, 每循环一次把移出的位记录并左移31位,然后和原来的数按位或。#include <stdio.h>unsigned int rightrot(unsigned int x, int n){ int i; for(i = 0; i < n; i++) {原创 2021-11-11 23:09:44 · 529 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习2-7
题目:编写一个函数invert(x, p, n),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位求反(即1变0,0变1),x的其余各位保持不变。自我解答:假设x = 110 101 010,p = 5, n = 3首先获得第p位开始的n个位的取反值: 先对x取反即~x,这时需要把除~x中除第p到p+1-n的位置0,~x & ((~0 << (p+1-n)) & (~(~0 << (p+1)))),即000 010...原创 2021-11-11 22:53:09 · 367 阅读 · 0 评论 -
C程序设计语言(K&R 第二版):练习2-6
题目:编写一个函数setbits(x, p, n, y),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。自我解答:#include <stdio.h>unsigned int setbits(unsigned int x, int p, int n, unsigned int y){ return (~(~0 << n) & y) << (p - n + 1)原创 2021-11-11 22:05:29 · 435 阅读 · 0 评论 -
C语言中按位或|和按位与&的常用用法
顾名思义,按位或和按位与是指对二进制数字中对位的操作。按位或和按位与一般在对寄存器的操作中使用广泛,以32位系统为例,一个寄存器一般有32bit,这些bit一般会分成不同的位域,代表不同的功能。在程序中一般会通过控制这些位域来完成某一具体的功能。程序在控制某一位域时,我们不期望对其他位域产生影响,而使用|和&能方便的完成这个功能。上图展示了一个16位寄存器的典型结构,划分为了不同的位域代表不同的含义,以bit4和bit5DTYPE为例,这一位域也许代表着不同的数据类型。程序中也许有一..原创 2021-11-11 12:45:40 · 3276 阅读 · 0 评论