《C语言程序设计》 - 何钦铭


第一章 引言

1.2 程序与程序设计语言

冯 · 诺依曼模型的存储程序概念:将程序与数据共同存储

结构化的程序设计方法:将复杂程序划分为若干个相互独立的模块(一个模块可以是一条语句、一段程序或一个函数等)使完成每个模块的工作变得单纯而明确,在设计一个模块时不受其他模块的牵连。同时,通过现有模块积木式的扩展就可以形成复杂的、更大的程序模块或程序

1.3 C语言的发展历史与特点

C语言的特点:(拥有底层操作能力)

1.结构化语言

2.易于移植

3.简洁紧凑,使用方便灵活

4.强大的处理能力

5.生成的目标代码质量高,运行效率高

第二章 用C语言编写程序

2.3 计算分段函数

#include <stdio.h>	//编译预处理指令

程序运行时,首先从main函数开始运行

%f指定输出时保留6位小数,%.2f则指定输出时保留2位小数

image-20210326214344680

循环体语句只能是一条语句(如果循环体语句由多条语句组成,必须用大括号把他们括起来,变成一条复合语句)

整型数据的输出格式控制说明%md,指定了数据的输出宽度为m(包括符号位)。若数据的实际位数(含符号位)小于m,左端补空格;若大于m,按实际位数输出

实型数据的输出格式控制说明 %m.nf,指定了输出浮点型数据时保留n位小数,且输出宽度是m(包括符号位和小数点)。若数据的实际位数(含符号位)小于m,左端补空格;若大于m,按实际位数输出

f是针对浮点型数据而言,若输出数据为整型,则%m.nf应改为%md

赋值运算符左边只能是一个变量

第三章 分支结构

ch = getchar()	//从键盘输入一个字符,并赋值给变量ch
putchar(输出参数);	//输出参数是字符型变量或字符型常量

第四章 循环结构

else和 if 的匹配准则:else与最靠近它的、没有与别的 else 匹配过的匹配

do-while语句:循环条件需要在循环体中明确,且至少会执行一次循环体

do {

​ 循环体语句

} while (表达式);

//判断一个数是否为素数

#include <stdio.h>
int main()	{
	int i,m;
	
	printf("Enter a number:");
	scanf("%d", &m);
	for(i=2;i<=m/2;i++)
		if(m%i==0)
			break;	//若m能被某个i整除,则m不是素数,提前结束循环 
		if(i>m/2 && m!=1)	//若循环正常结束,说明m不能被任何一个i整除	
			printf("%d is a prime number! \n", m);
		else
			printf("No! \n");
	return 0;
}

break语句强制循环结束,不再执行循环体中位于其后的其他语句,break语句应该和if语句配合使用,即条件满足时,才执行break跳出循环;否则若break无条件执行,意味着永远不会执行循环体中break后面的其他语句

continue语句:跳过循环体中continue后面的语句,继续下一次循环,continue语句一般也需要与if语句配合使用

break语句和continue语句的区别在于,break结束循环而continue跳过后面语句继续循环

break除了可以中止循环外,还用于switch语句,而continue只能用于循环

//使用嵌套循环计算1!+2!+3!+...+100
#include <stdio.h>
int main()	{
    int i,j;
    double item,sum;	//变量item中存放阶乘的值
    sum = 0;
    for(i=1;i<=100;i++)	{
        item = 1;	//置item的初值为1,以保证每次求阶乘都从1开始连乘
        	for(j=1;j<=i;j++)	//内层循环重复i次,算出item=i!
                item = item * j;
        	sum = sum+item;		//把i!累加到sum中
    }
    printf("1!+2!+...+100!=%e \n",sum);	//用指数形式输出结果
    
    return 0;
}

//在累加求和的外层for语句的循环体语句中,每次计算i!之前,都重新置item的初值为1,以保证每次计算阶乘都从1开始连乘

对嵌套循环初始化时,一定要分清内外层循环

4.5 循环结构程序设计

if(循环次数已知)
    使用 for 语句
else			//循环次数未知
    if(循环条件在进入循环时明确)
        使用 while 语句
    else		//循环条件需要在循环体中明确
        使用 do-while 语句

第五章 函数

函数定义的一般形式为:

函数类型	函数名	(形式参数表)	/*函数首部,由  函数类型、函数名、形式参数表  组成,函数名是函数整体的称谓
    函数类型一般指函数结果返回的类型,一般与return语句中表达式的类型一致
    
    函数不能嵌套定义
    
    函数首部后面不能加分号*/

函数体用return语句返回运算的结果(return语句只能返回一个值),return类型和函数类型一致

return是普通变量,不是形参,它只是函数实现过程中要用到的工作单元,只有必须从主调函数中得到的已知条件,才定义为形参,其他需要的工作单元都定义成普通变量

double cylinder (double r, double n)
/*函数的类型是double,即函数的结果类型也是double*/

5.1.3 函数的调用

充分理解函数调用与返回的实现过程,对学好函数程序设计是至关重要的

1.函数的调用过程

主调函数指调用其他函数的函数,如main( )

2.函数调用的形式:

函数名(实际参数表)

实参(实际参数):可以是变量、常量、表达式,例如,cylinder ( )中,使用变量 radius 和 height 作为实参

对于实现计算功能的函数,函数调用通常出现在两种情况下:

3.参数传递

函数定义时->

位于其首部的参数被称为形参

主调函数的参数被称为实参。形参除了能接受实参的值外,使用方法与普通变量类似。形参和实参必须一一对应,两者数量相同,类型尽量一致。

程序运行遇到函数调用时,实参的值传给形参

//计算圆柱体积
#include <stdio.h>
int main(void)
{
    double height, radius, volume;
    double cylinder(double r, double h);
    printf(" Enter radius and height:");
    scanf("%lf %lf", &radius, &height);
    
    volume=cylinder (radius, height);
    printf("Volume=%.3f\n",volume);
    return 0;
}

double cylinder (double r,double h)
{
    double result;
    
    result=3.1415926*r*r*h;		//计算圆柱体积
    return result;				//返回结果
}
    
而 main()函数中:
volume = cylinder (radius, height);
说明 radius 和 height 是实参。
    函数调用时,实参 radius 和 height 的值将被依次传给形参 r 和 h

实参需要传值给形参,所以函数的形参必须是变量,用于接受实参传递过来的值;实参可以是常量、变量或表达式

实参主调函数的,形参自定义函数

一般情况下表达式的类型与函数类型应一致,如果两者不一致,以函数类型为准

5.函数原型声明

函数声明的一般格式: 函数名(参数表);

5.2 数字金字塔

