C语言第三篇---数组、指针
在前面学习中,要存储一个数据,可以用一个变量去解决,当存储一组数据的时候,
就要用到整型数组、字符数组、指针数组、结构体数组。数组就是可以存放一组数据,这
样可以减少代码重复,提高性能。
一、数组
数组就是数据的整合,那么整型数组的定义的格式是:int 变量名[元素个数];例
如:int a[10]; 接着就是数据是怎么存放到整型数组中的,数组的存放有一下几种方式:
1.在定义时赋值,其余没有赋初始值的默认为0 int a[10]={1,2,3,4}
2.在定义时可以对单个进行赋值 int a[10]={[3]=5,[8]=12};
3.在定义时不赋值,以后赋值必须单个进行赋值 int a[10]; a[1]=1; a[5]=4;
既然数据可以存放多个数据,它在内存中是这么存放的:
代码(字符数组为例):
int main()
{
char cs[5]={'a','A','D','e','f'};
return 0;
}
内存给数组分配也是从大到小根据占据字节分配,依次的从上到下存放数据,并且cs
的地址就是它的首地址即cs[0],和存储变量的方式很雷同,只不过没有转换成二进制。
函数之间可以传递参数,这个参数可以是变量也可以是数组,要注意的就是传递过去
的是地址,而且默认的把数组当成指针传递过去,指针是8个字节,而数组要根据数据类
型和元素个数来决定,因此传递之前格外注意,需要数组长度时要提前换算出来。
二、二维数组
在存储数据用数组,难免有些不足,于是引出了二维数组,在我的理解下,我可以把
二维数组理解成数组的数组,或者把它看成两个一维数组,这样很容易就理解了。二维数
组可以存放的数据更多,实际上就是两个一维数组(呵呵,我怎么这么犟呢),举个例子:
int a[3][2]={{1,2},{3,4},{8,12}};
这就是个简单的二维数组,那么数组的输出方式可以用for循环来实现:
三、字符串
字符串就是一个一维数组,它就是由很多个字符组成,需要注意的就是字符串的末尾
是以'\0'为结束标志。例如:
char p[10]="minglei"; 或 char p[]={'m','i','n','g','l','e','i','\0'};
如果是以上面第二种方式赋值一定在最后面加上'\0',否则后果严重,字符串只认'\0'为
结束,找不到则去上一个字符串中去找,直到找到为止。因此错误的写法是:
char name[] ={'o','k'};
四、sizeof和strlen
sizeof和strlen的不同:'\0'也占据一个字节空间,因此用sizeof求所占字节数需要算
上'\0',而strlen只是单纯求字符串的长度,因此不算上'\0'。并且在用strlen这个函数时,需要
加头文件string.h
字符数组只能存储一个字符串,这往往时不能满足我们需求的,因此引出字符串数组
,也就是字符二维数组,可以存放多个字符串。
五、结构体
结构体类型,是我们人为的去定义一种类型供我们使用,这种类型可以存放多种数据
类型,比如整型,浮点型,字符,数组,指针等,它的作用非常广,这样定义有点面向对
象的意思,把一个对面的属性封装了起来。
它定义的格式是:struct 类型名称{成员1,成员2....};定义结构体变量有以下两种:
1. struct Student
{
char name[10];
short age;
};
struct Student stu={"minglei",23};
2. struct Student
{
char name[10];
short age;
}stu,stu2;
一种是先定义类型,再定义变量,另一种在定义类型的同时也定义了变量。
它的数据这么复杂,其实在内存中存储也是一样的,只不过它分配空间是从小往大分
配,正好反了过来。(定义一个数据类型并不分配内存)结构体所占的内存并不是所有成
员的总和,而是成员中最大类型的字节数的倍数。例如:
struct Student
{
char name[10];
int age;
};
这段代码中,结构体所占的内存是8个字节,并不是14。当然定义一个结构体相当于一
个对象,那么有多个对象怎么办呢,就是结构体数组。就好比定义的Student类型,有好
多学生似地,很通俗的理解了,那么在访问成员属性时用变量名.属性名,这一点就更像
面向对象编程了。
for可以进行嵌套,结构体也不例外,例如:一个学生有多门学科成绩,这些成绩可以
封装在一个结构体中,多个学生都有这些成绩。就是学生结构体嵌套成绩结构体。
六、枚举
和结构体类似的一种类型是枚举类型,枚举可以限定编程人员对一些变量赋固定的值
,这些值只能是枚举类型中定义的,没有定义的是不能随便赋其它的值。定义枚举类型的
格式是:enum 类型名{固定值1,固定值2....}
七、指针
C语言的核心就是指针,指针可以操作内存,间接性的修改内存里的数据,并且它的运
用很广泛,有数组指针,字符指针,字符串指针,指针的指针,函数的指针,结构体的指针
等,基本上是无所不指。指针的作用那么大,其实它就是个数据类型,它就是用来存放地址
的,通过它存放的地址可以访问到这个地址的存储空间。
1. 数组指针,顾名思义就是定义一个指针,指针里存放的值就是数组的地址。例如:
int a[3]={1,2,3};
int *p=a;
(int *)这是一体的,而变量p就是指针,这里是把a的地址给了指针p,并不是数值。而且
在定义指针时,数据类型要和所指向的数据类型一致。即数组a的int类型,因此指针p的类型
也必须是int类型,否则后果不堪设想。既然指针可以访问数据,那么指针到底是怎么把数组
的数据访问到的,代码如下:
#include <stdio.h>
int main()
{
int a[3]={1,2,3};
int *p=a;
int i; int size=sizeof(a)/sizeof(int);
for(i=0;i<size;i++)
{
printf("%d",*p);
p+=1;
}
}
代码中 *p就是访问p当前地址的存储空间里的值,而p+1 并不是数值+1,而是p的地址是
数组中下一个地址,所谓的+1,取决与数据类型,int类型4个字节,因此p+1是后4个字节的地
址,也就是数组a中的下一个地址,从而达到了遍历数组的功能。
2.字符指针就是单个字符的指针,比较关注的还是字符串指针,也就是字符数组指针,但是在
定义时完全可以用指针去代替,例如:
char *name="minglei"; //字符串常量
一般的定义是:
char name[10]; //字符串变量
char *n=name;
直接用指针的话,切记一点就是不可以再修改,而后面定义的就不同了,以后任何一个字符
都可以单独的拿出来访问修改。字符串指针用的很多,很多情况下都是和字符串去打交道的。
指针的指针这个就比较难理解了,其实想明白了很简单,无非就是将一个指针存放另一个指
针的地址,从而找到指针里存放的地址的存储空间,打个比方说:小明有本书,小明认识小白,
小白认识小李,那么小李就可以通过小白找到小明得到这本书。在定义指针的指针时需要注意一
点,指针要指向的变量,要和该变量数据类型一致,那么指针的数据类型是和*一体的,例如:定
义一个指针pp指向指针p;
int a=5; 变量a的数据类型为int
int *p=a; 指针p的数据类型为int *
int **pp=p; 指针pp的数据类型为int **
只要记住(数据类型 *)是一体的就很容易理解了。
3.指向函数的指针,这个就没有听说过,我们调用函数时都是用函数名();去调用,那么用指针
也可以实现调用,代码如下:
#include <stdio.h>
int test(int a,int b)
{
return a+b;
}
int main()
{
int (*p)()=test;
printf("%d",p(5,7));
return 0;
}
就这样,我们可以间接性的用指针去调用了函数,指针真的是太有趣了,简直无所不能。
4.结构体指针和枚举指针,这两个类似,唯一一个可以让指针升华的地方就是,结构体变量访
问成员属性是通过 . 的方式,而用指针则可以用 -> 很华丽的指向方式去访问,例如:
#include <stdio.h>
int main()
{
struct Student
{
char name[10];
short age;
};
struct Student stu={"minglei",23};
struct Student *p=&stu;
printf("name=%s,age=",p->name,p->age);
}
总结:指针的用途很广,可以说还是无所不能,只有你想不到的,没有指针做不到的。真的是学好指针才能真正明白C语言的真
谛,“上天入地,指哪到哪”,并且用指针去操作内存,可以大大的节省性能,大爱指针。
明天就要学OC了,内心还有点小激动呢,期待OC的到来,期待未来光明的黑马路。