🍍 字符串函数
type
status
date
slug
summary
tags
category
icon
password
Property
 
🍍
目录

 
C库提供了多个处理字符串的函数,ANSI C把这些函数的原型放在string.h头文件中。常用的函数有strlen()strcat()strcmp()strncmp()strcpy()strncpy()。另外,还有sprintf()函数,其原型在stdio.h头文件中
 

strlen()

strlen()函数用于统计字符串的长度
 
🍍 命令行参数
type
status
date
slug
summary
tags
category
icon
password
Property
 
假设一个文件中有一个名为fuss的程序。在UNIX环境中运行该程序的命令行是:
或者在Windows命令提示模式下是:
命令行参数(command-line argument)是同一行的附加项:
一个C程序可以读取并使用这些附加项
notion image
notion image
 
🥭 作用域、链接、存储周期
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
C提供了多种不同的模型或存储类别(storage class)在内存中储存数据。
目前所有编程示例中使用的数据都储存在内存中。从硬件方面来看,被储存的每个值都占用一定的物理内存,C 语言把这样的一块内存称为对象(object)。对象可以储存一个或多个值。一个对象可能并未储存实际的值,但是它在储存适当的值时一定具有相应的大小(面向对象编程中的对象指的是类对象,其定义包括数据和允许对数据进行的操作,C不是面向对象编程语言)。
从软件方面来看,程序需要一种方法访问对象。这可以通过声明变量来完成:
该声明创建了一个名为entity的标识符。标识符是一个名称,在这种情况下,标识符可以用来指定特定对象的内容。标 识符遵循变量的命名规则。在该例中,标识符entity即是软件(即C程序)指定硬件内存中的对象的方式。该声明还提供了储存在对象中的值。
变量名不是指定对象的唯一途径。考虑下面的声明:
第1行声明中,pt是一个标识符,它指定了一个储存地址的对象。但是,表达式*pt不是标识符,因为它不是一个名称。然而,它确实指定了一个对象,在这种情况下,它与 entity 指定的对象相同。一般而言,那些指定对象的表达式被称为左值。所以,entity既是标识符也是左值;*pt既是表达式也是左值。按照这个思路,ranks + 2 * entity既不是标识符(不是名称),也不是左值(它不指定内存位置上的内容)。但是表达式*(ranks + 2 * entity)是一个左值,因为它的确指定了特定内存位置的值,即ranks数组的第7个元素。顺带一提,ranks的声明创建了一个可容纳10个int类型元素的对象,该数组的每个元素也是一个对象。
所有这些示例中,如果可以使用左值改变对象中的值,该左值就是一个可修改的左值。现在,考虑下面的声明:
🥭 存储类别
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
C 使用作用域、链接和存储期为变量定义了多种存储方案。
notion image
 

自动变量

属于自动存储类别的变量具有自动存储期、块作用域且无链接。默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。为了更清楚地表达你的意图(例如,为了表明有意覆盖一个外部变量定义,或者强调不要把该变量改为其他存储类别),可以显式使用关键字auto,如下所示:
关键字auto是存储类别说明符(storage-class specifier)。auto关键字在C++中的用法完全不同,如果编写C/C++兼容的程序,最好不要使用auto作为存储类别说明符。
 
块作用域和无链接意味着只有在变量定义所在的块中才能通过变量名访问该变量(当然,参数用于传递变量的值和地址给另一个函数,但是这是间接的方法)。另一个函数可以使用同名变量,但是该变量是储存在不同内存位置上的另一个变量。
变量具有自动存储期意味着,程序在进入该变量声明所在的块时变量存在,程序在退出该块时变量消失。原来该变量占用的内存位置现在可做他用。
🥭 随机数函数和静态变量
type
status
date
slug
summary
tags
category
icon
password
Property
 
来看一个使用内部链接的静态变量的函数:随机数函数。ANSI C库提供了rand()函数生成随机数。生成随机数有多种算法,ANSI C允许C实现针对特定机器使用最佳算法。然而,ANSI C标准还提供了一个可移植的标准算法,在不同系统中生成相同的随机数。实际上,rand()是“伪随机数生成器”,意思是可预测生成数字的实际序列。但是,数字在其取值范围内均匀分布
 