//输出5行的数字金字塔
#include <stdio.h>
void pyramid (int n);	//函数声明
int main()
{
    pyramid(5);
    
    return 0;
}

void pyramid (int n)	//函数定义,输出n行数字金字塔 
{
    int i,j;
    
    for(i=1;i<=n;i++)		//需要输出的行数 
    {
        for(j=1;j<=n-i;j++)			//输出每行左边的空格 
            printf(" " );
        for(j=1;j<=i;j++)			//输出每行的数字 
            printf("%d ",i);
        putchar('\n');
    }
}

运行结果

image-20210403191528270

函数定义时,形参 n 决定了需要输出的数字金字塔的层数

5.2.2 不返回结果的函数

在很多程序设计中,调用函数是为了让它产生某些作用。具有类似作用的函数在有些语言中也称为过程

不返回结果的函数定义:

void 函数名(形参表)			//函数首部
{
    函数实现过程				//函数体
}

函数类型为void,表示不返回结果,函数体中可以使用没有表达式的 return 语句,也可以省略 return 。void 类型的函数虽然不直接返回一个值,但它的作用通常以屏幕输出等方式体现

在不返回结果的函数定义中,void不能省略;否则函数类型被默认定义为int

由于函数没有返回结果,函数调用不可能出现在表达式中,通常以独立的调用语句方式,如pyramid(5)

不返回结果的函数适用的场合主要是把一些确定的、相对独立的程序功能封装成函数。
主函数通过调用不同的函数,体现算法步骤,而各步骤的实现由相应函数完成,从而简化主函数结构,以体现结构化程序设计思想

5.2.3 结构化程序设计思想

按照自顶向下的方法分析问题,有助于后续的模块化设计与测试,以及系统的集成

5.3 复数运算

5.3.2 局部变量与全局变量

1.局部变量

定义在函数内部的变量,它们的有效作用范围局限于所在的函数内部,因此主调函数只有通过参数传递,才能把实参数据传递给函数使用;同样,形参的改变也不会影响到实参变量。这种变量的有效使用范围,确保了各函数之间的独立性,避免函数之间相互干扰

C还允许定义作用于复合语句中的局部变量,有效使用范围被局限于复合语句内,一般用作小范围内的临时变量

2.全局变量

定义在函数外而不属于任何函数的变量称为全局变量。全局变量的作用范围是从定义开始到程序所在文件的结束,它对作用范围内所有的函数都起作用。全局变量是为了解决多个函数间的变量共用

当某函数的局部变量与全局变量同名时,在该函数中全局变量不起作用,而局部变量起作用。**

对于其他不存在同名变量的函数,全局变量仍然有效。

当函数局部变量与复合语句的局部变量同名时,以复合语句为准

全局变量可以帮助解决函数多结果返回的问题,但全局变量更多地用于多函数间的全局数据表示

缺点:对于规模较大的程序,过多使用全局变量会导致各函数间相互干扰

因此在变量使用中,应尽量使用局部变量函数参数

5.3.3 变量生存周期和静态局部变量

计算机都是从主函数开始运行的,使得main( )函数中的局部变量,一开始就在内存数据区中分配了存储单元。而其他函数在被调用之前,其局部变量并未分配存储单元,只有当函数被调用时,其形参和局部变量才被分配相应存储单元;一旦函数调用结束返回主调函数,在函数中定义的所有形参和局部变量将不复存在,相应的存储单元由系统收回。根据这种特性,把局部变量称为自动变量。变量从定义开始分配存储单元,到运行结束存储单元被回收,整个过程称为变量生存周期

自动变量的定义形式:

auto int x,y

在自动变量定义时,auto可以省略,其形式与以前定义的普通变量完全相同

变量的作用范围生存周期是两个概念

C语言把保存所有变量的数据区分为动态存储区静态存储区

  • 动态存储区是使用堆栈来管理的,适合函数动态分配 与 回收存储单元
  • 静态存储区相对固定,用于存放全局变量 和 静态变量

image-20210404131417745

3.静态变量

静态变量:在程序执行前系统就为之静态分配(也即在运行时不再改变分配情况)存储空间的一类变量

一般来说,静态变量 = 全局变量,而即使在有明确区分全局和静态变量的程序语言中,在编译后的代码里二者也以相同的方式获取存储空间。

在静态存储区中,除了全局变量外还有静态局部变量,它存放在静态存储区,静态局部变量的生命周期会持续到程序结束

静态变量只能用于所定义函数,而不能用于其它函数

静态变量定义格式:

​ static 类型名 变量表

若没有静态保存的要求,不建议使用静态变量

第六章 回顾数据类型和表达式

6.3 类型转换

C中,不同类型的数据可以混合运算,但这些数据首先要转换成同一类型,然后再做运算

image-20210404151319314

赋值运算时,赋值两侧数据的类型最好相同,至少右侧数据的类型比左侧数据的类型级别低,或者右侧数据的值在左侧变量的取值范围内

6.3.2 强制类型转换

(类型名) 表达式;

表达式 (double) i 的类型是double,而i的类型并没有改变,还是原来的

6.4 表达式

6.4.4 逻辑表达式

~是位运算符,在二进制中是按位取反的意思,0变为1,1变为0

^是双目运算符,按位“异或”

6.4.8 其他运算

长度运算符sizeof是一个单目运算符,用来返回变量或数据类型的字节长度

(ch = getchar())! ='\n' 和 ch = getchar()! ='\n'不等价
    后者是一个赋值表达式,等价于ch = (getchar()! ='\n'),所以()中存放的是0或1

条件运算符的优先级较低只比赋值运算符高。结合方向是自右向左

移位运算:>>右移 <<左移

a>>b	//将a的二进制右移b位

操作数的移位并不改变原操作数的值

长度运算符sizeof是一个单目运算符,用来返回变量数据类型的字节长度

第七章 数组

int a [10];		//定义1个数组a,它有10个整型元素
//类型名 数组名 [数组长度];

在程序中使用数组,可以让一批相同类型的变量使用同一个数组变量名,用下标来相互区分。它的优点是表达简洁,可读性好,便于使用循环结构

数组名存放数组内存空间的首地址,是一个地址常量(不允许修改)

只能引用单个的数组元素,而不能一次引用整个数组,数组下标从0开始

注意区分数组的定义数组元素的引用,两者都要用到“数组名[整型表达式]”。

定义数组时,方括号内是常量表达式,代表数组长度,它可以包括常量和符号常量,但方括号内不能包括变量。也就是说,数组的长度在定义时必须指定,在程序的运行过程中是不能改变的。

引用数组元素时,方括号内是表达式,代表下标,可以是变量,下标的合理取值范围是[0, 数组长度-1]

