\n换行
\t制表符
\a 响铃符 \\ 反斜杠
\b 回退符 \? 问号
\f 换页符 \' 单引号
\n 换行符 \" 双引号
\r 回车符 \ooo 八进制
\t 横向制表符 \xhh 十六进制
\v 纵向制表符
在运算符前后各放一个空格是运算组合更清楚,花括号的位置不重要,选择一种风格,并保持下去。
所有变量必须先说明后使用,说明通常放在函数开始处的的可执行语句之前。用于说明变量性质。
int,float类型的取值范围取决于所使用的机器。int型一般是16位取值在-32768至32767之间,也有32位表示的。float类型一般是32位,至少有6位有效数字,取值在 之间,printf是个通用输出格式函数,printf(“%d\t%d\n”,a,b);该函数的数目和类型都必须匹配,否则出错。printf函数其实不是c语言本身的一部分,C语言没有定义输入输出的功能。printf是标准函数库的一个有用函数,标准库函数一般在c中都可以用。相对于printf的函数是scanf函数。
如果某个算术运运算。算符的运算分量均为整数类型,那么执行整数。然而,如果某个算术运算符有一个浮点运算分量和一个整数运算分量,那么这个整数运算分量在开始之前会被转换成浮点型。5/9程序的结果是0. 5.0/9程序执行的是两个浮点数消除,不需要截取。
%d 打印十进制整数
%6d 打印十进制整数,至少6个字符宽
%f 打印浮点数
%6.2f 打印浮点数,至少6个字符宽,小数点后有2位小数
%o 表示八进制
%x 表示十六进制
%c 表示字符串
%s 表示字符串
%% 表示百分号%
#define 名字 替换文本
#include
#define LOWER 0 /* lower limitof table */
#define UPPER 300 /* upperlimit */
#define STEP 20 /* step size */
/* print FahrenheitCelsius
table */
main()
{
int fahr;
for (fahr = LOWER; fahr <=UPPER; fahr = fahr + STEP)
printf("%3d %6.1f\n",fahr, (5.0/9.0)*(fahr32));
}
1.5字符输入输出
标准库提供了一次读/写一个字符的函数,其中最简单的是getchar和putchar两个函数。每次调用时,getchar函数从文本流中读入下一个输入字符,并将其作为结果值返回。也就是说,在执行语句
C=getchar();之后,变量c中将包含输入流中的下一个字符。
每次调用putchar函数时将打印一个字符。
1.5.1 文件复制
读一个字符
While(该字符不是文件结束指示符)
读出刚读入的字符
读下一个字符
#include
/* copy input to output; 1stversion */
main()
{
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
}
这里有个问题?为什么c是int型。因为字符在键盘、屏幕或其他任何地方无论以什么形式表现,它在机器内部都是以位形式存储的。Char类型专门用于存储这种字符型数据,当然int型也可以用于存储字符型数据。
这里需要解决如何区分文件中有效数据与输入结束符的问题。C语言在没有输入是,getchar将返回一个特殊值,这个特殊值与任何实际的字符都不同,称为EOF(end of file)。在我们声明变量c的时候,必须让他能存放getchar函数返回的任何值。这里之所以不把c声明成char类型,是因为它必须足够大,除了能存储任何可能的字符外还要能存储文件结束符EOF。因此将c声明成int。
EOF定义在头文件
上面那个程序可改写成下面简练形式
#include
/* copy input to output; 2ndversion */
main()
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
这里c = getchar()需要有个括号,不然的话c = getchar() != EOF等于c = (getchar() != EOF),这不是我们想要的。
1.5.2字符计数
#include
/* count characters in input;1st version */
main()
{
long nc;
nc = 0;
while (getchar() != EOF)
++nc;
printf("%ld\n", nc);
}
Double类型可以处理更大的数字。
#include
/* count characters in input;2nd version */
main()
{
double nc;
for (nc = 0; gechar() != EOF;++nc)
;
printf("%.0f\n", nc);
}
%.0f表示强制不打印小数点和小数部分。因此小数部分的位数为0.
1.5.3行计数
#include
/* count lines in input */
main()
{
int c, nl;
nl = 0;
while ((c = getchar()) != EOF)
if (c == '\n')
++nl;
printf("%d\n", nl);
}
这里,单引号中的字符表示一个整型值,该值等于此字符在机器字符集中对应的数值,我们称之为字符常量。但是,它只不过是小的整型数的另一种写法而已。例如,’A’是一个字符常量,在ASCII字符集中,其值是65.
1.5.4单词计数
#include
#define IN 1 /* inside a word*/
#define OUT 0 /* outside a word*/
/* count lines, words, andcharacters in input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF){
++nc;
if (c == '\n')
++nl;
if (c == ' ' || c == '\n' || c = '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d\n",nl, nw, nc);
}
1.7函数
int power(int base, int n)
声明参数的类型、名字以及该函数返回的结果类型。Power函数的参数使用的名字只在power函数内部有效,对其他任何函数都是不可见的:其他函数可以使用与之相同的参数名字而不会引起冲突。变量i和p也是这样:power函数中的i和main函数中的i无关。
#include
int power(int m, int n);
/* test power function */
main()
{
int i;
for (i = 0; i < 10; ++i)
printf("%d %d %d\n", i, power(2,i), power(3,i));
return 0;
}
/* power: raise base to nth
power; n >= 0 */
int power(int base, int n)
{
int i, p;
p = 1;
for (i = 1; i <= n; ++i)
p = p * base;
return p;
}
我们常把函数定义中的圆括号内列表出现的变量称为形式参数,而把函数调用中与形参对于的值称为实际参数。
函数原型与函数声明中参数名可以不一样。其实,函数原型中的参数名都可以省略,可以写成以下形式:
int power(int,int)。但是合适的参数名能够很好的起到说明性的作用。
函数原型就是定义函数的时候,函数声明就是函数声明。
1.8参数—传值调用
在c语言中,被调用函数不能直接修改主调函数中变量的值,而只能修改其私有的临时副本的值。必要时,可以让函数能够修改主调函数的变量。这种情况下,调用者需要向被调用函数提供待设置值的变量的地址(从技术角度看,地址就是指向变量的指针),而被调用函数则需要将对应的参数声明为指针类型,并通过它间接访问变量。
如果是数字函数,情况有所不同。当把数组名作为参数时,传递给函数的值是数组起始元素的位置或地址—它并不是复制数组元素本身。在被调用函数中,可以通过数组下标访问或修改数组元素的值。
1.10.外部变量与作用域
函数内部的私自变量或者叫局部变量和其他函数内部的同名变量是没有关系的。这些局部变量只有在函数被调用的时候才存在,函数退出时消失。现在我们把这种局部变量称为自动变量。区分static存储类的局部变量。
由于自动变量只有在函数调用期间存在,也就是说,在函数的2次访问之间,自动变量不能保留前次调用时的赋值,而是每次进入函数时都要显示为其赋值。
除自动变量外,还可以定义位于所有函数外部的变量,外部变量在程序执行期间一直存在,即使在对外部变量赋值的函数返回后,这些变量仍将保持原来的值不变。
外部变量必须定义在所有函数之外,且只能定义一次,定义后编译程序将为它分配存储单元。在每个要访问外部变量的函数中,必须声明相应的外部变量,此时说明其类型可以用extern语句显示声明。
需要注意的是,过分依赖外部变量会导致一定的风险,因为它是程序中的数据关系模糊不清---外部变量的值可能被意外或者不经意地修改,而程序的修改又变得非常困难。
2.2数据类型及长度
Short类型通常为16位,long通常为32位,int型可以是16,32位。关系表示为
16≤Short≤int≤long≥32位。
类型限定符signed和unsigned可用于限定char类型或任何整型。Unsigned的数总是正值或0,并遵守算术模 定律,其中n表示该类型占用的位数。如char类型占8位,那么unsigned char类型值范围为0~255,而signed char范围为-128~127.
2.3常量
整数常量 123456789L L表示long型 同理 UL
浮点数常量 123.4 后面可加F或者UF unsigned float
八进制前缀 0 如037
十六进制前缀 0x 如0XFUL UL表示unsigned long
字符常量 也是一个整数 书写时将一个字符括在单引号中。如’0’.在ASCII码中’0’表示48.
字符串常量 是用双引号扩起来的0个或多个字符组成的字符串序列。如”I am a string”.
从技术角度看,字符串常量就是字符数组。字符串的内部表示使用一个空字符’\0’作为串的结尾。因此,存储字符串的物理单元数比括在双引号中的字符数多了一个。C语言对字符串的长度没有限制,但程序必须扫描完整个字符串后才能确定字符串长度。标准库函数strlen(s)可以返回字符串参数s的长度,但不包括’\0’。
int strlen(char s[])
{
int i;
while (s[i] != '\0')
++i;
return i;
}
标准头文件
我们应该清楚字符常量与包含一个字符的字符串之间的区别:’X’,”X”是不同的,前者是一个整数,其值是字母X在机器字符集中对应的值。后者是包含一个字符X以及一个结束符'\0'的字符数组。
枚举enum是另外一种类型的常量。在没有显式说明的情况下,enum类型的第一个枚举名值为0,第二个为1,以此类推。如果指定了部分枚举名的值,那么未指定值的枚举名的值将依着最后一个指定值向后递增。Bu
不同枚举中的名字必须互不相同,同一枚举中不同名字可以具有相同的值。
2.4声明
变量的声明可以用const限定符限定,表明该值不能被修改。对数组而言,const限定符表明数组所有元素的值都不能被修改。
2.5 算术运算符
整除法会截断结果中的小数部分。算术运算符+、—、*、/、%(取模运算符)
2.6 关系运算符与逻辑运算符
包括以下运算符:
> >= < <=他们具有相同的优先级。
关系运算符的优先级比算术运算符低。因此,表达式i < lim-1等价于i < (lim-1)。
逻辑运算符&&与||有一些较为特殊的属性,由&&于||连接的表达式按从左到右的顺序进行求值,并且,在知道结果值为真为假后立即停止计算。
2.7 类型转换
自动转换是指把“比较窄的”操作数转换为“比较宽的”操作数。
将字符型转换成整型时注意,c语言没有指定字符型是unsigned还是signed。如果char型值的最左一位是1,则转换为负整数(就行符号扩展)。而在另一些机器中,在char的左边填0.这样就造成正值。所以,为了保证移植性,如果想要存储非字符数据,最好用signed和unsigned就行限定。
C语言中,很多情况会就行隐形转换。两个不同的类型,往往有较低类型提往较高类型。
当表达式中包含unsigned类型的操作数时,转换规则要复杂一些。在于带符号的值与无符号值之间的运算与机器相关。因此他们取决于不同整数类型大小。例如。Int占16位,long占32位,那么-1L<1U,这是因为unsigned int的1U被提升为signed long型;但是-1L>1UL,这是因为1L将被提升为unsigned long类型,因而成为一个较大的正数。
赋值时也会进行类型转换。右边的值要转换成左边的类型。左边变量的类型即是表达式结果的类型。
前面提到,无论是否进行符号扩展,字符型变量都将被转换成整型变量。当把较长的整数转换为较短整数或char类型时,超出的高位部分将被丢弃。因此,
int i;
char c;
i = c;
c = i;
执行后,c值保持不变,无论是否进行符号扩展。但是如果2个赋值语句顺序颠倒一下,则执行后可能会丢失信息。
如果x是float类型,i是int类型。语句x=i与i=x都要进行类型转换。Float转换成int类型时,小数部分别截取掉。当把double类型转换成float时,是进行四舍五入还是截取取决于具体实现。
函数参数传递过程中也产生类型转换。在没有函数原型情况下,char与short类型都要被转换成int,float转换成double。因此,即使调用函数的参数为char或float类型,我们也把函数声明为int或double类型。
强制转换
(类型名)表达式
2.8 自增运算符和自减运算符
前缀运算符++n表示先递增n的值然后再使用n的值。
后缀运算符n++表示先使用n的值然后在递增
若n为5;
X=++n;表示先递增n,n等于6,然后赋给x。此时x为6
X=n++;表示先把n赋值给x,然后再自增n。此时x为5;
2.9 按位运算符
& 按位与(AND)
| 按位或(OR)
^ 按位异或(XOR)
<< 左移
>> 右移
~ 按位求反
必须把位运算符&、|同逻辑运算符&&、||区分开来。
按位与运算符经常用于屏蔽某些二进制位。
按位或经常用于将某些二进制位设置为1.
移位运算符<<与>>分别用于将运算的做操作数左移和右移,移动的位数则由右操作数指定。表达式x<<2把x的值左移2位,右边空出的2位用0填补。当对unsigned类型的无符号值进行右移位时,左边空出的部分将用0填补;在对signed类型的带符号值进行右移时,某些机器将对左边空出的部分用符号填补(即“算术移位”),而另一些机器则对左边空出的部分用0填补(即“逻辑移位”)。
2.11
条件表达式
Expr1?expr2:expr3
首先计算expr1,如果其值不为0,则计算expr2,并以该值作为条件表达式的值,否则计算expr3,并以该值作为条件表达式的值.
比如
Z = (a > b)? a : b; //z=max(a,b)
3.1 语句与程序块
一个分号之前的是一个语句。
二用{}括起来的是程序块
3.4 switch语句
Switch(表达式)
{
Case 常量表达式:语句序列
Case 常量表达式:语句序列
}
利用break来结束switch,return 来返回值
Continue语句用来立即开始下一次循环的执行,continue只能用于循环语句,不能用于switch语句。
Goto语句 搭配标号
比如
Found:
X=1;
Goto found;
4.3 外部变量
默认情况下,外部变量与函数具有下列性质:通过同一个名字对外部变量的所有引用(即使这种引用来自于单独编译的不同函数)实(标准中把这一性质称为外部链接)。
因为外部变量完全可以在全局范围内访问,这就为函数之间的数据交换提供了一种可以代替函数参数与返回值的方式。任何函数都可以通过名字访问一个外部变量,当然这个名字需要通过某种方式进行声明。
如果函数之间需要共享大量的变量,使用外部变量要比使用一个很长的参数表更方便、有效。但是,这样做必须非常谨慎,因为这种方式对程序结构产生不良的影响,而且可能导致程序中各个函数之间具有太多的数据联系。
外部变量的用途还表现在他们与内部变量相比具有更大的作用域和更长的生存期。自动变量只能在函数内部使用,从其所在的函数调用时变量开始存在,在函数退出变量也将消失。而外部变量是永久存在的,它们的值在一次函数调用到下次函数调用之间保持不变。因此,如果两个函数必须共享某些数据,而这个函数互不调用对方,这种情况下最方便的方式便是把这些共享数据定义为外部变量,而不是作为函数参数传递。
4.4 作用域规则
外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译)文件结束。例如,如果mian,sp,val,push,pop是依次定义在某个文件中的5个函数或外部变量,如下所示:
Main(){}
Int sp=0;
Double bal[MAXVAL];
Void push(){}
Double pop(){}
那么,在push与pop这2个函数中不需要进行任何声明就能通过名字访问变量sp与val,但是,这2个变量名不能用在main函数中,push与pop函数也不能用在main函数中。
另一方面,如果要在外部变量的定义之前就使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性的使用关键字extern。
将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性,而变量定义除此之外还将引起存储器的分配。
在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问它(定义外部变量的源文件也可以包含对该变量的extern声明)。外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度。
外部变量的初始化只能出现在其定义中。
4.6 静态变量
Static定义外部变量时,改外部变量只能用于该文件,其他文件不能用该外部变量。
通常,函数是全局可访问的,但用static时,该函数名除了对该函数所在的文件可见外,其他文件都无法访问。
Static还可声明内部变量。Static类型的内部变量同自动变量一样,是某个特定函数的局部变量。只能在该函数中使用,但它与自动变量不同的是,不管函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。换句话说,static类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量。
4.9 初始化
在不进行显式初始化的情况下,外部变量和静态变量都被初始化为0,而自动变量和寄存器变量的初值没有定义。
数组初始化
int days[] = {31, 28, 31, 30,31, 30, 31, 31, 30, 31, 30, 31};
字符数组初始化
char pattern[] = "ould";
char pattern[] = { 'o', 'u','l', 'd'};
二者的初始化是一样的。此时数组的长度是5(加上了一个字符串结束符’\0’).
4.10 递归
递归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储器出来值的栈。递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写和理解。
4.11 C预处理器
一般来说,#include ”文件名”,这是用户自己创建的头文件,#include<文件名>这一般是系统的头文件。一般在源文件所在位置查找用户创建的头文件。
#define 名字 替换文本 一般占一行,但是如果写不下,在行末尾加一个反斜杠符\。其定义域也是从其定义点开始,到被编译的源文件结束。
#define max(A, B) ((A) > (B)? (A) : (B))
max(i++, j++);这里就是错误的,因为每个参数做了2次自增
#define square(x) x * x
squrare(z+1);这里计算结构也是错误的。
4.11.3 条件包含
#if !defined(HDR)
#define HDR
#endif
再举一例#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#else
#define HDR"default.h"
#endif
#include HDR
第五章 指针与数组
5.1 指针与地址
指针是能够存放一个地址的一组存储单元。
一元运算符&可用于取一个对象的地址。地址运算符&只能应用于内存中的对象,即变量与数组元素。它不能作用于表达式、常量或register类型的变量。
一元运算符*是间接寻址或间接引用运算符。当它作用于指针时,将访问指针所指向的对象。
指针只能指向某特定类型的对象(一个例外情况是指向void类型的指针可以存放指向任何类型的指针,但它不能间接引用自身)。
5.2 指针与函数参数
由于c语言是以传值的方式将参数值传递给调用函数。因此,被调用函数不能直接修改主调函数中变量的值。
void swap(int x, int y) /*WRONG */
{
int temp;
temp = x;
x = y;
y = temp;
}
swap(a, b);这里a,b的值不会改变。
void swap(int *px, int *py) /*interchange *px and *py */
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
swap(&a, &b);由于&是取地址符,&a就是指向变量a的指针。
5.3 指针和数组
数组名所代表的的就是该数组最开始的一个元素的地址。
Int a[10];
Int *pa;
Pa=&a[0];pa的值是数组元素a[0]的地址。完全可以写成pa=a;
Pa+i是a[i]的地址。
数组元素a[i]也可以写成*(a+i)。&a[i]和a+i的含义是相同的。a+i是a之后第i个元素的地址。相应的,pa是指针,pa[i]与*(pa+i)是等价的。
但是,我们必须记住,数组名和指针之间有一个不同之处,指针是一个变量,因此,在c语言中,语句pa=a和pa++都合法,但数组名不是变量,因此,类似于a=pa和a++形式的语句是非法的。
当把数组名传递给一个函数时,实际上传递的是该数组的第一个元素的地址。
在函数定义中,形式参数char s[]和char *s是等价的。
下面两个函数调用f(&a[2])和f(a+2)都是把起始于a[2]的子数组的地址传递给函数f。在函数f中,参数的声明可以为
f(int arr[]) { ... }或f(int *arr) { ... }。
对于函数f来说,它并不关心所引用的是否只是一个更大数组的部分元素。
5.4 地址算术运算
static char* allocp = allocbuf;
static char* allocp =&allocbuf[0];
if (allocbuf + ALLOCSIZE - allocp>= n)
在举一例
int strlen(char *s)
{
char *p = s;
while (*p != '\0')
p++;
return p - s;
}
5.5 字符指针与函数
下面两个定义之间有很大的差别:
char amessage[] = "nw isthe time"; /* 定义一个数组 */
char *pmessage = "now isthe time"; /* 定义一个指针 */
上述声明中,amessage是一个仅仅足以存放初始化字符串以及空字符串’\0’的一位数组。数组中的单个字符可以进行修改,但amessage始终指向同一个存储位置。另一方面,pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改成指向其他地址,但如果修改字符串内容的话,结果没有定义。
/* strcpy: copy t to s; arraysubscript version */
void strcpy(char *s, char *t)
{
int i;
i = 0;
while ((s[i] = t[i]) != '\0')
i++;
}
为了比较,下面使用指针方法实现strcpy函数
/* strcpy: copy t to s; pointerversion 2 */
void strcpy(char *s, char *t)
{
while ((*s++ = *t++) != '\0')
;
}
/* strcmp: return <0 ifs
int strcmp(char *s, char *t)
{
int i;
for (i = 0; s[i] == t[i]; i++)
if (s[i] == '\0')
return 0;
return s[i] t[
i];
}
指针方法
/* strcmp: return <0 ifs
int strcmp(char *s, char *t)
{
for ( ; *s == *t; s++, t++)
if (*s == '\0')
return 0;
return *s *
t;
}
下面2个表达式
*p++ = val; /* 将val压入栈*/
val = *p; /* 将栈顶元素弹出到val中*/
是进栈和出栈的标准用法。
5.8 指针数组的初始化
/* month_name: return name ofnth
month */
char *month_name(int n)
{
static char *name[] = {
"Illegal month",
"January", "February","March",
"April","May", "June",
"July","August", "September",
"October","November", "December"
};
4.11 指向函数的指针
int (*comp)(void *, void *)
comp是一个指向函数的指针,该函数有2个void*类型的参数,返回值为int。
用法
if ((*comp)(v[i], v[left]) <0)和定义基本一样
但是int *comp(void *, void *)的意义是
Comp是一个函数,该函数返回一个指向int类型的指针
第六章 结构体
6.1 结构体的基本知识
坐标
struct point {
int x;
int y;
};
关键字struct后的名字是可选的,称为结构标记(这里是point)。结构标记用于结构命名,在定义之后,结构标记就代表花括号内的声明,可以用它作为该声明的简写形式。
结构中定义的变量称为成员,结构成员、结构标记、和普通变量(即非成员)可以采用相同的名字。
声明
Struct {} x,y,z;从语法角度来讲,这种声明与声明
Int x,y,z;具有类似的意义。这2个声明都将x,y,z声明为指定类型的变量,并且为他们分配存储空间。
如果结构的声明后不带变量表,则不需要为它分配存储空间,它仅仅描述了一个结构的模板或轮廓。但是,如果结构声明中带有标记,则可以在以后结构实例时可以使用该标记定义。对于声明的结构声明point,语句
Struct point pt;定义了一个struct point 类型的变量pt。
结构的初始化可以在定义的后面使用初值表进行。如
Struct point maxpt={320,200};
可以通过下列形式来引用某个特定结构中的成员:
结构名.成员
如printf(“%d,%d”,pt.x,pt.y);
结构体可以嵌套
struct rect {
struct point pt1;
struct point pt2;
};
这是一个长方形的结构体,pt1,和pt2分别是对角线上2点。Struct rect screen;
Screen.pt1.x调用
6.2 结构与函数
结构的合法操作只有几种:作为一个整体复制和赋值,通过&运算符取地址,访问其成员。其中,复制和赋值操作包括向函数传递参数以及函数返回值。
Struct point origin,*pp;
Pp=&orgin;
printf("origin is(%d,%d)\n", (*pp).x, (*pp).y);
这里(*pp).x的括号是必须的,因为.的优先级比*的高。此外,c语言提供了另外一种简写方式,p是一个指向结构的指针,可以用p->结构成员引用相关成员。
printf("origin is(%d,%d)\n", pp->x, pp->y);
6.4 指向结构的指针
struct key {
char *word;
int count;
};
struct key *binsearch(char *,struct key *, int);函数返回值是指向key结构体的指针。
struct key *binsearch(char*word, struck key *tab, int n)
{
int cond;
struct key *low = &tab[0];
struct key *high = &tab[n];
struct key *mid;
while (low < high) {
mid = low + (highlow)
/ 2;
if ((cond = strcmp(word,mid>
word)) < 0)
high = mid;
else if (cond > 0)
low = mid + 1;
else
return mid;
}
return NULL;
}
这里mid = (low+high) / 2 /* WRONG*/因为两个指针的加法运算是非法的,但是指针的减法确实合法的。
千万不要认为结构的长度等于各成员长度的和。因为不同的对象有不同的对齐要求。
struct {
char c;
int i;
};可能需要8个字节的存储空间,而不是5个字节。使用sizeof运算符可以返回正确的对象长度。
6.7 类型定义(typedef)
Typedef int length;
Length len;length现在就是int
Typedef char* string;
这样string s;就等于char* s;
typedef int (*s);//typedef int* s;这里s都是表示指向int型指针
typedef struct point{//point 是结构标记
int x;
int y;
}yefeipoint1;
typedef struct {
int x;
int y;
}yefeipoint2;
typedef struct pointyefeipoint3;
//typedef struct yefeipoint4;//这个就有问题。因为只是给struct起了别名,貌似这样也理解错了,而是struct就不能这么用。
#define yefei struct//这是上面的解决方案
#define yefeipoint6 structpoint//这个都可以,牛逼呀
typedef yefei pointyefeipoint5;这里除了yefeipoint4,其他都是代表struct point
union testUnion{
int x;
float y;
char c;
}s;和struct是一模一样的。联合名.成员 联合指针->成员。把-这个想象成针
联合可以保存不同类型长度的对象和变量,联合在每一次只能对一个成员复制。联合只能用其第一个成员类型的值进行初始化。
第7章 输入与输出
Getchar与putchar
#include
#include
main() /* lower: convert inputto lower case*/
{
int c
while ((c = getchar()) != EOF)
putchar(tolower(c));
return 0;
}
7.2 格式化输出---printf函数
负号------左对齐
数--------指定最小宽度
小数点----用于将字段宽度和精度分开,小数点前面是字段宽度,后面是精度
O--------八进制
X x------十六进制
U--------无符号十进制
S--------char *型
F--------float
P--------void *
7.4 格式化输入—scanf函数
int scanf(char *format, ...)
format和printf里的格式差不多,其他参数必须是指针,用于指定经格式转换后的相应输入保存的位置。
7.5 文件访问
文件指针:它指向一个包含文件信息的结构,这些消息包括:缓冲区位置、缓冲区中当前字符的位置、文件的读或写状态、是否出错或是否到达文件结尾等等。
FILE *fp;
FILE *fopen(char *name,char*mode);
fp是一个指向FILE的指针,fopen函数是一个返回一个指向FILE指针。注意,FILE像int一样是一个类型名,而不是结构标记。Mode有读(r),写(w)及追加(a)。
文件被打开后,就需要考虑用哪种方法对文件进行读写。可以有很多方式,其中getc和putc最简单。getc从文件中返回下一个字符,它需要知道文件指针,以确定对哪个文件执行操作:int getc(FILE *fp),该函数返回fp指向的输入流中的下一个字符。如果到达文件尾或者出现错误,该函数将返回EOF。
putc是一个输出函数,int putc(int c,FILE *fp)
该函数将字符c写入到fp指向的文件中,并返回写入的字符。如果发生错误,返回EOF。
7.7 行输入和行输出
标准库提供了一个输入函数fgets,它和前面几章用到的getline类似。
char *fgets(char *line,int maxline, FILE *fp)
fgets函数从fp指向的文件中读取下一个输入行(包括换行符),并将它存放在字符数组line中,它最多可读取maxline-1个字符。读取的行将以’\0’结尾保存到数组中。通常情况下,fgets返回line,但如果遇到文件结尾或者发生错误,则返回NULL。
输出函数fputs将一个字符串(不需要包含换行符)写入到一个文件中:
int fputs(char *line,FILE *fp)如果发生错误,该函数将返回EOF,否则返回一个非负值。
7.8 其他函数
strlen、strcpy、strcat、strcmp它们都在头文件
s和t是 char *类型,c和n是int类型。
strcat(s, t) 将t指向的字符串连接到s指向的字符串的末尾。
strncat(s, t, n) 将t指向的字符串中前n个字符连接到s指向的字符串的末尾。
strcmp(s, t) 根据s指向的字符串小于(s
strncmp(s, t, n) 同strcmp一样,但是只比较前n个字符
strcpy(s, t) 将t指向的字符串复制到s指向的位置
strncpy(s, t, n) 将t指向的字符串前n个字符复制到s指向的位置
strlen(s) 返回s指向的字符串的长度
strchr(s, c) 在s指向的字符串中查找c,若找到,则返回指向它第一次出现的位置的指针,否则返回NULL
strrchr(s, c) 在s指向的字符串中查找c,若找到,则返回指向它最好一次出现的位置的指针,否则返回NULL
7.8.2. 字符类别测试和转换函数
头文件
isalpha(c) 若c是字母,则返回一个非0值,否则返回0
issuper(c) 若c是大写字母,则返回一个非0值,否则返回0
islower(c) 若c是小写字母,则返回一个非0值,否则返回0
isdigit(c) 若c是数字,则返回一个非0值,否则返回0
isalnum(c) 若isalpha(c)或者isdigit(c),则返回一个非0值,否则返回0
isspace(c) 若c是空格、横向制表符、换行符、回车符,换页符或纵向制表符,则返回一个非0值
toupper(c) 返回c的大写形式
tolower(c) 返回c的小写形式
7.8.3 ungetc函数
int ungetc(int c,FILE *fp)
该函数将字符c写回到文件fp中。如果执行成功,则返回c,否则返回EOF。
7.8.4 命令执行函数
system(char *s)执行包含在字符串s中的命令。然后执行当前程序。
system(“date”);
输出当前日期和时间。
7.8.5 存储管理函数
malloc和calloc用于动态分配存储块。
void *malloc(size_t n)当分配成功时,返回一个指针,指针指向n字节长度的未初始化的存储空间。否则返回NULL。
void *calloc(size_t n,size_t size)当分配成功时,它返回一个指针,该指针指向的空闲空间足以容纳由n个指定长度的对象组成的数组,否则返回NULL,该存储空间被初始化为0.
上面的都是返回指针。。。。。
int *ip;
ip = (int *) calloc(n,sizeof(int));
free(p)释放p指向的存储空间。其中,p必须是malloc或calloc函数得到的指针。使用已经释放的指针同样是错误的。
7.8.6 数学函数
头文件
sin(x),sin(x),atan2(y, x),log(x),log10(x)
exp(x) 指数函数
pow(x, y)
sqrt(x) x的平方根(x≥0)
fabs(x) x的绝对值
7.8.7 随机数发生器函数
函数rand()生成介于0和RAND_MAX之间的伪随机指数序列。其中,RAND_MAX是在头文件
如需转载,请注明来源。