为了看清楚程序内部的情况,使用可移植的ANSI版本,而不是编译器内置的rand()函数。可移植版本的方案开始于一个“种子”数字。该函数使用该种子生成新的数,这个新数又成为新的种子。然后,新种子可用于生成更新的种子,以此类推。该方案要行之有效,随机数函数必须记录它上一次被调用时所使用的种子。这里需要一个静态变量。
静态变量next的初始值是1,其值在每次调用rand0()函数时都会被修改(通过魔术公式)。该函数是用于返回一个0~32767之间的值。注意,next是具有内部链接的静态变量(并非无链接)。这是为了方便稍后扩展本例,供同一个文件中的其他函数共享。
这两次的输出完全相同,这体现了“伪随机”的一个方面。每次主程序运行,都开始于相同的种子1。可以引入另一个函数srand1()重置种子来解决这个问题。关键是要让next成为只供rand1()srand1()访问的内部链接静态变量(srand1()相当于C库中的srand()函数)。把srand1()加入rand1()所在的文件中。
注意,next是具有内部链接的文件作用域静态变量。这意味着rand1()和srand1()都可以使用它,但是其他文件中的函数无法访问它。
设置seed的值为1,输出的结果与前面程序相同。但是设置seed的值为513后就得到了新的结果。 注意 自动重置种子如果 C 实现允许访问一些可变的量(如,时钟系统),可以用这些值(可能会被截断)初始化种子值。例如,ANSI C有一个time()函数返回系统时间。虽然时间单元因系统而异,但是重点是该返回值是一个可进行运算的类型,而且其值随着时间变化而变化。time()返回值的类型名是time_t,具体类型与系统有关。这没关系,我们可以使用强制类型转换:
🥭 分配内存:malloc()和free()
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
存储类别有一个共同之处:在确定用哪种存储类别后,根据已制定好的内存管理规则,将自动选择其作用域和存储期。然而,还有更灵活地选择,即用库函数分配和管理内存。
所有程序都必须预留足够的内存来储存程序使用的数据。这些内存中有些是自动分配的。例如,以下声明:
为一个float类型的值和一个字符串预留了足够的内存,或者可以显式指定分配一定数量的内存:
该声明预留了100个内存位置,每个位置都用于储存int类型的值。声明还为内存提供了一个标识符。因此,可以使用x或place识别数据。回忆一下,静态数据在程序载入内存时分配,而自动数据在程序执行块时分配,并在程序离开该块时销毁。
 
C 能做的不止这些。可以在程序运行时分配更多的内存。主要的工具是malloc()函数,该函数接受一个参数:所需的内存字节数。malloc()函数会找到合适的空闲内存块,这样的内存是匿名的。也就是说, malloc()分配内存,但是不会为其赋名。然而,它确实返回动态分配内存块的首字节地址。因此,可以把该地址赋给一个指针变量,并使用指针访问这块内存。因为char表示1字节,malloc()的返回类型通常被定义为指向char的指针。
然而,从ANSI C标准开始,C使用一个新的类型:指向void的指针。该类型相当于一个“通用指针”。malloc()函数可用于返回指向数组的指针、指向结构的指针等,所以通常该函数的返回值会被强制转换为匹配的类型。在ANSI C中,应该坚持使用强制类型转换,提高代码的可读性。然而,把指向 void的指针赋给任意类型的指针完全不用考虑类型匹配的问题。如果 malloc()分配内存失败,将返回空指针。
 
🥭 类型限定符
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
我们通常用类型和存储类别来描述一个变量。
C90 还新增了两个属性:恒常性(constancy)和易变性(volatility)。这两个属性可以分别用关键字const 和 volatile 来声明,以这两个关键字创建的类型是限定类型。
C99标准新增了第3个限定符:restrict,用于提高编译器优化。
C11标准新增了第4个限定符:_Atomic。C11提供一个可选库,由stdatomic.h管理,以支持并发程序设计,而且_Atomic是可选支持项。 C99 为类型限定符增加了一个新属性:它们现在是幂等的!这个属性听起来很强大,其实意思是可以在一条声明中多 次使用同一个限定符,多余的限定符将被忽略:
有了这个新属性,就可以编写类似下面的代码:
 