//允许对静态数组和动态数组初始化
static int b [5] = {1,2,3,4,5};	
//初始化静态数组b,静态存储的数组如果没有初始化,系统自动给所有的数组元素赋0
static int b [5];					

int fib[20] = {0,1};	
//对数组fib的前2个元素赋初值,其余元素的值不确定不确定不确定

数组的应用离不开循环,将数组的下标作为循环变量,通过循环,就可以对数组的所有元素逐个进行处理

数组的长度在定义时必须确定,如果无法确定,需要估计其上限,并将该上限作为数组长度

7.2.2 二维数组的定义和引用

二维数组主要用于表示二维表矩阵,引用二位数组的元素要制定两个下标(行下标和列下标)

由于二维数组的行(列)下标从0开始,而矩阵或二维表的行(列)从1开始,用二维数组表示二维表和矩阵时,就存在行(列)计数的不一致。为了解决这个问题,可以把矩阵或二维表的第k行(列)也看成从0开始,即如果二维数组的行(列)下标为k,就表示矩阵或二维表的第k行(列)。

二维数组的定义形式为:类型名 数组名 [行长度] [列长度]

7.2.3 二维数组的初始化

定义二维数组时,可以对数组元素赋初值,二维数组的初始化方法有两种

1.分行赋初值(也可以只针对部分元素)(直观清晰,不易出错,推荐)

int a [3] [3] = {{1,2,3}{4,5,6}{7,8,9}};

由于二维数组的元素在内存中按行优先方式存放,将行下标作为外循环的循环变量,列下标作为内循环的循环变量,可以提高程序的执行效率

7.3 判断回文

'\0' 是字符串结束符

字符串的操作:用结束符'\0'来控制循环

字符串的存储和运算可以用一维字符数组来实现。数组长度取上限80,以回车符'\n'作为输入结束符

//采用赋值的方法将字符串"a"存入数组s。它等价于:
static char s [80] = "a";

/*区分"a"和'a':
"a"是字符串常量,包括'a'和'\0'两个字符,用一维字符数组存放;
'a'是字符常量,只有一个字符,可以赋给字符变量

输入字符串时,需要实现设定一个输入结束符。一旦输入它,就表示字符串输入结束,并将输入结束符转换为字符串结束符'\0'

将字符串存入字符数组时,由于它有一个结束符'\0',数组长度至少是字符串的有效长度+1,。例如,字符串"Happy"的有效长度是5,存储它的数组的长度至少应该为6

(字符串由有效字符字符串结束符 '\0' 组成)

将字符串存入一维字符数组后,对字符串的操作就是对该字符数组的操作,一般通过比较数组元素的值是否等于 '\0' 来决定是否结束循环,即用结束符'\0'来控制循环

/*统计字符串中数字字符的个数*/
#include <stdio.h>
int main()
{
    int count,i;
    char str [80];
    
    /*输入字符串*/
    printf("Enter a string:");
    i = 0;
    while ((str [i] =getchar() )! ='\n')
        i++;
    str [i] ='\0';
    
    /*统计字符串中数字字符的个数*/
    count=0;
    for(i=0;str[i]!='\0';i++)	/*循环条件:str[i]不等于'\0'*/
        if (str[i]<='9' && str [i] >='0')
            count++;
    printf("count=%d\n",count);
    
    return 0;
}
/*运行结果:
Enter a string: It's 512?
count = 3

程序首先输入一个字符串,再处理该字符串,统计其中数字字符('0'、...、'9')的数量
输入一串字符后,输入结束符'\n'被转换为字符串结束符'\0',字符串"It's 512?"存入数组str中
由于字符串"It's 512?"只占用了数组的一部分,所以处理不能针对str的所有80个元素,只能针对该字符串,即数组str中第1个'\0'前面的字符,在处理时,程序从数组的首元素str[0]开始,按下标递增的顺序,逐个处理数组元素,一旦遇到某个元素是'\0',说明字符串已结束,处理也随之结束

第八章 指针

如果事先无法确定需要处理的数据数量,一种方法是估计一个上限,并将该上限作为数组长度,这常常会造成空间浪费;另一种方法是利用指针实现存储空间的动态分配

使用指针可以对复杂数据进行处理,能对计算机的内存分配进行控制,在函数调用中使用指针还可以返回多个值

8.1 密码开锁

/*获取密码的两种方法*/
#include <stdio.h>
int main()
{
    int x = 5342;
    int *p = NULL;	/*定义整型指针变量p,NULL值为0,代表空指针*/
    
    p = &x;		/*将变量x的地址存储在p中*/
    
    /*通过变量名x输出密码值*/
    printf("If I know the name of the variable, I can get it's value by name:%d\n",x);
    
    /*通过变量x的地址输出密码值*/
    printf("If I know the address of the variable is:%x,then I also can get it's value by address:%d\n",p, *p);
    
    return 0;
}
/*程序中定义了变量x来存放密码,
再定义一个特殊的指针变量p,用于存放变量x的地址。
这样既可以通过变量名x直接得到密码值,也可以在不知道变量名的情况下,通过指针变量p所存放的x的地址间接找到密码值。

8.1.2 地址和指针

计算机为了对内存单元中的数据进行操作,一般是按“地址”存取的,如下图

image-20210406212150489

举例:调用函数printf("%d",x)程序执行时是将变量翻译为它所在的内存地址进行操作的
同时可以描述为:将x所在的内存地址1000~1001单元的内容按照整型格式输出。这种变量的方法叫做直接访问

直接访问:一般以变量所在的内存单元的第1个字节的地址作为它的地址,如变量x的内存地址是1000,y的地址是1002,z的地址是1004,变量x、y、z的内容分别为20、1和155

还有一种使用变量的方法,通过变量的地址进行操作:用指针(point)访问内存和操纵地址

image-20210406212719320

假设再定义一个变量p,它位于2000单元,该单元中存放了变量x的地址1000,如上图,此时取出变量p的值1000,就可以访问内存1000单元,实现对变量x的操作,也就是说通过变量p,可以间接访问变量x

使用变量p访问变量x的过程实现了对变量x的间接操作。在C语言中把这种专门用来存放变量地址的变量称为“指针变量”,简称为指针

指针是用来存放内存地址的变量

scanf("%d",&n)
/*把输入的值存储到变量n所在的内存单元里,	&n代表变量n的内存地址/存储位置

&称为地址运算符*/

8.1.3 指针变量的定义

如果在程序中声明一个变量,并使用地址作为该变量的值,那么这个变量就是指针变量。

定义指针变量的一般形式为:
类型名 *指针变量名;

类型名指定指针变量所指向变量的类型,必须是有效的数据类型

