------- iOS培训、PHP培训、android培训、java培训、期待与您交流! ----------
说明:
1、 数组
2、 结构体(struct)
3、 公用体(union)
4、 枚举(enum)
5、 指针
(一) 数组
1、 内存中的地址:
1> 以字节为单位的存储空间,每个字节都有一个唯一编号,编号就是地址(每个函数都有一个自己的地址;);
2> 当定义一个变量时,系统会分配一个带有唯一地址的存储单元来存储此变量(第一个字节地址就为该变量的地址);
2、 一维数组:
1> 定义形式:类型数组名[元素个数];
如:int a[5]; //[]中个数为固定值:可以为常量、常量表达式,但不能用变量
2> 一维数组的存储:
① 数组名表整个数组的地址,也就是数组的起始地址(如:数组a的地址为ffc1);
3> 一维数组初始化形式:类型数组名[元素个数]={元素1,元素2…};
如:int a[2] ={8,10};
① 对所有数组元素赋值时,可省略元素个数;
如:int a[] ={2,5,8};
② 元素值列表可是数组所有元素的初值,也可是前面部分元素的初值;
如:int a[4] ={2,4};
③ 数组初始化时赋值方式只能用于数组定义,定以后只能一个元素一个元素的赋值;
如:int a[3]; a[0] = 1; a[1] = 3; a[2]= 5;
4> C语言编译器不对数组下标越界进行检查;
3、 一维数组与函数参数:
1> 一维数组的元素作为函数实参,与同类型的简单变量实参一样,都为单向的值传递,即形参的改变不影响实参;
2> 一维数组的名称作为函数实参,传递的为整个数组,即形参与实参数组完全等同,存放于统一存储空间的同一数组,因此,形参数组修改,实参数组也同时被修改,形参数组的元素可省略;
4、 二维数组:
1> 二维数组定义形式:类型数组名[行数][列数];
如:int a[2][3];//共2行3列,6个元素,
2> 二维数组的存储:
① 数组的地址为首地址:a = a[0] = &a[0][0],a[1]=&a[1][0];
3> 二维数组的初始化:
① 按行初始化:
如:int a[2][3]= {{2,2,4},{2,5,8}};
② 按存储顺序初始化(先存放第1行,再存放第2行):
如:int a[2][3]= {2,2,3,5,8,5};
③ 对部分元素初始化:
如:int a[2][3]= {{2},{3,6}};
int b[3][3] ={{},{ , , 2},{1,5,9}};
④ 只初始化部分元素(可省略行数,但不能省略列数):
如:int a[][3] ={1,2,3,4,5,6};
int b[][3] = {{1,2,3},{2,4},{}};
(二) 结构体(struct):
1、 允许内部的元素是不同类型的(数组:只能有多个同类型的数据构成);
2、 结构体定义:
1> 结构体内部的元素,也就是组成成分,称为”成员”;
2> 结构体类型一般定义格式:
具体举例:
3、 结构体变量的定义:
1> 先定义结构体类型,再定义变量;
如:structPerson per;
2> 定义结构体类型的同时定义变量:
3> 直接定义结构体类型变量,省略类型名:
4、 结构体注意点:
1> 不允许对结构体本身递归定义;
2> 结构体内可包含别的结构体;
3> 定义结构体类型,只是说明了该类型的组成情况,并没有给它分配存储空间,就像系统不为int类型本身分配空间一样。只有当定义属于结构体类型变量时,系统才会分配存储空间给该变量;
4> 结构体变量占用的内存空间是其成员所占内存只和,且各成员在内存中按定义的顺序一次排列;
5、 结构体的初始化:
1> 初始化:
2> 注意:结构体定义与赋值不能分开进行;
6、 结构体的使用:
1> 一般对结构体变量操作是以成员为单位进行的,引用的一般形式为:结构体变量名.成员名;
如:per.age =27; // .称为成员运算符,在所有运算符中优先级最高;
2> 若某成员也是结构体变量,可连续使用成员运算符”.”访问最低一级成员;
如:per.birthday.year= 1986;
3> 相同类型的结构体变量间可进行整体赋值;
如:structPerson per1 = {“jim”,27};
Struct Person per2= per1;
1> 定义:结构体数组有3种定义方式;
① 先定义结构体类型,再使用struct Person per[5];进行定义数组;
② 定义结构体类型的同时定义数组;
③ 直接定义结构体变量、省略类型名的同时定义数组;
2> 初始化:
① 直接定义结构体变量、省略类型名同时定义数组,并赋值;
② 可使用数组下标访问每一个结构元素方式赋值(与普通数组用法一致);
8、 结构体作为函数参数:
1> 结构体变量做函数实参,传递的为全体成员的值,并将实参中成员的值意义赋值给对应的形参成员。(结构体变量做函数参数传递,形参改变不影响实参值);
9、 指向结构体的指针:
1> 每个结构体变量都有自己的存储空间和地址,因此指针也可指向结构体变量;
2> 结构体指针变量的定义格式:struct 结构体名 *指针变量名;
3> 有指向结构体的指针后,有3种访问结构体成员的方式:
① 结构体变量名.成员名;
② (*指针变量名).成员名;
③ 指针变量名->成员名;
(三) 共用体(union)
1、 定义共用体类型:union 共用体名{成员参数列表};
2、 定义共用体变量:union 共用体名 共用体变量名;
1> 先定义共用体类型,再定义共用体变量:union 共用体名 共用体变量名;
2> 定义共用体类型同时定义共用体变量;
3> 省略共用体名,直接定义共用体变量;
3、 公用体与结构体异同:
1> 结构体是所有成员所占内存和;
2> 共用体是最长成员所占内存;
(四) 枚举(enum)
1、 枚举:是C语言中的一种数据类型,不是构造类型,可用于声明一组常数。当一个变量有几个固定的可能取值时,就可将这个变量定义为枚举类型(如:季节:春夏秋冬)。
2、 枚举类型的定义形式:enum 枚举名 {枚举元素1,枚举元素2…枚举元素n};
如:enum Season{spring,summer,autumn,winter};//最后一个值不需符号结尾
3、 枚举变量的定义(3种方式):
1> 先定义枚举类型,再定义枚举变量;
如:enum Season{spring,summer,autumn,winter};//定义枚举类型
enum Season s; //定义枚举变量
2> 定义枚举类型同时定义枚举变量;
如:enum Season{spring,summer,autumn,winter}s; //定义枚举类型同时定义枚举变量
3> 省略枚举名称,直接定义枚举变量;
如:enum {spring,summer,autumn,winter}s; //省略枚举名,直接定义枚举变量
4、 枚举使用的注意:
1> C 语言编译器会将枚举元素(spring/summer等)作为整型常量处理,称为枚举常量;
2> 枚举元素值取决于定义枚举元素排列的先后顺序,默认情况下,第一个枚举元素值为0,一次累加;
3> 可在定义枚举类型时改变枚举元素值:
如:enum {spring=1,summer,autumn=3,winter};
4> 若部分未置顶值的枚举元素,其值为前一元素值+1;
5、 枚举变量的基本操作:
1> 赋值:可给枚举变量赋枚举常量或整型值;
如:s = spring; 或 s = 3;
2> 遍历枚举元素;
如:for(s =spring;s<=winter;s++){打印s值} //s为枚举变量名
(五) 指针
1、 变量的直接引用(通过变量名读写变量):通过变量名引用变量,由系统自动完成变量名和其存储地址的转换;
2、 指针变量:用来存放变量地址的变量;
3、 指针定义:类名标识符 *指针变量名;
1> 用法:int *p; float *q;
2> 说明符*:用来说明此变量是指针变量,不能省略,但它不属于变量名的一部分;
3> 类型标识符:表指针变量所指向的变量的类型,且只能指向这种类型的变量;
4、 指针初始化:
1> 先定义后初始化:
如:int a = 10;//定义int类型变量
int *p; // 定义指针变量p
p = &a; //将变量a的地址赋值给指针变量p,因此指针变量p指向变量a
2> 定义同时初始化:
如:int a = 10;//定义int类型变量
int *p = &a;//定义指针变量,并将变量a的地址赋值给指针变量p
3> 初始化注意:指针变量是用了存放变量地址的,不能随意赋值一个常数;
如:int *p; p = 200; //错误的
5、 指针运算符:
1> 给指针指向的变量赋值:
① 用法:char a = 10;
char *p = &a; //指针变量p指向变量a,*用来说明p是个指针变量
*p = 9; //通过指针变量p间接修改变量a的值;*是指针运算符,*p代表p值地址对应的存储空间
printf(“a的值:%d”,a); // a =9
2> 取出指针所指向变量的值:
① 指针运算符除可赋值外,还可取值;
② 用法:char a = 10; char *p; p = &a;
char value = *p;//*p根据p值(即变量a的地址)访问对应的存储空间,并去除存储内容(即变量a的值),赋给value
printf(“a的值:%d”,a); // a =9
3> 使用注意:
① 指针变量未指向确定地址前,不能对他所指向的内容赋值:
如:int *p; *p = 10; //错误的
② 指针变量指向确定变量后再进行赋值。
6、 指针的用途:
1> (指针变量p简介访问变量)写个函数swap(参数1,参数2),互换2个实参的值;
2> (默认一个函数只能有一个返回值,使用指针可实现函数有”多返回值”)写个函数sumAndMinus,可同时计算2个整型的和差,返回和与差;
7、 关于指针的疑问:
1> 同一编译器环境下,一个指针变量所占用的内存空间是固定的:
2> 占用空间不跟随所指向变量的类型而改变;
3> 每个指针变量所占用的内存空间是固定的,且存储的都为地址,却还给指针变量分类型:变量类型与占用有关,指针就会根据指针类型去认定变量类型;
如:char c = 1;int *p = &c; // 用*p获取变量c值时,指针p认为c为int类型。
8、 指向一维数组元素的指针:
1> 用指针指向一维数组元素:
① 用法:int a[2]; int *p; p = &a[0]; *p = 10;
printf(“a[0]=%d”,a[0]); //a[0]=10
2> 用指针遍历数组元素:
① 指针*p=array[0]=array; 因此 *(p+1)=array[1];
② P+1不一定代表array值+1,加多少与数组类型有关;
③ 若使用*(p++)循环遍历数组,则数组遍历完后,p未指向任何元素,需重新p=&array[0];或 p=array;
④ 数组遍历完后,指针变量p还是指向a[0],因为p值一直未变过;
⑤ 数组名若为常量,则不能进行赋值运算:
如:int value = *(a++); //错误的
3> 指针与数组的总结:(指针p,数组a)
① 若p指向了一个数组元素,则p+1表指向数组该元素的下一个元素;
如:p =&a[0]; p+1 = a[1];
② 对不同类型的数组元素,p值的改变是不同的,若数组元素为int型,p+1表p值+2(16位编译器下);
③ 若p=&a[0],则:
。p+i = a + i =a[i]; //a表数组首地址,a+i也是地址;
。*(p+i) =*(a+i)=a[i];
。虽然p+i=a+i=a[i];但使用还是有区别的,因为p可改变自身值,如p++使p的值自增,而数组名a表数组首地址的常量,它的值不可改变,即a++不合法;
④ 引用一个数组元素可有2种方法:
。下标法:如a[i];
。指针法:如:*(p+i) 或 *(a+i)
4> 数组、指针与函数参数:
① 数组名作为实参,是将实参数组的首地址传递给形参数组,两个数组共同占用同一段内存空间,这样形参数组中的元素值改变会使实参数组元素值改变;
② 这种地址传递可使用指针来实现,函数的实参和形参都可分别使用数组或指针(4种情况);
③ 指针和数组可互相切换使用,但并不能说指针就等于数组;
9、 指针与字符串:
1> 用指针遍历字符串的所有字符:
如:char s[]=”love”;char *p; p = s;
for(;*p != ‘\0’;p++)
{
printf(“%c”,*p);
}
2> 用指针直接指向字符串:
① 可直接使用指针指向一个字符串,省略定义字符数组步骤;
如:char *s=”love”; intlen = strlen(s);//测量字符串长度
② 在string.h中声明的函数:函数中的形参都指向字符变量的指针类型;
。字符串长度函数:size_tstrlen(const char *);
。字符串拷贝函数:char*strcpy(char *,const char *);
。字符串拼接函数:char*strcat(char *, const char *);
。字符串比较函数:intstrcmp(constchar *, const char *);
。注意:他们的参数都指向字符变量的指针类型,因此可传入指针变量或数组名;
③ 指针指向字符串的其它方式:
。定义指针变量,再指向字符串;
如:char *s; s =”love”;
字符数组不允许如此:chars[10]; s =”love”; // 错误的:s为常量,表数组首地址,不能进行赋值运算
若使用char*s = “java”; *s=”love”; // 则是错误的,内存溢出
3> 指针处理字符串的注意:
如:将字符串love修改首字符为L;
① char a[] = “love”;// 定义一个字符串变量
*a = ‘L’; // 修改字符串首字符
printf(“%s”,a); // 输出a的值
② char *p = “love”;// 定义一个字符串常量
*p = ‘L’; //错误的:p指向的一块字符串常量,常量内部的字符不允许修改;
printf(“%s”,a); // 输出a的值
10、 返回指针的函数与被指向函数的指针:
1> 返回指针的函数:
① 返回指针的函数一般形式:类型名 *函数名(参数列表);
2> 指向函数的指针:
① 为什么指针可指向一个函数:
。函数在内存中要占据部分存储空间,所以它有个起始地址,即函数入口地址;
。指针变量是用来存储地址的,所以利用指针指向函数,其中函数名就代表函数的地址;
② 指向函数的指针一般形式:函数返回值类型 (*指针变量名)(形式参数1,形式参数2。。。);
③ 形式参数的变量名可省略,甚至整个形式参数列表都可省略;
④ 使用注意:
。由于这类指针变量存储的是一个函数的入口地址,所以对它做加减运算(如:p++)是无意义的;
。返回指针的函数定义char*upper(char *str)和指向函数的指针的定义int(*p)(inta,int b)非常相似;
。指向函数的指针变量主要有两个用途:调用函数、将函数作为参数在函数间传递。
(六) typedef定义别名的使用:
1、 typedef作用简介:
1> 使用typedef关键字为各种数据类型定义一个新名字(别名);
如:typedef intInteger; typedef unsigned int Uinterger;
调用:Interger i= 10;
2> 可在别名基础上再起别名;
如:typedef intInteger; typedef Integer MyInteger;
2、 typedef与指针:
1> typedef可给基本数据类型起别名,也可给指针起别名;
如:typedef char*String; 调用:String str = “this is a string”;
3、 typedef 与结构体:
1> 给结构体起别名可使代码更加简洁明了;
2> 默认情况下结构体变量的使用:使用struct定义结构体;
3> 使用typedef给结构体起别名:
① 先定义结构体,再起别名;
如:定义一个结构体MyPoint后,使用typedef struct MyPoint Point起别名;使用Point p;调用结构体;
② 定义结构体同时起别名;
如:typedefstruct MyPoint
{
float x;
}Point;
③ 省略结构体名称时,直接定义结构体别名;
如:typedefstruct
{
float x;
}Point;
4、 typedef 与指向结构体的指针:
1> typedef 可给指针、结构体起别名,也可给指向结构体的指针起别名;
如:typedefPoint *pp;
5、 typedef 与枚举类型:
1> 使用typedef给枚举类型起别名可使代码简洁;
① 先定义枚举类型Season,在使用typedef enum Searson Son;给枚举类型起别名,再使用Son s = spring;定义枚举变量;
② 可定义枚举类型同时起别名;
③ 可省略枚举名,直接定义枚举变量别名;
6、 typedef 与指向函数的指针:
① 给指向函数的指针类型起别名:
如:定义一个sum函数,使用typedef int (*MySum)(int , int);给指向函数的指针类型起别名,使用MySum p = sum;定义一个指向sum函数的指针变量,最后使用(*p)(4,5)利用指针变量p调用sum函数;
7、 typedef 与#define:
1> typedef char *String1;
String str1,str2;
2> #define String2 char *
String2 str3,str4;
3> 上面可以得出:
① String1 就是 char*,所以char *str1;char *str2;
② String2就是 char*,所以char *str3,str4;为char *str3;char str4;
4> 因此,给类型起别名,最好使用typedef,而不使用#define;
PS:本章总结了关于C语言重要数据类型,这个是重点哟!
------- iOS培训、PHP培训、android培训、java培训、期待与您交流! ----------