名字的作用域是程序中可以使用该名字的部分。在函数中声明的自动变量来说,作用域就是该变量的声明函数。不同函数中声明的具有相同名字的各个局部变量之间没有任何关系。函数的参数也是这样,实际上可以将它看作是局部变量。外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。
如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性地使用关键字extern。
变量声明用于说明变量的属性(主要是变量的类型)。而变量的定义初次意外还将引起存储器的分配。·
一个外部变量只能在某个文件中定义一次,定义外部变量的源文件中也可以包含对该外部变量的extern声明,,外部变量的定义中必须制定数组的长度,但external声明则不一定要制定数组长度。
外部变量的初始化只能出现在其定义中。
静态变量:
静态变量用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译器文件的声誉部分。
register声明:
register声明告诉编译器,它所声明的变量在程序中使用频率较高,将其放在机器的寄存器中。这样可以使程序更小,执行速度更快。
register声明只只用于自动变量以及函数的形式参数。
实际使用中,每个函数只有恨少的变量可以保存在寄存器中,且值允许某些类型的变量。编译器可以忽略过量的或不支持的寄存器变量。
程序块结构:
变量的声明(包括初始化)除了可以紧跟在函数开始的花括号之后,还可以紧跟在任何其他表示复合语句开始的左花括号之后。以这种方式声明的变量可以隐藏程序块外与之同名的变量,它们之间没有任何关系。并在与左花括号对应的右花括号出现之前一直存在。
初始化
在不进行初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的值则没有定义。
外部变量和静态变量初始化表达式必须是常亮表达式,且只初始化一次。
自动变量与寄存器变量,在每次进入函数或程序块时都将被初始化。初始化表达式可以不是常亮表达式。
数组的初始化可以在声明的后面紧跟一个初始化表达式列表,初始化表达式列表用花括号括起来,各初始化表达式之间通过逗号分隔。当省略数组的长度时,编译器将把花括号中初始化表达式的个数作为数组的长度。
如果初始化表达式的个数比数组元素数少,则对外部变量、静态变量和自动变量来说,没有初始化表达式的元素将被初始化为0、如果初始化表达式的个数比数组元素多,则是错误的。不能一次将一个初始化表达式指定给多个数组元素,也不能跳过前面的数组元素而直接初始化后面的数组元素。
字符串数组的初始化比较特殊:可以用一个字符串来代替用花括号括起来并用逗号分隔的初始化表达式序列。
递归
函数可以递归调用,即函数可以直接或间接调用自身,每次调用都会得到一个与以前的自动变量集合不同的心得自动变量集合。
这里的例题不算是很难,想通其中关键就好,我当时就是卡在一个很坑的地方。就是中间元素最开始是被移动到第0个元素的位置的,然后以中间元素的值为标准左右大小排序好后是需要把这个基准值移动回去的,我就是卡在移动回去的话,与之交换的值不是比其小的值。
但是其实是与之交换的值是最后一个比基准值小的元素,所以交换后基准值左侧的会比基准值都要小。
例子中swap函数的作用是将第一个参数数组的第二个参数的元素与第三个参数的元素交换。说起来绕嘴其实就是数组中两个元素交换。
首先是终止运行的判断。小于两个只有一个元素没有比较比较。
if (left >= right)
return;
然后是将中间的基准值与最左侧的元素交换,让最左侧元素参与对比排序。
swap(v ,left,(left + right) / 2);
将目前判断的代表最左侧元素的值保存。用于后面的排序。
last = left;
然后是for语句循环从left+1开始到right递增。left + 1是因为要将已经是基准值的最左侧元素跳过去。
for (i = left + 1; i <= right; i++)
然后是每个元素都与基准值比较,如果比基准值大就交换位置。然后用last去记录最后一个已经交换好的位置。
if (v[i] < v[left])
swap(v, ++last, i);
然后将基准数恢复到它原来的位置,这样一次交换就完成了。
后面的引用自身就是将以刚刚的基准值划分的两部分分别继续排序。
下面的SWAP函数没啥好说的,很简单。
C预处理器
预处理就是形成可执行文件之前的一个处理手段。
文件包含指令(#include指令)将已经有的定义和声明(通常是其他文件)包含进本文件中。#include “文件名”和#include <文件名>
如果文件名用引号括起来,则在源文件所在位置查找该文件,如果在该位置没有找到文件,或文件名是用尖括号(<>)括起来的,则将根据气筒的定义查找该文件,被包含的文件本身也可包含#include指令。
在大的程序中,#include指令是将所有生命捆绑在一起的较好的方法。它保证所有的源文件都具有相同的定义与变量生命,这样可以避免出现一些不必要的错误。如果某个包含文件的内容发生了变化,偶有依赖于该包含文件的源文件都必须重新编译。
宏替换
宏定义:#define 名字 替换文本
后续所有出现名字的地方都将被替换为替换文本#define指令中的名字与变量名的命名方式相同,替换文本可以是任意字符串。
替换文本是#define指令行尾部所有的剩余部分内容,单也可以是一个较长的宏定义分成若干行,这时需要在待续的行末尾加上一个反斜杠\。#define指令定义的名字的作用域从其定义点开始,到被编译的源文件的末尾处结束。宏定义也可以使用前面的宏定义,替换只对记号进行,对括在引号中的字符串不起作用。
宏定义可以带参数,这样对不同的宏调用使用不同的替换文本。
如#define max(A, B) ((A) > (B) ? (A) : (B))
max(p+q, r+s)就把这个想象成函数定义就好。p+q和r+s是参数。
把其中的p+q代替A r+s代替B。
可以使用#undef取消名字的宏定义,这样可以保证后续的调用时函数调用,而不是宏调用。
下面有个引用的情况。
参数名(也就是宏的参数) 在替换文本的中,前以#作为前缀的参数,将变为由实际参数(也就是宏名称带的参数,并且加上引号变为字符串。其他不变。)其中的符号都会加上\转义变成普通字符,不具有意义。
预处理运算符##将两个参数合并成一个参数,符号前后空格省略,之后介绍这个。我也不知道有什么卵用。
条件包含
(书上的感叹号真的需要吐槽一下,就是一个丨,不仔细看根本不知道是啥。)
书上写了defined(名字)能确定该名字是否已经被定义,如果已定义则值为1,如果未定义值为0.
而#if对之后的表达式求值,如果非0就包含之后的语句。
这里用的是感叹号!+defined(HDR)。
意思就是如果未包含(因为感叹号!)HDR则包含之后的语句。直到遇到#endif、#elif或#else语句之后终止包含。
网友评论