指针变量名是指针变量的名称

定义指针变量要使用指针声明符 *

int i, *p;
//声明变量i是int型,变量p是指向int型变量的指针
int *p
//定义一个指针变量p,指向整型变量

指针和指针变量在含义上存在一定的差异

->指针被认为是一个概念,是计算机内存地址的代名词之一,而指针变量本身就是变量,和一般变量不同的是它存放的是地址

大多数情况下并不特别强调他们的区别,如果未加声明,指针 = 指针变量

指针变量用来存放变量的地址,由于不同类型的变量在内存中占用不同大小的存储单元,所以只知道内存地址,还不能确定该地址上的对象。因此在定义指针变量时,除了指针变量名,还需要说明该指针变量所指向的内存空间上所存放数据的类型

int *p;		//定义一个指针变量p,指向整型变量
char *cp;	//定义一个指针变量cp,指向字符型变量
float *fp;	//定义一个指针变量fp,指向实型变量

定义多个指针变量时,每一个指针变量前面都必须加上 *

指针变量的类型是指它所指向的变量的数据类型。无论何种类型的指针变量,它们都是用来存放地址的,因此指针变量自身所占的内存空间大小和它所指向的变量数据类型无关

尽管不同类型的变量所占的内存空间不同,但不同类型指针变量所占的内存空间大小都是相同的

&:地址运算符

*:间接访问运算符,还被用于定义指针变量

指针变量也要先赋值再使用,当然指针变量被赋的值应该是地址

p = &i;
//将指针p和变量i关联起来,这也是指针最常用的赋值方法
p = 0;
p = NULL;
//说明怎样把特殊值0赋值给指针p,这时指针的值为NULL。
//常量NULL在系统文件stdio.h中被定义,其值为0,将它赋给指针时,代表空指针,空指针不指向任何单元

image-20210407135423040

在定义指针变量时,要注意以下几点:

  • 指针变量名是一个标识符,要按照C标识符的命名规则对指针变量进行命名。
  • 指针变量的数据类型是它所指向的变量的类型,一般情况下一旦指针变量的类型被确定后,它只能指向同种类型的变量
  • 定义指针变量时需要使用指针声明符 *,但指针声明符并不是指针的组成部分。例如定义 int *p ;说明p是指针变量,而不是*p

8.1.4 指针的基本运算 (想同类型的指针还能进行赋值、比较和算术运算)

1.取地址运算和间接访问运算

&用于给出变量的地址,例如:

int *p, a=3;
p=&a;
//用运算符& 取变量a 的地址,并将这个地址值作为指针p的值,使指针p指向变量a
//运算符& 的操作数必须是变量
  • :间接访问运算符。除了被用于定义指针变量外,还被用于访问指针所指向的变量

例如:当p指向a时,*p和 a 访问同一个存储单元,*p的值就是 a 的值,如下图所示

image-20210407201604119

/*取地址运算和使用指针访问变量*/
#include <stdio.h>
int main()	{
    int a = 3, *p;	//定义整型变量a 和整型指针p*
    
    p = &a;			//把变量a的地址赋给指针p,即p指向a
    printf("a=%d, *p = %d\n", a, *p);	//输出变量a的值和指针p所指向变量的值
    *p = 10;		//对指针p所指向的变量赋值,相当于对变量a赋值
    printf("a=%d, *p=%d\n", a, *p);
    printf("Enter a:" );
    scanf("%d", &a);	//输入a
    printf("a=%d, *p=%d\n", a, *p);
    (*p) ++;	//将指针所指向的变量加1
    printf("a=%d, *p=%d\n", a, *p);
    
    return 0;
}

/*运行结果
a = 3,*p = 3
a = 10, *p = 10
Enter a: 5
a = 5, *p = 5
a = 6, *p = 6
int a = 3, *p
//定义了指针变量,p是变量名,*表示其后的变量是指针
//*p代表指针p所指向的变量
int a = 1, x, *p;
p = &a;
x = *p++;
/*指针先指向a,其后的语句x = *p++,将p所指向的变量a的值赋给变量x,然后修改指针的值,使得指针p不再指向变量a*/

要正确理解指针的含义,带有间接地址访问符 * 的变量的操作在不同的情况下会有完全不同的含义,这既是C的灵活之处,也是初学者最容易出错的地方

*p = *p+1、++*p 和 (*p)++
//都是将指针p所指向变量的值+1
    
//表达式 *p++ 等价于 *(p++),先取 *p 的值作为表达式的值,再将指针p的值+1,运算后,p不再指向变量a

2.赋值运算

一旦指针被定义并赋值后,就可以如同其他类型变量一样进行赋值运算

int a = 3, *p1, *p2;		//定义整型变量指针p1和p2
p1 = &a;					//把变量a的地址赋给指针p1
p2 = p1;					//p1和p2都指向变量a
//此时,*p1、*p2和a访问同一个存储单元,它们的值一样 

给指针赋值是使指针和所指向变量之间建立关联的必要过程,指针之间的相互赋值只能在相同类型的指针之间进行

8.1.5 指针变量的初始化

int a;
int *p1=&a;		//在定义指针p1的同时给其赋值,使指针p1指向变量a
int *p2=p1;		//在定义指针p2的同时给其赋值,使p2和p1的值相同

进行指针初始化需要注意:不能用数值作为指针变量的初值

例如:

int *p = 1000;//是不对的
int *p = 0;//是将指针变量初始化为空指针。这里0是ASCII字符NULL的值

8.2 角色互换

函数参数包括实参和形参,两者的类型要一致。如果将某个变量的地址作为函数的实参,相应的形参就是指针

实参 形参之间的数据传递是单向的“值传递”方式

调用函数不能改变实参变量的值,当指针变量作为函数参数时也遵循这一个规则

调用函数不能改变实参指针变量的值,但可以改变实参指针变量所指向的变量的值。这样的机制被称为引用调用

采用引用调用机制需要在函数定义时将指针作为函数的形参,在函数调用时把变量的地址作为实参

void swap1 (int x, int y)
{
    int t;
    t=x;
    x=y;
    y=t;
}
/*
函数swap1()使用的是普通变量调用(值调用),参数的传递是从实参变量到形参变量的单个方向上的值的传递。在swap1()被调用时,将实参a和b的值传递给形参x和y,在函数中通过变量t实现了变量x和y的值的交换。但当返回主调函数时后,函数swap1()中定义的变量都销毁了,而主调函数中的变量a和b的值没有任何改变

即在函数swap1()中,改变了形参的值,但不会反过来影响到实参的值。因此,调用swap1()不能改变函数main()中实参a和b的值。
即swap1()的调用过程没有实现a和b的值的交换
*/

void swap2(int *px, int *py)
{
    int t;
    t = *px;
    *px = *py
    *py = t;
}

/*
函数swap2()的实参是指针变量pa和pb,其值分别是变量a和b的地址。在函数swap2()被调用时,将实参pa和pb的值传递给形参px和py。这样px和py中分别存放了a和b的地址,px指向a,py指向b,由于*px和a代表同一个存储单元,只要在函数中改变*px的值,就改变了该存储单元的内容。返回主调函数后,由于a代表的单元的内容发生了变化,a的值就改变了。

在函数swap2()中交换*px和*py的值, 主调函数中a和b的值也相应交换了,即达到了数据交换的目的
*/

void swap3(int *px, int *py)
{
    int *pt;
    pt = px;
    px = py;
    py = pt;
}

/*
函数swap3()的参数形式与2一样,调用时的参数传递过程也相同,但在函数swap3()中直接交换了形参指针px和py的值,由于相同的理由,形参px和py的改变不会影响实参pa和pb

因此调用该函数并不能改变主调函数main()中变量a和b的值
*/

​ 要通过函数调用来改变主调函数中某个变量的值,可以把指针作为函数的参数。在主调函数中,将 该变量的地址 或者 指向该变量的指针 作为实参,在被调函数中,用指针类型形参接受该变量的地址,并改变形参所指向变量的值

将指针作为函数的参数就能使函数返回多个值

/*输入2000 和 61,输出2000-3-1*/
#include <stdio.h>
void month-day(int year, int yearday, int *pmonth, int *pday);

int main()	{
    int day, month, year, yearday;
    printf("input year and yearday:" );
    scanf("%d%d", &year, &yearday );
    month-day(year,yearday, &month, &day);
    printf("%d-%d-%d \n",year month, day);
    
    return 0;
}

void month-day(int year, int yearday, int *pmonth, int *pday);	{
    int k, leap;
    int tab [2][13] = {
        {0,31,28,31,30,31,30,31,31,30,31,30,31},
        {0,31,29,31,30,31,30,31,31,30,31,30,31},
    };
    
    /*建立闰年判别条件leap*/
    leap = (year%4 == 0 && year%100 != 0)  || year % 400 == 0;
    
    for (k=1;yearday>tab[leap] [k]; k++)
        yearday-=tab[leap] [k];
    *pmonth=k;
    *pday=yearday;
}

/*在函数main()中调用函数 month_day()时,将变量 month 和 day 的地址作为实参,在被调函数中用形参指针 pmonth 和 pday 分别接收地址,并改变了形参所指向变量的值。因此,函数main()中month 和 day 的值也随之改变

8.3 冒泡排序

指针和数组有很多相似之处,比如指针名和数组名都代表内存地址,不同的是指针名是一个变量,数组名是一个常量,也就是说,指针名所代表的地址是可以改变的,数组名所代表的地址是不能改变的

//冒泡排序算法:输入n个正整数,将它们从小到大排序后输出
#include <stdio.h>
void bubble(int a [], int n);
int main()	{
    int n, a[8];
    int i;
    
    printf("Enter n (n<=8):" );
    scanf("%d",&n);
    printf("Enter a [%d]:", n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    bubble(a, n);
    printf("After sorted, a[%d] =", n);
    for (i=0; i<n; i++)
        printf("%d", a[i]);
    
    return 0;

}

void bubble(int a [], int n)	{		//n是数组a中待排序元素的数量
    int i, j, t;
    for (i=1;i<n;i++)					//外部循环
        for (j=0;j<n-1;j++)				//内部循环
            if (a[j] > a[j+1])	{		//比较两个元素的大小
                t = a[j]; a[j] = a[j+1]; a[j+1] = t;//如果前一个元素大,则交换
            }
}

/*
bubble()实现数组元素的排序。它有2个形参,a是等待排序的整形数组名,n指明数组a待处理的数组元素的数量
*/

8.3.2 指针、数组和地址间的关系

数组的基地址是在内存中存储数组的起始位置,它是数组中第一个元素的地址,因此数组名本身是一个地址即指针值

​ 在访问内存方面,指针是以地址作为值的变量,而数组名的值是一个特殊的固定地址,可以把它看作是常量指针

虽然在很多方面数组和指针都能处理同样的问题,但它们之间有一个本质的不同,数组a是指针常量,不是变量,所以像 a=p, a++这样的表达式都是非法的,不能改变指针常量a的值

两同类型的指针相减,表示他们之间相隔的数组元素数目

8.4.2 字符串和字符指针

字符串常量实质上是一个指向该字符串首字符的指针常量

第九章 结构 - 把一些数据分量聚合成一个整体的数据类型

像数组和指针一样,结构也是一种构造数据类型,它与数组的区别在于:数组中所有的元素的数据类型必须是相同的
结构中各成员的数据类型可以不同

//输出平均分最高的学生信息
#include <stdio.h>
struct student	{		//学生信息结构定义
    int num;			//学号
    char name [10];
    int computer, english, math;
    double average;
};

int main()	{
    int i,n;
    struct student s1, max;		//定义结构变量
    printf("Input n:");
    scanf("%d", &n);
    printf("Input the student's number, name and course scores\n" );
    for (i=1;i<=n;i++)	{
        printf("No.%d:", i);
        scanf("%d%s%d%d%d",&s1.num, &s1.name, &s1.math, &s1.english, &s1.computer);
        s1.average=(s1.math+s1.english+s1.computer) /3.0;
        if (i==1) max=s1;
        if (max.average < s1, average)
            max=s1;
    }
    printf("num:%d, name:%s, average:%.2f\n",max.num, max.name, max.average);
    
    return 0;
}

/*
在程序首部定义了结构类型 struct student,其中的成员分别代表学生的基本信息项,。在main()中用此结构类型定义了两个结构变量s1、max

结构变量可以通过 结构成员操作符"." 对其某个成员进行引用

如果两个结构变量的类型相同,也可以直接赋值,如max=s1,将一个结构变量的所有成员值都赋值给另一个
*/

结构能够把有内在联系的不同类型的数据汇聚成一个整体,使它们相互关联;同时结构又是一个变量的集合,可以按照对基本数据类型的操作方法单独使用其成员变量。

结构类型定义的一般形式为:

struct 结构名	{
    类型名 结构成员名1;
    类型名 结构成员名2;
    ...
    类型名 结构成员名n;
}

