黑马程序员—(iOS开发)C语言构造与指针类型---(五)

时间:2023-02-19 17:04:39

  ------- iOS培训PHP培训android培训java培训、期待与您交流! ----------  

说明:

1、  数组

2、  结构体(struct)

3、  公用体(union)

4、  枚举(enum)

5、  指针

 

(一)   数组

1、  内存中的地址:

1>    以字节为单位的存储空间,每个字节都有一个唯一编号,编号就是地址(每个函数都有一个自己的地址;);

2>    当定义一个变量时,系统会分配一个带有唯一地址的存储单元来存储此变量(第一个字节地址就为该变量的地址);

2、  一维数组:

1>    定义形式:类型数组名[元素个数];

如:int a[5]; //[]中个数为固定值:可以为常量、常量表达式,但不能用变量

2>    一维数组的存储:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

①    数组名表整个数组的地址,也就是数组的起始地址(如:数组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>    二维数组的存储:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

①    数组的地址为首地址: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>    结构体类型一般定义格式:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

具体举例:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

3、  结构体变量的定义:

1>    先定义结构体类型,再定义变量;

如:structPerson per;

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

2>    定义结构体类型的同时定义变量:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

3>    直接定义结构体类型变量,省略类型名:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

4、  结构体注意点:

1>    不允许对结构体本身递归定义;

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

2>    结构体内可包含别的结构体;

3>    定义结构体类型,只是说明了该类型的组成情况,并没有给它分配存储空间,就像系统不为int类型本身分配空间一样。只有当定义属于结构体类型变量时,系统才会分配存储空间给该变量;

4>    结构体变量占用的内存空间是其成员所占内存只和,且各成员在内存中按定义的顺序一次排列;

5、  结构体的初始化:

1>    初始化:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

2>    注意:结构体定义与赋值不能分开进行;

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

6、  结构体的使用:

1>    一般对结构体变量操作是以成员为单位进行的,引用的一般形式为:结构体变量名.成员名;

如:per.age =27; // .称为成员运算符,在所有运算符中优先级最高;

2>    若某成员也是结构体变量,可连续使用成员运算符”.”访问最低一级成员;

如:per.birthday.year= 1986;

3>    相同类型的结构体变量间可进行整体赋值;

如:structPerson per1 = {“jim”,27};

     Struct Person per2= per1;

7、  结构体数组:

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>    同一编译器环境下,一个指针变量所占用的内存空间是固定的:

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

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种情况);

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

③    指针和数组可互相切换使用,但并不能说指针就等于数组;

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>    返回指针的函数:

①    返回指针的函数一般形式:类型名 *函数名(参数列表);

                           黑马程序员—(iOS开发)C语言构造与指针类型---(五)

2>    指向函数的指针:

①    为什么指针可指向一个函数:

。函数在内存中要占据部分存储空间,所以它有个起始地址,即函数入口地址;

。指针变量是用来存储地址的,所以利用指针指向函数,其中函数名就代表函数的地址

②    指向函数的指针一般形式:函数返回值类型 (*指针变量名)(形式参数1,形式参数2。。。);

③    形式参数的变量名可省略,甚至整个形式参数列表都可省略;

黑马程序员—(iOS开发)C语言构造与指针类型---(五)

④    使用注意

。由于这类指针变量存储的是一个函数的入口地址,所以对它做加减运算(如: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培训、期待与您交流! ----------