目录
- 零,引言
- 0.1,简介
- 0.2,程序结构
- 0.3,关键字
- 0.4,注释符
- 一,数据类型及基本运算量
- 1.1,数据类型
- 基本数据类型
- 构造数据类型
- 1.2,常量
- 直接常量
- 符号常量
- 1.3,变量
- 1.4,库函数
- 数学函数
- 输入/输出函数
- 1.5,运算符及表达式
- 运算符的种类
- 运算符的结合性
- 算数运算符和算数表达式
- 强制类型转换
- 赋值运算中的自动类型转换
- 二,顺序结构程序设计
- 2.1,结构化程序设计概述
- 2.2,C语言概述
- 2.3, 赋值语句
- 2.4,数据输入/输出函数调用语句
- 字符输入/输出
- 格式输入/输出
- 2.5,选择结构程序设计
- 2.6,循环结构程序设计
- 三,数组
- 3.1,概念
- 3.2,定义
- 3.3,初始化
- 一维数值数组的初始化
- 二维数值数组的初始化
- 3.4,数组元素的使用
- 3.5,字符数组的使用
- 字符数组的输入/输出
- 字符串处理函数
- 四,用户自定义函数
- 4.1,用户自定义函数的种类
- 4.2,函数的定义
- 4.3,被调函数的声明
- 4.4,函数的调用
- 4.5,函数调用的参数传递
- 函数的形参和实参的特点
- 参数传递方式
- 函数的嵌套调用
- 函数的递归调用
- 4.6,数组作函数参数
- 数组元素作函数实参
- 数组名作函数参数
- 4.7,变量的作用域
- 局部变量
- 全局变量
- 4.8,变量的存储类型
- 用auto声明动态局部变量
- 用static声明静态局部变量
- 用register声明寄存器变量
- 用extern声明外部变量
- 五,编译预处理
- 5.1,宏定义
- 无参宏定义
- 带参宏定义
- 5.2,文件包含
- 六,用户自定义数据类型
- 6.1,结构体类型
- 定义
- 结构体类型变量的成员变量
- 6.2,共用体类型
- 定义
- 使用
- 6.3,枚举类型
- 定义
- 使用
- 6.4,类型声明符typedef
- 七,指针
- 7.1,指向变量的指针变量
- 指针运算符
- 指针变量作为函数参数
- 7.2,指向数组的指针变量
- 通过指针变量引用数组元素
- 两个指针变量之间的运算
- 相减运算
- 关系运算
- 指向数组的指针变量作为函数参数
- 指向多维数组的指针和指向多维数组的指针变量
零,引言
0.1,简介
面向过程的语言
编译方式
高级语言
0.2,程序结构
//例子
// 凡是在程序中调用库函数时,都必须包含该函数定义所在的头文件
//scanf和printf是标准输入/输出函数,其头文件为stdio.h
#include<stdio.h>
int main()
{
printf("这是一个简单的C程序!\n");
return 0;
int x; //程序中所有用到的变量都必须先声明,再使用
scanf("%d",&x);
printf(“x is %d",x);
}
0.3,关键字
- 类型声明符:用于定义(或声明)变量,数组,自定义函数或自定义数据类型
- 语句定义符:用于表示一个语句的功能,如if,for,while等
- 预处理命名字:如include
0.4,注释符
/*
块注释
*/
//行注释
//注释:可使编译程序跳过处理
一,数据类型及基本运算量
1.1,数据类型
分为4类:
- 基本数据类型
- 构造数据类型
- 指针类型
- 空类型
基本数据类型
C语言的基本数据类型分为整型,浮点型,字符型和枚举类型。其中,整型又分为基本整型,短整型,长整型和双长整型;浮点型分为单精度浮点型和双精度浮点型。
构造数据类型
根据已定义的一个或多个数据类型用构造的方法来定义的数据类型。
- 数组类型([])
- 结构体类型(struct)
- 共用体类型(union)
- 指针类型(*)
- 空类型(void)
1.2,常量
在整个应用程序运行期间值不会发生变化的量。
整型常量,浮点型常量,字符型常量,字符串型常量等。
直接常量
数值型常量(常数)
字符型常量:由单引号括起来的单个普通字符或转义字符
字符串型常量:由双引号括起来的字符
注:字符型常量占一个字节的内存空间,而字符串型常量占的内存字节数等于字符串中的字符数加1;增加的一个字节中存放字符'\0'(ASCII码为0的字符),这是字符串结束的标志。
符号常量
用一个标识符来表示一个常量
#define 符号常量名 常量
#define是一条预处理命令,称为宏定义命令
注:符号常量不占内存,是临时符号,在预编译时,用值替换名称。
1.3,变量
在程序运行期间,值可以改变的量(一个名称和相应的数据类型(占用空间的大小及值的范围))
先定义后使用
定义与初始化的区别:初始化比定义多了个赋值。(在执行阶段分配内存赋值
1.4,库函数
由C编译系统提供,用户无需定义,也不必在程序中声明,只需在文件开头添加相应的#include指令。
数学函数
#include <math.h>
输入/输出函数
#include <stdio.h>
1.5,运算符及表达式
C语言的运算符不仅具有不同的优先级,而且还受运算符结合性的制约。
运算符的种类
- 算数运算符
- 赋值运算符
- 逗号运算符
- 关系运算符:也称为比较运算符;是双目运算符,左结合性;例:(>)等等
- 逻辑运算符:非!,与&&,或||
- 条件运算符:三目运算符,右结合性;用于条件求值(?:)
- 指针运算符
- 求字节数运算符:用于计算数据类型所占的字节数(sizeof())
- 特殊运算符:有函数调用,强制类型转换,数组下标,结构体成员或共用体成员等
- 位操作运算符
优先级:逻辑运算符!,算术运算符,关系运算符,逻辑运算符&&,||,赋值运算符
运算符的结合性
左结合性(自左至右)和右结合性(自右到左)
多数运算符具有左结合性;单目运算符,三目运算符,赋值运算符具有右结合性。
算数运算符和算数表达式
所有算数运算符都是双目运算符。
运算结果的数据类型应该与运算量的类型相同;如果两种不同,运算结果的数据类型与表示范围大,精度高的数据保持一致。
自增,自减运算符均为单目运算符。
强制类型转换
(类型声明符)(表达式) //单个变量可以不加括号
赋值运算中的自动类型转换
字符型赋给整型,由于字符型为一个字节,故将字符的ASCII码值放到整型量的低八位中,高位为0。
二,顺序结构程序设计
2.1,结构化程序设计概述
结构化程序的特点:
- 只有一个入口,一个出口
- 无死语句(永远也执行不到的语句)
- 无死循环(永远也不结束的循环)
设计方法:
- 自顶向下,逐步细化:先考虑整体结构,再逐步将问题分解具体化。
- 模块化
三种基本结构:
顺序结构
选择结构
循环结构:分为当型循环(while型循环)和直到型循环(until型循环)
2.2,C语言概述
C语句分为:
- 表达式语句
- 函数调用语句
- 控制语句:分支选择语句,循环语句,转向语句(goto语句,break语句,continue语句,return语句)
- 复合语句:把多个语句用括号{}括起来组成的一个语句
- 空语句:只有分号组成的语句。什么也不执行的语句。
2.3, 赋值语句
由赋值表达式末尾加上分号构成的语句。
变量=表达式 //格式
注:变量定义时不允许连续给多个变量赋初值,而赋值语句允许
int a=5,b=5; //变量定义
a=b=5; //赋值语句
强调:在C语言的函数中,变量的定义必须出现在块的开头,而不是在块的中间或结尾。
2.4,数据输入/输出函数调用语句
字符输入/输出
字符输出函数putchar():在显示器上输出单个字符
putchar(字符型运算量);
//举例
putchar('a'); //输出字符a
键盘输入函数getchar():在键盘上输入一个字符
getchar();
//举例
char c;
c = getchar();
格式输入/输出
printf函数和scanf函数
printf("格式控制字符串",输出列表);
/*格式字符串的一般形式如下:
%[标志][输出最小宽度][精度][长度]类型
标志:为-,+,#,空格四种
输出最小宽度:用十进制整数来表示输出的最少位数,若多于精度则溢出,少则补空格
精度:以"."开头,后跟十进制整数
长度:格式符有h和l两种。h短整型输出,l长整型输出
类型
*/
//默认不换行
scanf("格式控制字符串",地址列表);
/*
地址由地址运算符”&“后跟变量名组成
&是一个取地址运算符,&a是功能为求变量的地址的表达式
%[*][输入数据宽度][长度]类型
"*"符:表示该输入项被读入后不赋给相应的变量,即跳过
宽度:用十进制整数指定输入的宽度(即字符数)
长度:格式符有h和l两种。h短整型输出,l长整型输出
*/
//注:在输入字符数据时,若格式控制串中没有非格式字符,则认为所有输入的字符均为有效
//注:如果格式控制串中有非格式字符,则输入时也要输入该非格式字符,否则结果会出错
2.5,选择结构程序设计
//关系表达式,逻辑表达式的格式如下:
表达式 关系运算符 表达式
表达式 逻辑运算符 表达式
//if语句,可嵌套
if(表达式)
{
语句1}
else if(表达式)
{
语句2
}
else{
语句3
}
//条件运算式,如果表达式1为真,则返回表达式2的值,否则返回表达式3的值
表达式1 ? 表达式2 : 表达式3
max = (a>b) ? a : b //举例
//switch语句,可用于多分支选择
switch(表达式)
{case 常量表达式1::语句1;
case 常量表达式2: 语句2;
```
//case常量表达式作为条件,满足则执行其后的所有的case,不能自动跳出整个switch
default :语句n+1; //可省略
//若以上case条件都不符合,则执行default后的语句
}
int main() //举例
{
int a = 2;
switch(a)
{
case 1:printf("one ");
case 2:printf("two ");
case 3:printf("three ");
case 4:printf("four ");
default :printf("zero ");
}
}
break语句:用于跳出,可跳出当前循环,也可用于跳出switch语句。
continue语句:跳过本次循环
2.6,循环结构程序设计
//for循环
for(表达式1;表达式2;表达式3)
{ 循环体语句;
}
for(循环变量赋初值, 循环控制条件, 循环变量增加) 循环体语句;
/*
先求解表达式1,求解表达式2,若其值为真(非0),则执行循环体语句,然后求解表达式3
,继续求解表达式2,接续下一次循环;若其值为假(0),则结束循环,执行for语句之后的。
*/
//while循环
while(表达式)
{ 语句;
}
//do-while循环
do
{ 语句;}
while(表达式);
三,数组
3.1,概念
在C语言中,数组属于构造数据类型。必须先定义。
把一批具有相同属性的数据用一个统一的名称来代表,用下标来区分不同的元素。
分为:
- 数值数组
- 字符数组
- 指针型数组
- 结构体类型数组等
3.2,定义
//格式
类型声明符 数组名 [常量表达式];
类型声明符 数组名 [常量表达式] [常量表达式]·····;
int a,b[10],c[2][5] //同一个类型声明多个数组和变量的例子
注:对于同一个数组,其所有元素的数据类型都是相同的
- 数组定义后就为数组中的各元素在内存中分配了一片连续的存储单元 ,数组名就是这段连续存储单元的首地址。
- 定义数组时,不能使用变量,函数或表达式,但可以直接使用直接常量,符号常量或常量表达式。
- 允许在同一个类型声明中,声明多个数组和变量
- 数组中的元素必须是同一类型,不允许在同一数组同时存放不同类型的数据
3.3,初始化
数组的初始化赋值是指在数组定义时给数组元素赋初值。数组初始化是在编译阶段进行的。
一维数值数组的初始化
类型声明符 数组名[常量表达式] = {值, 值,```, 值};
//举例
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int a[10] = {0,1,2} //部分赋值,后面的自动赋0值
int a[] = {1,2,3,4,5} //自动判定个数
小规定:
- 可以只给部分元素赋初值(给前面部分元素赋值,后面的剩余元素由系统自动赋0值。
- 只能给元素逐个赋值,不能给数组整体赋值。
- 如果给全部元素赋值,可以不给处数组元素的个数。
二维数值数组的初始化
//按行分段赋值 例子:
int a[5][3] = {{20,57,29}, {51,16,62}, {11,51,62}, {15,62,72}, {15,67,73}};
//按行连续赋值 例子:
int a[5][3] = {20,57,29,51,16,62,11,51,62,15,62,72,15,67,73};
注意事项:
- 可以只对部分元素赋初值,未赋初值的元素自动取0值。
- 如对全部元素赋初值,则第一维的长度可以不给出。
- 二维数组可以看作时由一维数组的嵌套而构成的。
3.4,数组元素的使用
下标只能为整型常量或整型表达式。若为小数,C编译系统将自动取整。
注意:
- 定义时下标只能是常量,使用时下标可以是常量,变量或表达式。
- 使用数组元素时,数组名,类型和维数必须与定义数组时保持一致。如二维数组引用必须给出两个下标。
- 使用数组元素时,下标值必须在其范围内。
3.5,字符数组的使用
在C语言中没有专门的字符串变量,用数组来存放字符型数据(一个字符串)被称为字符数组。
注:当把一个字符串存入一个数组时,也把结束符'\0'存入数组,并以此作为该字符串是否结束的标志。'\0'是由C编译系统自动加上的。
//初始化赋值
char c[] = {'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm'};
char c[] = {"c program"};
char c[] = "c program";
字符数组的输入/输出
int i;
char c[5];
for(i = 0;i<5; i++)
scanf("%c", &c[i]);
for(i = 0; i<5; i++)
printf("%c\t", c[i]);
printf("\n");
return 0;
//使用格式字符串”%c“,逐个输入/输出字符
char s[15];
printf( "input string: \n");
scanf("%s", s);
printf("%s\n", s);
return 0;
//使用格式字符串”%s“,一次性地输入/输出一个字符串
/*注意:使用%s时,用scanf函数输入字符串需要注意不能含有空格,否则将以空格作为串的结束符。
This is an example.
This
Press any key to continue
*/
字符串处理函数
#include <stdio.h>
char s[] = "C program";
//把字符数组中的字符串输出到显示器,即在屏幕上显示该字符串
puts(s); //C program
//输入,不以空格作为字符串输入结束的标志,而以回车作为输入结束
gets(s); //study 1
puts(s); //study 1
还有(引用string.h):
- 字符串连接函数strcat()
- 字符串拷贝函数strcpy()
- 字符串比较函数strcmp()
- 测字符串长度函数strlen()
四,用户自定义函数
4.1,用户自定义函数的种类
- 有返回值函数和无返回值函数
- 无参函数和有参函数
4.2,函数的定义
类型标识符 函数名([形式参数列表])
{
声明部分;
执行部分;
[return 表达式;]
}
//在此处[ ]内的是可选加的
注:不能嵌套定义
4.3,被调函数的声明
对于用户自定义函数,不仅要在程序中定义函数,还要注意声明。
//函数声明的一般形式
类型声明符 被调函数名( 类型 形参名, 类型 形参名, ·····); //第一种方式
类型声明符 被调函数名(类型, 类型, ····); //第二种方式
//举例子1:
char str(int a); //char str(int);
int main()
{
····
return 0;
}
char str(int a)
{
····
}
//举例子2:
int max(int a, int b)
{if(a>b)
return a;
else
return b;
}
int main()
{
int x,y,z;
scanf("%d%d", &x, &y);
z= max(x, y);
printf("maxmum = %d\n", z);
return 0;
}
如上在一个函数中调用另一个自定义函数时,如果被调函数在主调函数之后定义,那么在主调函数中调用被调函数之前应对该被调函数进行声明。
4.4,函数的调用
一个C源文件必须有且仅有一个主函数main,C程序的执行总是从main函数开始,完成对其他函数的调用后再返回到main函数,最后由main函数结束整个程序。
main函数可以调用其他函数,而不允许被其他函数调用;而其他函数必须通过调用才能执行函数体,完成相应的函数功能。
//函数调用的一般形式
函数名(实参列表) //各实参无需指定类型,多个实参之间用逗号分隔。
//实参列表中的参数可以是常量,变量,函数,表达式或其他构造类型的数据。
函数调用的方式:
- 函数表达式
- 函数语句:例如printf函数
- 函数参数
4.5,函数调用的参数传递
函数的形参和实参的特点
- 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。且只在函数内部有效。
- 实参在进行函数调用时,必须具有确定的值,以便把这些值传递给形参。
- 实参和形参的数量,类型,顺序应严格保持一致 。
参数传递方式
- 单向值传递:实参是常量,变量,函数,表达式时。
- 双向地址传递:实参是数组名或指针时。
函数的嵌套调用
//举例
#include <stdio.h>
long f1(int p)
{
int k,r = 0;
int f2(int);
for(k=1;k<=p;k++)
r = r+k;
return f2(r);
}
int f2(int q)
{
int i,c = 1;
for(i = 1;i <= q;i++)
c = c*i;
return c;
}
int main()
{
int i,s = 0;
for (size_t i = 0; i < 4; i++)
{
s=s+f1(i);
printf("s=%d\n",s);
return 0;
}
}
函数的递归调用
直接或间接调用自身。
4.6,数组作函数参数
数组作为函数的参数使用,进行数据传递
数组元素作函数实参
- 要求数组元素的类型和函数形参变量的类型一致
- 对数组元素的处理是按简单变量对待
注:在简单变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。
数组名作函数参数
- 要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组定义。
- 在数组名做函数参数时,函数执行过程中把实参数组的首地址赋给形参数组名。
- 形参数组和实参数组为同一数组,共同拥有一段内存空间。
4.7,变量的作用域
需要注意的:如果同一个源文件中,在不同作用域可以定义同名变量,在使用时,作用域小的变量会屏蔽作用域大的变量。
按作用域范围分为:局部变量和全局变量。
局部变量
也称为内部变量。局部变量在函数体中的声明部分定义(局部变量定义的同时直接被声明,亦称为定义)。作用域仅限于本函数内。
全局变量
也称为外部变量,是在函数外部定义的变量。作用域是整个源文件。
因此也出现了全局变量定义前后的不同,在之前使用全局变量则需要对其声明(全局变量的声明符为extern);在之后使用全局变量可不加以声明。
//举例
void f1()
{
····
extern x,y; //声明后面的全局(外部)变量 x,y
····
}
float x,y; //外部变量
int main()
{
····
return 0;
}
//对于全局变量x,y,函数f1和main都可以使用,区别在于f1加以声明后才可以使用。
4.8,变量的存储类型
根据变量值存在的时间(即生存期)角度来分:静态存储方式和动态存储方式。
静态存储方式:在程序运行期间分配固定的存储空间。全局变量全部存放在静态存储区,从程序开始执行分配存储区,到执行完毕释放。
动态存储方式:在程序运行期间根据需要进行动态分配存储空间的方式。在函数开始调用时分配,到函数结束时释放。
存储类别 类型名 变量名;
用auto声明动态局部变量
函数中的局部变量,如不专门声明,都是动态地分配存储空间的。这类局部变量称为自动变量。
auto int b,c=3; //关键字auto可以省略,隐含为自动存储类别,属于动态存储方式。
用static声明静态局部变量
希望函数中的局部变量的值在函数调用结束后不消失而保留原值时可用。
- 静态局部变量属于静态存储类别。
- 在编译时赋初值,即只赋初值一次,不像自动变量赋初值是在函数调用时进行,每调用一次函数重新赋一次初值,相当于执行一次赋值语句。
- 在定义局部变量时不赋初值的话,对静态局部变量来说,编译时会自动赋初值0(对于数值型变量)或空字符(对于字符变量);而对自动变量来说,则它的值时一个不确定的值。
用register声明寄存器变量
为了提高效率,C语言允许将局部变量的值放在CPU中的寄存器,这种变量称为寄存器变量,用关键字register进行声明。
register int i,f=1; //举例
使用说明:
- 只有局部自动变量和形参可以作为寄存器变量。
- 计算器数量有限,不能定义过多。
- 现在的计算机能够识别使用频繁的变量,从而自动地将这些变量放在寄存器中,而不需要程序设计者指定。
用extern声明外部变量
和定义的含义不同,使用前请确定已经在某处定义了该变量。
适用于在要使用处之后定义的外部变量,在此处对其声明后,即可丛声明处起,合法地使用该外部变量。
//举例
#include <stdio.h>
int max(int x,int y)
{
int z;
z = x>y?x:y;
return(z);
}
int main()
{
extern int a,b; //声明举例
printf("%d\n",max(a,b));
return 0;
}
int a=13,b=-8; //外部变量
五,编译预处理
C语言特有的功能。当对一个源文件进行编译时,系统将自动引用预处理程序对源文件中的预处理部分进行处理,处理完毕后,自动对源文件进行编译。
5.1,宏定义
允许用一个标识符来表示一个符号串,称为宏。
宏名:被定义为宏的标识符。
宏代换(宏展开):由预处理程序自动对程序中所有出现的宏名用宏定义中的符号串去代换。
宏分为有参数和无参数两种;
无参宏定义
#define 标识符 符号串
#define m (y*y+3*y) //举例
注意事项:
符号串中可以含任何字符,可以是常数,也可以是表达式;在宏展开时以该符号串取代宏名(在行末不必加分号,若加上分号则连分号也一起代换),仅是简单的代换,预处理程序对它不作任何检查,如果宏定义有错误,也只能在编译已被宏展开后的源文件时发现。
宏定义必须写在函数之外,其作用域为从宏定义命令开始到源文件结束为止。如果要终止其作用域可使用#undef命令。例如:
#define PI 3,14,159
int main()
{····
return 0;
}
#undef PI //此处要注意宏定义区分大小写
f1()
{
····
}
注:本书195页的此处有一处错误,可能时C语言版本的问题,目前版本的C语言的宏定义是区分大小写的。所以使用undef时需要注意对应。
带参宏定义
C语言允许宏带有参数。在宏定义中的参数称为形参,在宏调用中的参数称为实参。
对带参数的宏,在调用中不仅要宏展开,还要用实参去代换形参,而不是值传递。
//带参宏定义的一般形式
#define 宏名(形参列表) 符号串
//带参宏调用的一般形式
宏名(实参列表);
//举例
#include <stdio.h>
#define max(a,b) (a>b)?a:b
int main()
{
int x,y,max;
printf("input two numbers: ");
scanf("%d%d",&x,&y);
max = max(x,y);
printf("max = %d\n",max);
return 0;
}
注意事项:
- 带参宏定义中,宏名和形参列表之间不能有空格出现。
- 在带参宏定义中,形参不分配内存单元,因此不必进行类型定义。
- 在宏定义中的形参只能是标识符。
- 宏代换中对实参表达式不进行计算而直接地照原样代换。
- 为了使宏代换能够完成与函数调用预期相同的功能,宏定义中的符号串应加括号,字符中出现的形参也应加括号。但还需注意它俩有着本质上的区别。
5.2,文件包含
用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。
#include "文件名"
#include <文件名>
//两种皆可,举例
#include "stdio.h"
/*
两种的区别:使用尖括号表示在包含文件目录中去查找(包含目录式由用户在设置环境时设置的),而不在源文件目录中去查找;使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。
*/
六,用户自定义数据类型
6.1,结构体类型
由若干成员组成,每一个成员可以是一个基本数据类型或者又是一个构造类型,各成员可以取不同的数据类型。在声明和使用之前必须先定义它。
定义
//一般形式
struct 结构体类型名
{
成员变量列表
};
//成员变量的声明
类型声明符 成员变量名;
//例如
struct stu
{
int num; //成员变量名的声明
char name[20];
float score;
};
//先定义结构体类型,再定义结构体变量,如下:
struct stu
{
int num;
char name[20];
float score;
};
struct stu student1,student2;
//在定义结构体类型的同时定义结构体变量,如下:
struct 结构体类型名
{
成员变量列表
}结构类型变量列表;
//举例
struct stu
{
int num;
char name[20];
float score;
}student1,student2;
//直接定义结构体变量,如下:
struct
{
成员变量列表
}结构类型变量列表;
//举例
struct
{
int num;
char name[20];
float score;
}student1,student2;
//嵌套的结构体类型,举例:
struct date
{
int month;
int day;
int year;
};
struct
{
char name[20];
struct date birthday; //被声明为结构体date类型的
}person1, person2;
结构体类型变量的成员变量
在程序中使用结构体类型变量时,除了允许具有相同类型的结构体类型变量相互赋值以外,一般对结构体类型的使用(包括赋值,输入,输出,运算等),都是通过结构体类型变量的成员变量来实现的。
//表示方法的一般形式
结构体类型变量名.成员变量名
//举例
student1.num
person1.birthday.month //多级结构体类型的逐级查找
//该变量的用法,举例
#include <stdio.h>
#include <string.h>
int main(){
struct stu
{
int num;
char name[20];
float score;
}student1,student2;
student1.num = 2012102;
strcpy(student1.name,"zhang li");
printf("input score\n");
scanf("%f",&student1.score);
student2 = student1;
printf("number=%d\n",student2.num);
printf("name=%s\n",student2.name);
printf("score=%f\n",student2.score);
return 0;
}
//初始化及整体赋值,例如:
struct stu
{
int num;
char name[20];
float score;
}student1,student2 = {2012102,"zhang li",88.5};
//结构体类型数组的定义和使用,例如:
struct stu
{
int num;
char name[20];
float score;
}student[3] = {{2012101,"li ping",54},{2012102,"zhang ping",52},{2012103,"he fang",92.4}};
int main()
{
int i,c =0;
float ave,s = 0;
for(i = 0;i<3;i++)
{
s+=student[i].score;
if(student[i].score < 60) c+= 1;
}
printf("s=%f\n",s);
ave = s/3;
printf("average = %f\ncount = %d\n", ave, c);
return 0;
}
6.2,共用体类型
使几个不同的变量共享同一段内存的结构,称为共用体类型的结构。
定义
//共用体类型变量的一般形式:
union 共用体名
{
成员变量列表
}共用体变量列表;
//举例
union Data
{
int i;
char ch;
}a,b;
注:此处和结构体的定义相似,但含义有所不同。结构体变量所占内存长度是各成员占用的内存长度之和,每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度,所有成员共用同一内存单元。
使用
一些要注意的:
同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一个成员,而不能存多个。
初始化时列表只能有一个常量。
共用体变量中起作用的成员是最后一次被赋值的成员。
共用体类型和结构体类型可以互相参与对方的定义中。
//举例
struct
{
int num;
char name[10];
char job;
union
{
int class;
char posi[10];
}cate;
}p[2];
6.3,枚举类型
被声明为该枚举类型的变量的取值不能超过所定义的范围。
枚举类型的主要用途是为一组相关的常量值取一个有意义的名字,以便于在程序中使用和区分。
定义
//一般形式
enum 枚举名{枚举值列表};
//先定义枚举类型后定义枚举类型变量,举例
enum weekday{sum,mon,tue,web,thu,fri,sat};
enum weekday a,b;
//同时定义枚举类型和枚举类型变量
enum 枚举类型名{枚举值列表}枚举类型变量列表;
//举例
enum weekday{sum,mon,tue,web,thu,fri,sat}a,b;
//直接定义枚举类型变量
enum {枚举值列表}枚举类型变量列表;
//举例
enum {sum,mon,tue,web,thu,fri,sat}a,b;
使用
使用需要注意的:
- 枚举值是常量。
- 枚举类型元素本身由系统定义了一个表示序号的数值,从零开始,顺序递增。
- 只能把枚举值赋给枚举变量,不能把元素的数值直接赋给枚举变量。
- 枚举元素不是字符型常量也不是字符型常量,使用时不要加单引号或双引号。
//举例
enum weekday{sum,mon,tue,web,thu,fri,sat)a,b;
a = sum;
b = mon;
a = (enum weekday)2; //把数值赋给枚举变量,要使用强制类型转换。 相当于a = tue;
printf("%d,%d\n",a,b); //2,1
6.4,类型声明符typedef
为数据类型取别名
typedef 原类型名 新类型名;
//一般的例子
typedef int integer; //整型变量的类型声明就变成了integer
//定义结构体类型
typedef struct stu
{
int num;
char name[20];
float score;
}stud; //以后它就不叫stu了,而是叫stud
//可用宏定义来代替typedef的功能
#defiine integer int; //和上面的那个功能相似
注:
- 宏定义由预处理完成,而typedef则是在编译时完成。
- 宏定义是简单的字符串代换,而typedef是在对类型声明符重新命名。
#define pin1 int *
typedef (int *) pin2;
//试着思考下这两个的区别吧~
七,指针
为了正确地访问内存单元,必须为每个内存单元编号。
内存单元的编号也叫做内存单元的地址,通常也把这个地址称为指针。
指针虽然实际上是一个地址,但它却是一个数据结构的首地址,指向一个数据结构。
在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。
7.1,指向变量的指针变量
指针类型声明符 *指针变量名;
//举例
int *p1; //一个指针变量只能指向同类型的变量,如p1只能指向整型变量
指针运算符
在C语言中,变量的地址是由编译系统分配的,用户并不知道变量地址的具体值。
//取地址运算符&
&变量名;
//使用举例
int a=5,b=6;
int *p = &a; //初始化
p = &b; //赋给
int *pb;
pb = p; //互相赋值
//取内容运算符*
printf("%d\n",*p); //表示指针变量所指的变量;单目运算符,其后跟随必须是指针变量。
指针变量作为函数参数
作用是将一个变量的地址传递到另一个函数中。
//举例
#include <stdio.h>
void swap(int *p1,int *p2) //此处的*是类型声明符
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
/* *p1对pointer_1存放的地址进行取值,指向a(表示对a存储地址中的值进行操作
*p2对pointer_2存放的地址进行取值,指向b(表示对b存储地址中的值进行操作
*/
}
int main()
{
int a,b;
int *pointer_1,*pointer_2;
scanf("%d,%d",&a, &b);
pointer_1 = &a;pointer_2 = &b; //两者存放的都是地址
if(a<b) swap(pointer_1,pointer_2);
printf("max = %d,min = %d\n",a,b);
return 0;
}
7.2,指向数组的指针变量
一个数组是由连续的一块内存单元组成的,数组名就是这块连续内存单元的首地址。
所谓数组的指针,是指数组的起始地址,数组元素的指针是指数组元素的地址。
指向数组的指针变量就是存储数组首地址的变量。
类型声明符 *指针变量名;
//举例
int a[10];
int *p1 = &a[0]; //指向数组元素的初始化
int *p2;
p2 = a; //将数组名赋给指针变量
//p1,p2,a,&a[0]均指向同一单元
通过指针变量引用数组元素
当p的初值为&a[0]时,可得:
- p+i 和 a+i 就是a[i]的地址。
- 指向数组的指针变量也可以带下标,如[p[i]与*(p+i)等价。
//引用数组元素的
#include <stdio.h>
int main()
{
int a[5] ,i ,*p;
for(i = 0;i < 5;i++)
a[i] = i;
for(i = 0;i < 5;i++)
printf("a[%d]=%d\n",i ,a[i]); //下标法
for(i = 0;i < 5;i++)
printf("a[%d] = %d\n",i ,*(a+i)); //指针固定法
p = a;
for(i = 0;i < 5;i++)
printf("a[%d] = %d\n",i ,*(p+i)); //指针固定法
for(i = 0;i < 5;i++,p++)
printf("a[%d] = %d\n",i ,*p); //指针移动法
return 0;
}
两个指针变量之间的运算
相减运算
- 只有指向同一数组的两个指针变量之间才能进行运算。
- 两个指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。
关系运算
- 相等是指向同一数组元素。
- 大于是前者处于高地址位置,小于则相反。
注:指针变量可以实现本身的值的改变,而数组名是数组的首地址,是常量。
指向数组的指针变量作为函数参数
数组名就是数组的首地址,实参向形参传递数组名实际上就是传递数组的地址,形参得到该地址后也值向同一数组。
同样,指针变量的值也是地址,数组指针变量的值即为数组的首地址,当然也可作为函数的参数使用。
#include <stdio.h>
void inv(int *x,int n)
{
int *p,temp,*i ,*j ,m=(n-1)/2;
i =x; j =x+n-1 ;p =x+m;
for( ;i<=p; i++, j--)
{
temp = *i;
*i = *j;
*j = temp;
}
}
int main()
{
int i,a[10] = {3,7,9,11,0,6,7,5,4,2};
printf("the original array:\n");
for(i=0;i<10;i++)
printf("%d,",a[i]);
printf("\n");
inv(a,10);
printf("the array has been inverted:\n");
for(i=0;i<10;i++)
printf("%d,",a[i]);
printf("\n");
return 0;
}
指向多维数组的指针和指向多维数组的指针变量
#include <stdio.h>
int main()
{
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
//以下是地址
printf("%d, %d, %d, %d, %d\n", a, *a, a[0], &a[0], &a[0][0]); //1245008
printf("%d, %d, %d, %d, %d\n", a+1, *(a+1), a[1], &a[1], &a[1][0]); //1245024
printf("%d, %d\n", a[1]+1, *(a+1)+1); //1245028
//以下是值
printf("%d, %d\n", *(a[1]+1), *(*(a+1)+1)); //5
return 0;
}
类型声明符 (*指针变量名)[长度]
//举例
int (*p)[4]; //表示p是一个指针变量,指向包含4个元素的多个一维数组
后面还有关于指针的几个小节和一章文件,一章位运算没写,抱歉。感兴趣的话请自行查找。