/*在struct之后,自行命名一个结构名,struct与结构名两者合起来共同组成结构类型名,如struct student

大括号里的内容是结构所包括的是结构成员

第十章 函数与程序结构

函数设计时应减少全局变量的使用。应采用定义局部变量作为函数的临时工作单元,使用参数和返回值作为函数与外部进行数据交换的方式。只有当确实需要多个函数共享的数据时,才定义为全局变量

//用递归函数实现求n!
include <stdio.h>
double fact(int n);

int main ()	{
    int n;
    
    scanf("%d",&n);
    printf("%f",fact(n));
    
    return 0;
}

double fact(int n)	{
    double result;
    if (n==1 || n==0)	//递归出口
        result = 1;
    else
        result = n*fact(n-1);	//函数递归调用
    	//注意不能写成fact(n) = n*fact(n-1)
    return result;
}

/*fact()函数中,函数自己调用自己的形式称为函数的递归调用

递归函数编程时,要抓住递归方法的两个要点:递归出口与递归调用式子
fact()函数的核心语句if-else体现的就是这两个要点

*/

任何递归函数都必须包含条件,来判断是否要递归下去,一旦结束条件成立,递归克隆应该不再继续,以递归出口值作为函数结果,然后返回,结束一个递归克隆函数体。

递归的实质是把问题简化成形式相同、但较简单一些的情况,程序书写时只给出统一形式,到运行时再展开

10.2.3 递归程序设计

两个关键点:

  • 递归出口:递归的结束条件,到何时不再递归调用下去
  • 递归式子:递归的表达式,如fact(n) = n*fact(n-1)

递归程序设计的技巧性要求比较高,关键是归纳出递归式子,不同的问题其递归式子也不同,需要具体分析,然后确定递归的尽头——递归出口,在编写程序时只给出运算规律,具体实现细节应该让计算机去处理。千万不要钻到细节的实现上去

10.3 宏基本定义

宏 #define 用来定义一些符号常量,可以方便程序的编制

宏定义的格式:

#define  宏名  宏定义字符串
/*
#表示define在编译预处理中起作用,不是真正的C语句,所以行尾无需跟分号
常采用大写字母作宏名,宏名中间不能有空格
宏定义字符串时宏名对应的具体实现过程,可以是任意字符串,中间可以有空格,以回车符作结束

在程序编译时,所有出现宏名的地方,都会用宏定义字符串来替换。所以宏也称为宏替换

举例:
#define PI 3.1415926
#define TRUE 1
#define FALSE 0
*/

宏的用途包括:

  • 符号常量,如PI、数组大小定义,以增加程序的灵活性
  • 简单的函数实现(参数使用必不可少)
//带参数的宏定义
#include <stdio.h>
#define MAX(a,b)	a>b? a:b
#define SQR(x)	x*x
int main()	{
    int x, y;
    scanf("%d%d", &x, &y);
    x = MAX(x,y);	//引用宏定义
    y = SQR(x);		//引用宏定义
    printf("%d %d \n", x, y);
    
    return 0;
}

/*宏引用形式 和 函数调用 的实现过程完全不同。
宏替换在程序编译预处理时完成,对于MAX(x,y)的编译预处理,首先用变量名x和y分别替换a、b,然后再用包含x、y的条件表达式替换MAX。编译结束后,程序汇总MAX(x,y)便消失

如果定义函数max(x, y),对它的处理要到程序执行时才进行,首先进行参数传递,把实参值复制给形参a和b,然后主函数暂停执行,去执行函数max(),等求出较大值后,通过return语句返回,主函数再继续运行

函数调用时,如果实参是表达式,要先计算表达式,再把结果值传递过去
宏替换不作计算,直接替换进去

10.3.4 文件包含(include)

文件包含的作用是把指定的文件模块内容插入到#include所在的位置,当程序编译连接时,系统会把所有#include指定的文件拼接生成可执行代码,include不是真正的C语句

对于复杂问题常常有大量宏定义,并被多个程序使用,自定义头文件是一个很好的解决办法,避免了多处重复定义相关宏,并能做到定义的一致性,一旦定义好写成头文件,

C的编译预处理功能主要包括文件包含(#include)、宏定义(#define)、条件编译

一般的程序经过编译后,所有的C语句都生成到目标程序中,如果只想把源程序中一部分语句生成目标代码,可以使用条件编译,可以为一个程序提供多个版本,不同的用户使用不同的版本

#define FLAG 1

#if FLAG

​	程序段1

#else

​	程序段2

#endif

条件编译指令均以#开头,与if-else语句完全不同:

​ if-else的两个分支程序段都会被生成到目标代码中,由程序运行时根据条件决定执行哪一段;

​ 而条件编译#if···#else···#endif不仅形式不同,而且它起作用的时刻在编译预处理的时候。一旦经过处理后,只有一段程序生成到目标程序中,另一端被舍弃,#if的条件只能是宏名,不能是程序表达式

​ 条件编译的好处:1.目标代码精简 2.系统代码保护性更好

所有的编译预处理指令都是在编译预处理步骤中起作用

10.4.3 文件模块间的通信(局部变量、全局变量、外部变量)

局部变量从属于函数,仅在函数内部有效

全局变量可以在整个程序中起作用。全局变量只能在某个模块中定义一次,如果其他模块要使用该全局变量,需要通过外部变量的声明,当程序连接时会统一指向全局变量定义的模块。否则不经声明而直接使用全局变量,程序编译时会出现“变量未定义”的错误

​ 对于全局变量来说,还有一种称为外部变量的形式。即全局变量的使用位置先于该全局变量的定义,在使用之前需要声明为外部变量

外部变量声明格式:

​ extern 变量名表;

它只起说明作用,不分配存储单元,对应的存储单元在全局变量定义时分配。

静态全局变量:如果整个程序只有一个文件模块,静态全局变量与一般的全局变量作用完全相同。当程序由多个文件模块构成时,静态全局变量用于限制全局变量作用域的扩展

为避免自己定义的全局变量影响其他人编写的模块,即所谓的全局变量的副作用,C的静态全局变量可以把变量的作用范围仅局限于当前的文件模块中,即使其他文件模块使用外部变量声明,也不能使用该变量

如果一个程序包括多个文件模块,要实现在一个模块中调用另一模块中的函数时,就需要对函数进行外部声明

声明格式:

extern  函数类型  函数名(参数表说明);

/*
extern可以省略,编译程序如果在当前文件模块中找不到函数定义体,自动认为该函数是外部函数
*/

为了避免各文件模块间相互干扰,C允许把函数定义成静态的,以便把函数的使用范围限制在文件模块内

静态的函数在C中也称为内部函数,定义格式为:

static 函数类型 函数名(参数表说明);

第十一章 指针进阶

11.1.2 指针数组的概念

一维指针数组定义的一般格式为:

​ 类型名 *数组名 [数组长度];