const类型限定符

以const关键字声明的对象,其值不能通过赋值或递增、递减来修改。在ANSI兼容的编译器中:
🍎 文件输入输出
type
status
date
slug
summary
tags
category
icon
password
Property
 
🍎

 
 

文件通信

有时,需要程序从文件中读取信息或把信息写入文件。这种程序与文件交互的形式就是文件重定向这种方法很简单,但是有一定限制。例如,假设要编写一个交互程序,询问用户书名并把完整的书名列表保存在文件中。如果使用重定向,应该类似于:
用户的输入被重定向到 bklist 中。这样做不仅会把不符合要求的文本写入 bklist,而且用户也看不到要回答什么问题。
C提供了更强大的文件通信方法,可以在程序中打开文件,然后使用特殊的I/O函数读取文件中的信息或把信息写入文件。
🍏 建立结构声明
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
结构声明(structure declaration)描述了一个结构的组织布局。声明类似下面这样:
该声明描述了一个由两个字符数组和一个float类型变量组成的结构。
该声明并未创建实际的数据对象,只描述了该对象由什么组成。〔有时把结构声明称为模板,因为它勾勒出结构是如何储存数据的〕
关键字 struct,它表明跟在其后的是一个结构,后面是一个可选的标记(该例中是 book),稍后程序中可以使用该标记引用该结构。所以在后面的程序中可以这样声明:
这把library声明为一个使用book结构布局的结构变量。
结构的标记名是可选的。但是(在一处定义结构布局,在另一处定义实际的结构变量),必须使用标记。
 
在结构声明中,用一对花括号括起来的是结构成员列表。每个成员都用自己的声明来描述。例如,title部分是一个内含MAXTITL个元素的char类型数组。成员可以是任意一种C的数据类型,甚至可以是其他结构!
🍏 定义结构变量
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
 
结构有两层含义。一层含义是“结构布局”。结构布局告诉编译器如何表示数据,但是它并未让编译器为数据分配空间。下一步是创建一个结构变量,即是结构的另一层含义。程序中创建结构变量:
编译器执行这行代码便创建了一个结构变量library。编译器使用book模板为该变量分配空间:一个内含MAXTITL个元素的char数组、一个内含MAXAUTL个元素的char数组和一个float类型的变量。这些存储空间都与一个名称library结合在一起
notion image
 
在结构变量的声明中,struct book所起的作用相当于一般声明中的int或float。例如,可以定义两个struct book类型的变量,或者甚至是指向struct book类型结构的指针:
结构变量doyle和panshin中都包含title、author和value部分。指针ptbook可以指向doyle、panshin或任何其他book类型的结构变量。从本质上看,book结构声明创建了一个名为struct book的新类型。
就计算机而言:
🍏 结构数组
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
显然,每本书的基本信息都可以用一个 book 类型的结构变量来表示。为描述两本书,需要使用两个变量,以此类推。可以使用这一类型的结构数组来处理多本书。
notion image
 

声明结构数组

声明结构数组和声明其他类型的数组类似。下面是一个声明结构数组的例子:
以上代码把library声明为一个内含MAXBKS个元素的数组。数组的每个元素都是一个book类型的数组。因此,library[0]是第1个book类型的结构变量,library[1]是第2个book类型的结构变量,以此类推。
notion image

标识结构数组的成员

🍏 嵌套结构
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
有时,在一个结构中包含另一个结构(即嵌套结构)很方便。例如,Shalala Pirosky创建了一个有关她朋友信息的结构。显然,结构中需要一个成员表示朋友的姓名。然而,名字可以用一个数组来表示,其中包含名和姓这两个成员
notion image
首先,注意如何在结构声明中创建嵌套结构。和声明int类型变量一样,进行简单的声明:
该声明表明handle是一个struct name类型的变量。当然,文件中也应包含结构names的声明。
其次,注意如何访问嵌套结构的成员,这需要使用两次点运算符:
从左往右解释fellow.handle.first: