C程序设计语言(The C Programming Language)读书笔记

这篇笔记涵盖了C语言的基础,包括变量、表达式、控制流、函数、数组、指针、结构体等内容。深入讲解了输入/输出、文件操作、预处理器和类型转换。还介绍了UNIX系统接口中的文件描述符、I/O操作和内存管理。笔记适合初学者,旨在建立扎实的C语言编程基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

C程序设计语言 笔记

1 导言

1.1 入门

1.2 变量与算术表达式

1.3 for语句

1.4 符号常量

#define指令可以把符号名(或称符号常量)转换为特定的字符串:

#define 名字 替换文本

1.5 字符输入/输出

文本流时多行字符构成的字符序列,行末是一个换行符

c = getchar(): 从输入流读入下一个字符

putchar( c ):打印一个字符

1.5.1 文本复制
#include <stdio.h>
main
{
int c;
while((c=getchar())!=EOF)
	putchar(c);
}

注意,c声明为int是因为它必须足够大,除了存储字符外还要存储EOF(-1)

1.5.2 字符计数
1.5.3 行计数
1.5.4 单词计数

1.6 数组

1.7 函数

1.8 参数——传值调用

1.9 字符数组

C语言中的字符串常量以字符数组的形式存储,并以’\0’标志字符串的结束。printf函数中的格式规范%s规定对应的参数必须是以这种形式表示的字符串。

1.10 外部变量与作用域

定义:创建变量或分配存储单元

声明:说明变量性质,不分配存储单元

外部变量必须定义在所有函数之外,且只能声明一次,定义后编译程序将为它分配存储单元。在每个需要访问外部变量的函数中必须声明相应的外部变量。声明时可以用extern语句显式地声明,也可以通过上下文隐式声明(外部变量与使用它的函数定义在同一个源文件中,且定义在函数之前)。

2 类型、运算符与表达式

2.1 变量名

2.2 数据类型及长度

unsigned类型的数总是正值或0,并遵守算术模2^n定律,其中n是该类型占用的位数。

2.3 常量

字符串常量可以在编译时连接。

编译时可以将多个字符串常量连接起来:

printf("%s\n","AAA" "BBB");
// 等价于
printf("%s\n","AAABBB");

存储字符串的物理存储单元数比括在双引号中的字符数多一个(’\0’)。

2.4 声明

默认情况下外部变量和静态变量将被初始化为0,未经显式初始化的自动变量的值为未定义。

对数组而言,const限定符指定数组所有元素的值都不能被修改。

2.5 算术运算符

取模运算符%不能应用于float或double。

2.6 关系运算符与逻辑运算符

2.7 类型转换

两个操作数有不同的类型,运算前将“较低”类型提升为“较高”类型

2.8 自增运算符与自减运算符

2.9 按位运算符

2.10 赋值运算符与表达式

2.11 条件表达式

2.12 运算符优先级与求值次序

3 控制流

3.1 语句与程序块

3.2 if-else语句

3.3 else-if语句

int binsearch(int x,int v[],int n){
	int low,high,mid;

	low=0;
	high=n-1;
	while(low<=high){
		mid=(low+high)/2;
		if(x<v[mid])
			high=mid-1;
		else if(x>v[mid])
			low=mid+1;
		else
			return mid;
	}
	return -1;
}

3.4 switch语句

3.5 while循环与for循环

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-gap;j>=0&&v[j]>v[j+gap];j-=gap){
				temp=v[j];
				v[j]=v[j+gap];
				v[j+gap]=temp;
			}
}

3.6 do-while循环

3.7 break语句与continue语句

3.8 goto语句与标号

4 函数与程序结构

4.1 函数的基本知识

4.2 返回非整型值的函数

4.3 外部变量

4.4 作用域规则

外部变量或函数的作用域从声明它的地方开始,到其所在的文件的末尾结束。此外,如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性地使用关键字extern。

4.5 头文件

4.6 静态变量

用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。因此,通过static限定外部对象,可以达到隐藏外部对象的目的。

static也可以用于声明内部变量,static类型的内部变量同自动变量一样,是某个特定函数的局部变量,只能在该函数中使用,但与自动变量不同的是,不管其所在函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。

4.7 寄存器变量

register声明用于告诉编译器该变量在程序中使用频率较高,建议编译器将其放在寄存器中。

无论寄存器变量实际上是不是存放在寄存器中,它的地址都是不能访问的。

4.8 程序块结构

4.9 初始化

在不进行显式初始化的情况下,外部变量和静态变量都将初始化为0,而自动变量和寄存器变量的初始值则没有定义。对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只能初始化一次,对于自动变量与寄存器变量,则在每次进入函数或程序块时都将被初始化。

当使用字符串常量初始化字符数组时,会在字符数组最后自动添加一个’\0’,即:

char pattern[] = "ould";

实际效果如下:

char pattern[] = {'o','u','l','d','\0'};

4.10 递归

void qsort(int v[], int left, int right){
	int i,last;
	void swap(int v[],int i,int j);

	if(left >= right){
		return ;
	}

	swap(v,left,(left + right)/2);
	last = left;
	for(i = left +1; i<= right; i++){
		if(v[i] < v[left]){
			swap(v, ++last,i);
		}
	}
	swap(v, left, last);
	qsort(v,left,last-1);
	qsort(v,last+1,right);
}

void swap(int v[], int i, int j){
	int temp;
	temp = v[i];
	v[i] = v[j];
	v[j] = temp;
}

4.11 C预处理器

4.11.1 文件包含

在使用#include预处理命令时,如果文件名用引号包括,则在源文件所在位置查找该文件,如果在该位置没有找到文件,或者如果文件名是用尖括号括起来的,则将根据相应的规则查找该文件,这个规则同具体的实现有关。被包含的文件本身也可包含#include指令。

4.11.2 宏替换

也可以把一个较长的宏定义分成若干行,这时需要在待续的行末尾加上一个反斜杠\。#define定义的名字的作用域从其定义点开始,到被编译的源文件的末尾结束。宏定义中也可以使用前面出现的宏。宏定义也可以带参数。

可以使用#undef指令取消名字的宏定义。

4.11.3 条件包含

5 函数与指针

5.1 指针与地址

5.2 指针与函数参数

5.3 指针与数组

当把数组名传递给一个函数时,实际上传递的是该数组第一个元素的地址。在函数形参定义中char s[]和char *s是等价的。

数组是常量,不能对数组进行修改,但是指针可以。

5.4 地址算术运算

指针与整数之间不能相互转换,但0是唯一例外,常量0可以赋值给指针,指针也可以和常量0进行比较。程序中经常用符号常量NULL代替0。

5.5 字符指针与函数

char amessage[]="now is the time"; /*定义一个数组*/
char *pmessage="now is the time"; /*定义一个指针*/

amessage是一个仅存放初始化字符串及空字符的一维数组,amessage始终指向同一存储位置。pmessage是一个指针指向一个字符串常量,之后可以修改指向地址。

5.6 指针数组以及指向指针的指针

5.7 多维数组

5.8 指针数组的初始化

5.9指针与多维数组

5.10 命令行参数

main(int argc, char *argv[])

第一个参数表示运行程序时命令行参数数目

第二个参数时指向字符串指针的数组,其中每个字符串对于一个参数

argv[0]时启动程序的程序名

5.11 指向函数的指针

// 定义
int (*comp)(void *, void *)

//错误,该函数返回一个指向int类型的指针
int *comp(void *, void *)

// 调用
if((*comp)(v[i],v[left]) < 0)

5.12 复杂声明

6 结构

6.1 结构的基本知识

struct声明定义了一种数据类型:

struct {...} x,y,z;

struct初始化:

struct point maxpt = {320,200};

可以通过如下形式引用某个特定结构中的成员:

结构名.成员

6.2 结构与函数

通过结构的指针访问结构成员:

struct point *pp;
(*pp).x

因为结构指针的使用频度非常高,为了使用方便,C语言提供了一种简写方式,对于指向结构的指针p,可以用如下形式引用结构成员:

p->结构成员

6.3 结构数组

表达式

sizeof 对象

以及

sizeof (类型名)

将返回一个无符号整型值(size_t),它等于指定对象或类型占用的存储空间字节数。

6.4 指向结构的指针

6.5 自引用结构

自引用的结构定义 一个包含其自身实例的结构是非法的,但是在结构体重声明指向其自身类型的指针是合法的。

struct tnode {
	char *word;
	int count;
	struct tnode *left;
	struct tnode *right;
}

typedef:

typedef struct tnode *Treeptr;
typedef struct tnode{
	char *word;
	int count;
	Treeptr left; // struct tnode *left;
	Treeptr right; // struct tnode *right;
} Treenode;


Treeptr talloc(void){
	return (Treeptr)malloc(sizeof(Treenode));
}

6.6 表查找

6.7 类型定义(typedef)

6.8 联合

联合是可以(在不同时刻)保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对其要求。读取的类型必须是最近一次存入的类型。

联合是一个结构,它的所有成员相对基地址偏移量都为0,此结构空间要大到足够容纳最”宽“的成员,并且,其对齐方式要适合于联合中所有类型的成员。

联合只能用其第一个成员类型的值进行初始化。

6.9 位字段

位字段:直接定义和访问一个字中的位字段的能力,而不需要通过按位逻辑运算。位字段是字中相邻位的集合。

struct {	unsigned int is_keyword : 1;	unsigned int is_extern  : 1;	unsigned int is_static  : 1;}flags;

以上定义了一个变量flags,它包含3个一位的字段,冒号后的数字表示字段的宽度(二进制位数)。

7 输入与输出

7.1 标准输入/输出

prog < infile

使用<实现输入重定向,将键盘输入替换为文件输入

otherprog | prog

将otherprog的标准输出通过管道重定向到prog的标准输入

prog > outfile

将prog的输出从标准输出重定向到文件中

使用输入/输出库函数引用:

#include <stdio.h>

7.2 格式化输出-printf函数

int printf(char *format,arg1, arg2, ...)

在字符%和转换字符中间可能依次包含下列组成部分:

  • 负号,用于指定被转换的参数按照左对齐的方式输出
  • 数,用于指定最小字段宽度
  • 小数点,用于将字段宽度和精度分开
  • 数,用于指定精度,即指定字符串中要打印的最大字符数、浮点数小数点后的位数、整型最少输出的数字数目

7.3 变长参数表

int printf(char *fmt, ...)

宏va_start用于初始化指向第一个无名参数的指针,va_list用于定义指向每个无名参数的指针。

7.4 格式化输入-scanf函数

7.5 文件访问

文件指针,指向一个包含文件信息的结构,这些信息包括:缓冲区位置、缓冲区中当前字符的位置、文件的读或写状态、是否出错是否已经到达文件结尾等。

7.6 错误处理-stderr和exit

7.7 行输入和行输出

7.8 其他函数

8 UNIX系统接口

8.1 文件描述符

文件描述符与文件指针区别:

文件指针:C语言中使用文件指针做为I/O的句柄。文件指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括缓冲区和文件描述符。而文件描述符是文件描述符表的一个索引,也就是说c语言的文件指针是Linux系统中对文件描述符的一种封装。

文件指针

上面是文件指针指向的结构体,可以发现文件指针结构体里包含有文件描述符,说明文件指针是对文件描述符的一种封装。文件指针是C语言库里的提供的一个结构体,文件描述符是系统调用接口;

为什么系统已经有了文件描述符,库里面还要对其做一层封装呢?也是优点:一、方便程序员使用;二、可以提高程序的移植性。

FILE结构体里面还有缓冲区:

将数据写入硬盘文件中时,缓冲区的刷新方式默认为全缓冲,

将数据写入显示文件中,缓冲区的刷新方式默认为行缓冲;

而系统调用的函数write()写入时,是没有缓冲的,是因缓冲区是C库提供的,在FILE结构体里。文件描述符和缓冲区都是FILE结果体的成员,所以文件描述符指向的file结构体里是没有缓冲区的。

文件描述符:在Linux系统中打开文件就会获得文件描述符,它是个很小的正整数。每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针,已打开的文件在内核中用file结构体表示,文件描述符表中的指针指向file结构体。

文件描述符

标准输入(stdin)的文件描述符是 0

标准输出(stdout)的文件描述符是 1

标准错误(stderr)的文件描述符是 2

文件描述符的分配规则:从当前未被分配的最小整数处分匹配。

8.2 低级I/O-read和write

8.3 open、creat、close和unlink

8.4 随机访问-lseek

8.5 实例-fopen和getc函数的实现

8.6 实例-目录列表

在UNIX系统中,目录就是文件,它包含一个文件名列表和一些指示文件位置的信息。”位置“是一个指向其他表的(即i结点表)的索引。文件的i结点是存放除文件名以外的所有文件信息的地方。目录项通常仅包含两个条目:文件名和i结点编号。

8.7 实例-存储分配程序

malloc->sbrk

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值