关键是要掌握指针数组中,每个数组元素中存放的都是地址,通过数组元素可以访问它所指向的单元

指针数组既可以直接对数组元素进行赋值(地址值)和引用,也可以间接访问数组元素所指向的单元内容

指针数组元素的操作与对同类型指针变量的操作相同

11.1.3 指向指针的指针(二级指针)

定义:类型名 * * 变量名;

int a = 10;
int *p = &a;
int **p = &p;

/*scanf("%d",&a); 表示从键盘接收一个整数存储到内存中&a所指的地址,也就是变量a中
&a表示变量a的地址

12. 文件

程序在实现的过程中,依赖于把数据保存到变量中,而变量是通过内存单元存储数据的,数据的处理完全由程序控制。当一个程序运行完成或中止运行,所有变量的值不再保存。

一般的程序都会有数据输入与输出,如果输入输出数据量较大就会受到限制。

文件可以解决上述问题,它通过把数据存储在磁盘文件中,得以长久保存。当有大量数据输入时,可通过编辑工具实现建立输入数据的文件,程序运行时将不再从键盘输入,而从指定的文件上读入,从而实现数据一次输入多次使用。同样,当有大量输入输出时,可以将其输出到指定文件,不受屏幕大小限制,并且任何时候都可以查看结果文件。一个程序的运算结果还可以作为其他程序的输入,进行进一步加工

实际上,用记事本编辑文件时,输入的数据先是在内存中,保存后,数据才被写入到磁盘文件中

//文件操作的函数
fopen()
fscanf()
fclose()

为了提高数据存取访问的效率,C程序对文件的处理采用缓冲文件系统的方式进行,这种方式要求程序与文件之间有一个内存缓冲区,程序与文件的数据交换通过该缓冲区来进行

文件系统分为缓冲文件系统非缓冲文件系统

缓冲文件系统:进行文件操作时,系统自动为每一个文件分配一块文件内存缓冲区(内存单元),C对文件的所有操作就通过对文件缓冲区的操作来完成。当程序要向磁盘文件写入数据时,先把数据存入缓冲区,然后再由操作系统把缓冲区的数据真正存入磁盘。若要从文件读入数据到内存,先由操作系统把数据写入缓冲区,然后程序把数据从缓冲区读入到内存。

image-20210610191328535

12.1.7 文件处理步骤

(1)定义文件指针

(2)打开文件:文件指针指向磁盘文件缓冲区

(3)文件处理:文件读写操作;

(4)关闭文件

12.2 用户信息加密和校验

C中,基本的文件操作有2个:从磁盘文件中读信息(读操作)和把信息存放到磁盘文件(写操作)中。为了实现读写操作,首先要定义文件指针,然后打开文件即请求系统分配文件缓冲区,接着进行文件读写操作,文件操作完成后要关闭文件。在文件操作中,通过调用系统函数来实现文件的所有操作

附录A C语言基本语法

运算符 名称
* 取指针内容
& 取地址

结构的基本操作:

​ if:

x 是一个可以修改的变量,其类型为struct s

y 是一个类型为struct s

m 是类型struct s 的一个成员变量的名字

v 是一个表达式

​ SO:

​ x:引用整个结构,其类型为struct s

​ y.m:引用结构变量y的成员变量m,其类型为m的类型

​ x.m=v:将表达式v的值保存到结构变量x的成员变量m中,其类型为m的类型

​ x=y:将y的值赋给x,结果的类型为struct s

​ f(y):调用函数f( )并将结构变量y的内容作为参数传递给该函数。在函数f( )的内部,形式参数的类型必须是struct s

​ return y:返回结构变量y的内容,函数的返回值必须被声明为struct s类型

指针的基本操作

x 是一个类型为t的比那辆

pt 是一个指向t类型变量的指针变量

v 是一个表达式

​ SO:

&x:生成一个指向x的指针,表达式的类型为指向t的指针

pt=&x:使得指针pt指向x,表达式的类型为指向t类型变量的指针

pt=NULL:将指针pt设置为空指针(也可以用0表示空)

pt==NULL:判断pt是否为空指针

*pt:取得指针pt指向的值,表达式的类型为t

*pt=v:将表达式v的值保存在pt所指向的位置中,表达式的类型为t

image-20210607143428110

image-20210607143747124

多行注释不能嵌套使用

在复合语句内可以定义局部变量,该局部变量将覆盖 在该复合语句外定义的 同名变量。这些局部变量的作用域限制在定义它们的 复合语句的内部

break语句只能用于for、while、do或者switch语句内部,在遇到break语句之后,这些语句将立即结束执行,计算机将接着执行这些语句后面的语句

continue语句只能用于循环语句内部。当遇到continue语句之后,循环体中continue语句后面的语句将被跳过,计算机将接着开始执行下一次循环

return的第一种常见形式:return;

执行return语句将使得程序的执行流程立刻回到调用者。这种形式的return语句只能用在那些无返回值的函数中

return的第二种常见形式:return 表达式;
这个语句将 表达式的值 ** 作为 函数返回值 返回给调用者,如果计算机执行到函数的最后一条语句,但是还没有遇到return语句,执行流程仍将返回调用者**,就好像已经执行了return语句一样,在这种情况下,函数将不返回值

else子句总和最后一个没有else子句的if语句配对

while (表达式):表达式的求值在循环体执行前进行

switch语句:如果没有default语句,那么计算机将接着执行下面case语句中的语句

字符数组的初始化是一个特例,C允许使用字符串常量来初始化字符数组

char today [] ="Monday";
//定义了一个字符数组today,该数组的初始化值为:'M','o','n','d','a','y'和'\0'

定义指针变量的一般形式为:

类型名	*指针变量名

类型名 指定 指针变量所指向的 数据类型,必须是有效的数据类型

int *pt
//声明了一个指向int类型变量的指针pt

struct point *pt;
//声明了一个指向point结构类型的指针pt


//指向数组的指针  被声明为  指向  该数组所容纳元素类型的  指针
//比如上面声明的指针pt也可以用来指向一个整数数组
char *pt [100];

struct point (*fnPrt) (int);
//声明了一个指向返回值类型为 struct point 的函数的指针,该函数接受一个  整数参数

void* 是通用的指针类型,任何类型的指针都可以保存到void* 类型中,并且随后将其从 void* 类型的指针中取出而不改变原来的值,除此以外,C不允许不同类型指针之前的转换

(5)结构变量定义及初始化

结构的一般声明形式如下:

struct 结构名 {
    类型名	结构成员名1;
    类型名 结构成员名2;
    ...
    类型名 结构成员名n;
} 变量列表;

结构中包含所有声明的成员变量。每个成员变量声明由一个类型名加上一个或者多个成员变量名组成

如果要声明结构变量,可以在结构定义的时候,在结束的分号之前加上一个或者多个成员变量的名字,也可以在定义结构之后使用如下形式的语句声明结构变量

struct	结构名	变量列表;
/*如果在定义结构类型的时候没有指定名字的话,就不能使用上述形式。在这种情况下,必须在定义结构类型的时候声明该类型的所有变量
*/

