为什么阅读《C Primer Plus》第六版
准备好好研究下redis源码,但是很久没用c语言写代码了,平时工作主要用java和js。
所以准备重新阅读学习下c语言经典书籍:C Primer Plus。
更好地阅读redis源码。
第6章和第7章 循环 分支和跳转
略
第8章 字符输入/输出和输入验证
单字符I/O getchar()和putchar()
/* echo.c -- 重复输入 */
#include <stdio.h>
int main(void)
{
char ch;
while ((ch = getchar()) != '#')
putchar(ch);
return 0;
}
- ANSI C标准: getchar,putchar -> stdio.h
- getchar和putchar都不是真正的函数,是宏。
缓冲区
为什么有缓冲区?
一个块进行传输比逐个发送字符节约时间,打错还可修正,按下Enter键,才是真正的输出。
- 完全缓冲IO: 缓冲区被填满才刷新缓冲区,通常出现在文件输入中
- 行缓冲IO: 出现换行符时刷新缓冲区
结束符
#define EOF (-1)
判断是否文件结尾,检测返回EOF,如果是EOF表示是文件结尾。
大部分系统有办法通过键盘模拟文件结尾条件EOF
注意以下几点:
- 不用定义EOF,stdio.h统一定义
- 不用担心EOF的实际值。
- ch是整数不会影响putchar(),该函数仍然会打印等价的字符。
- 模拟输入EOF字符,不能是-1,大部分unix和linux是Ctrl+D,还有的是Ctrl+Z
重定向和文件
<符合 重定向输入 >符号重定向输出
- ./echo < my.txt my.txt重定向为输入
- ./echo > my.log my.log重定向输出
组合重定向:./echo < my.txt > my.log
顺序无关,但是文件名不能相同。
总之:
- 使用重定向运算符不能读取多个文件的输入,也不能输出到多个文件。
- 重定向运算符连接一个可执行程序和一个数据文件,不能用于两个数据文件之间,也不能两个程序之间。
- 文件名和程序名之间的空格不是必须的。
输入验证
实际应用中,用户经常输入错误,需要做验证。
scanf返回成功读取项的个数。
scanf("%ld",&n) == 1 判断用户输入一个整数时是否正确。
第9章 函数
如何组织程序?C的设计思想是,把函数用作构件块。
首先,什么是函数?函数是完成特定任务的独立程序代码单元。
语法规则定义了函数的结构和使用方式。虽然C中的函数和其他语言中的函数、子程序、过程作用相同,但是细节上略有不同。
一些函数执行某些动作,如printf()把数据打印到屏幕上;
一些函数找出一个值供程序使用,如strlen()把指定字符串的长度返回给程序。
一般而言,函数可以同时具备以上两种功能。
创建简单函数
/* lethead1.c */
#include <stdio.h>
#define NAME "GIGATHINK, INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA 94904"
#define WIDTH 40
void starbar(void); /* 函数原型 */
int main(void)
{
starbar();
printf("%s\n", NAME);
printf("%s\n", ADDRESS);
printf("%s\n", PLACE);
starbar(); /* 使用函数 */
return 0;
}
void starbar(void) /* 定义函数 */
{
int count;
for (count = 1; count <= WIDTH; count++)
putchar('*');
putchar('\n');
}
程序输出
****************************************
GIGATHINK, INC.
101 Megabuck Plaza
Megapolis, CA 94904
****************************************
- 程序在3处使用了starbar标识符:函数原型告诉编译器函数starbar()的类型;函数调用表明在此处执行函数;函数定义明确地指定了函数要做什么。
- 函数和变量一样,有多种类型。
- 一般而言,函数原型指明了函数的返回值类型和函数接受的参数类型。这些信息称为该函数的签名。对于starbar()函数而言,其签名是该函数没有返回值,没有参数。
- 程序把starbar()原型置于main()的前面。当然,也可以放在main()里面的声明变量处。放在哪个位置都可以。
- 函数头中的starbar()后面没有分号,告诉编译器这是定义starbar(),而不是调用函数或声明函数原型。
- 程序把starbar()和main()放在一个文件中。也可以把它们分别放在两个文件中。如果把函数放在一个单独的文件中,要把#define和#include指令也放入该文件。
- starbar()函数中的变量count是局部变量(local variable),意思是该变量只属于starbar()函数。可以在程序中的其他地方(包括main()中)使用count,这不会引起名称冲突,它们是同名的不同变量。
函数参数
void show_n_char(char ch, int num)
该行告知编译器show_n_char()使用两个参数ch和num,ch是char类型,num是int类型。这两个变量被称为形式参数,简称形参。和定义在函数中变量一样,形式参数也是局部变量,属该函数私有。这意味着在其他函数中使用同名变量不会引起名称冲突。每次调用函数,就会给这些变量赋值。
在使用函数之前,要用ANSI C形式声明函数原型,不过可以省略变量名
void show_n_char(char, int);
调用函数
show_n_char(SPACE, 12);
被调函数不知道也不关心传入的数值是来自常量、变量还是一般表达式。
因为被调函数使用的值是从主调函数中拷贝而来,所以无论被调函数对拷贝数据进行什么操作,都不会影响主调函数中的原始数据
使用return从函数中返回值
编译多源代码文件的程序
- 下面的命令将编译两个文件并生成一个名为a.out的可执行文件
cc file1.c file2.c
- 如果后来改动了file1.c,而file2.c不变,可以使用以下命令编译第1个文件,并与第2个文件的目标代码合并
cc file1.c file2.o
使用头文件
文件的头文件定义例子:
/* hotel.h -- 符号常量和 hotel.c 中所有函数的原型 */
#define QUIT 5
#define HOTEL1 180.00
#define HOTEL2 225.00
#define HOTEL3 255.00
#define HOTEL4 355.00
#define DISCOUNT 0.95
#define STARS "**********************************"
// 显示选择列表
int menu(void);
// 返回预订天数
int getnights(void);
// 根据费率、入住天数计算费用
// 并显示结果
void showprice(double rate, int nights);
指针简介
什么是指针?从根本上看,指针是一个值为内存地址的变量(或数据对象)。
假设一个指针变量名是ptr,可以编写如下语句:
ptr = &bah;// 把bah的地址赋给ptr
现在ptr的值是bah的地址。
nurse = 22;
ptr = &nurse; // 指向nurse的指针
val = *ptr; // 把ptr指向的地址上的值赋给val
声明指针
int * pi; // pi是指向int类型变量的指针
char * pc; // pc是指向char类型变量的指针
float * pf, * pg; // pf、pg都是指向float类型变量的指针
使用指针在函数间通信
/* swap3.c -- 使用指针解决交换函数的问题 */
#include <stdio.h>
void interchange(int * u, int * v);
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
interchange(&x, &y); // 把地址发送给函数
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int * u, int * v)
{
int temp;
temp = *u; // temp获得 u 所指向对象的值
*u = *v;
*v = temp;
}
输出
Originally x = 5 and y = 10.
Now x = 10 and y = 5.
- interchange(&x, &y); 该函数传递的不是x和y的值,而是它们的地址。
网友评论