结构变量初始化可以用一对大括号将结构成员变量的初始值列表包围起来。在声明全局结构变量的时候,每一个成员变量的初始化表达式都必须是常量表达式

//C允许使用一个同类型的结构变量初始化另外一个结构变量
struct date tomorrow = today

(6)联合变量定义和初始化

联合的一般声明形式如下所示:

union 联合名
{
    成员声明
    成员声明
    ...
} 变量列表;

/*上面的形式可以用来定义名为“联合名”的联合,该联合包含 所有列出的成员变量。联合中的所有成员共享同一块内存空间,C编译程序保证分配给联合的内存能够容纳其最大的成员变量*/

如何声明联合变量:

  • 可以在联合定义的时候,在结束的分号之前加上这些变量的名字

  • 也可以在定义联合之后使用如下形式的语句声明联合变量

    union  联合名  变量列表;
    

(7)枚举变量定义和初始化

定义枚举数据类型的一般格式如下:

enum 枚举名 {枚举值1,枚举值2,...} 变量列表;
/*上面的语句形式定义了名为 枚举名枚举类型,其枚举值分别为枚举值1,、枚举值2等。
每一个枚举值应该是一个合法的标识符,或者是一个标识符后面跟上一个等号,再加上一个常量表达式
变量列表本身是可选的,它代表一组该类型的变量(也可以同时初始化)

如果要声明一个枚举变量(假定该枚举类型已经在前面定义过),可以采用如下方式

enum 枚举名 变量列表;

某个枚举变量的值只能是定义时列出的枚举值之一

(8)存储类型及作用域

存储类型用于描述编译程序为变量分配内存的方式,该术语也可以用于描述某个特定函数的使用范围

C中有4种存储类型:auto、static、extern和register

声明时可以省略存储类型,这时编译程序将使用默认的存储类型

作用域用于描述某个特定的标识符在程序中的可见范围

定义在任何函数或者语句块碗面的标识符可以在同一个文件中随后的任意地方被引用

定义在语句块内的标识符只能在该语句块内被引用,因此在该语句块外可以定义同名的标识符

标号和形式参数在整个语句块中都可以引用

标号名、结构名和结构成员名、联合与枚举类型的名字以及变量名和函数名只要求在同类中唯一

(9)函数

当为函数指定存储类型的时候,只能使用关键字 static 或者 extern

声明为static的函数只能在定义该函数的文件内使用

声明为extern(如果不指定存储类型,默认为extern)的函数可以在其他文件中使用

(10)变量

(11)typedef语句

用于给基本数据类型和导出数据类型定义一个新的名字。这个语句本身并不创造新的数据类型,而只是给已经存在的数据类型起一个新的名字,因此编译程序对使用新名字声明的变量按照使用原来名字声明的变量同样的方式对待

//使用typedef语句的一般形式如下:
//typedef 老的变量名 新的变量类型名
    
typedef struct
{
    float x;
    float y;
} POINT;

/*上面的语句给一个结构类型赋予名字POINT,该结构类型包含两个名为x和y的浮点数成员变量。随后可以使用POINT来声明新的变量,如下所示:*/
POINT origin = {0.0,0.0};

    

8.函数定义、调用和原型

如果在参数列表的括号中指定void,那么函数不接受参数

如果函数的参数类型为一维数组,那么在参数列表中不需要说明该数组的长度

在函数定义的前面可以加上关键字inline,该关键字只是编译程序将函数的实际代码插到适当的位置,而不是取调用函数,这样可以获得更快的执行速度

(2)函数调用

函数内部不能修改实际的参数,如果给函数传递一个指针参数,函数内部可以对该指针指向的位置进行修改,但是不能对实际的指针进行修改

9.预处理指令

(1)#define指令

//#define指令一般形式:
#define  宏名  宏定义字符串

/*定义了一个名为“宏名”的宏,并将该宏与其名字后的第一个空格后直到该行结束的字符串等价起来。C语言预处理器将用这个字符串替换随后程序中任何位置出现的宏名*/

//#define指令的另一种形式:
#define 宏名 (参数1, 参数2, ..., 参数n)宏定义字符串
/*定义了一个名为“宏名”的宏,该宏接收一组参数,在随后的程序中任何出现宏名的地方,预处理器将使用后面的宏定义字符串替换该宏名,并使用实际的参数替换宏定义字符串中的参数*/

(2)#if指令

// #if指令的一种常用形式如下所示
#if 常量表达式
    程序段
#endif
        
/*预处理器将对常量表达式求值,如果结果为非0,那么#if和#endif之间的语句将被处理。否则,预处理器和编译程序都不会处理这些语句*/

(3)#ifdef指令

//#ifdef语句的一般使用形式如下:
#ifdef 标识符
	程序段
#endif
        
/*如果标识符代表的宏已经被定义过了(可能是通过#define语句,也可能是通过命令行上的-D命令选项),#ifdef和#endif之间的语句将被编译,否则,这些语句将被忽略。如同#if指令一样,#ifdef指令后面也可以有#elif指令和#else指令

与#ifdef相对应的还有#ifndef指令,意义正好和#ifdef相反*/

(4)#include 指令

完。

《啊哈C语言》笔记

啊哈C语言

学计算机究竟是学什么呢?答案是逻辑思维和编程思维

//选择排序
#include <stdio.h>
#include <stdlib.h>
int main()	{
    int a[6], i, t, j;
    for(i=1;i<=5;i++)	{
        scanf("%d",&a[i]);
    for(i=1;i<=4;i++)	{
        for(j=1;i<=5;j++)	{
            if(a[i]>a[j])	{
                t=a[i];a[i]=a[j];a[j]=t;
            }
        }
    }
    }
    
    for(i=1;i<=5;i++)
        printf("%d ", a[i]);
	return 0;
}
//给一个字符变量赋值
char a='x';

%c在C语言中代表字符型格式符
%s在C语言中代表字符串型格式符

用scanf进行字符串读入时,遇到空格就提前终止了
用gets进行读入时却可以读入一整行

使用puts(a)输出时,会在末尾自动换到下一行,相当于printf("%s\n", a)
    
两个字符串的比较可以用函数strcmp( )
strcmp(a, b)就是比较字符串a和字符串b在字典中的顺序。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM