一结构体
1. 结构体
如果需要保存学生的年龄、成绩、姓名等多个字段时需要用到结构体,即将多个相关的数据封装在一起。
struct Stu{
char name[20];
int age;
float score;
};
定义了一个新的数据类型Stu,可以通过Stu定义变量。
struct Stu s1, s2;
s1.age = 18;
s1.score = 90;
s2.age = 20;
s2.score =99;
Stu是数据类型,不是变量, s1和s2是变量,s1有两个成员age和score
2. 结构体变量的赋值与初始化
struct XYPoint {
int x;
int y;
};
struct XYPoint point = {3,4}; //初始化
struct XYPoint point2;
point2 = point; //赋值
//成员分别赋值
point2.x = 10;
point2.y = 20;
练习:定义一个表示学生的结构体
struct Person{
char name[20];
int age;
int height;
};
struct Person p;
p = (struct Person) {"zhangsan", 18, 172}; //强制转换后赋值是正确的
也可以这样初始化,指定属性的名称:
struct Person p = {.name = "zhangsan", .age = 15, .height = 173};
3. 在iOS中的应用
struct Rect{
XYPoint origin;
XYSize size;
};
4. 结构体取别名
typedef struct Stu Student;
Student s1; //用别名定义变量
typedef unsigned long size_t;
size_t xx;
也可以在声明的时候定义别名:
typedef struct {
int width;
int height;
} Size;
Size就是这个结构体的别名
5.返回结构体的函数
如根据给定的数值创建一个函数返回该结构体
Rect rectMake( int x, int y, int width, int height){
Rect rect = {{x,y } , {width, height} };
return rect;
}
Rect rect1 = rectMake(10,20,100,30);
6.结构体大小
结构体对齐
7.结构体指针
Rect rect1 = rectMake(12,23,34,45);
Rect *p = &rect1;
(*p).origin.x = 100;
p-> origin.x = 100; //箭头实际上就是先取*再取.操作
二、枚举
1. 枚举的定义
季度、 星期几、 颜色、 型号等数据在存储时,可以使用枚举类型
enum Color {
Red,
Green,
Blue,
Yellow,
Brown
};
定义了一个数据类型Color,可以通过Color来定义变量,其变量的取值只能是定义Color类型时指定的取值,如:Red,Green,Blue,Yellow,Brown。如:
enum Color myColor = Red;
2.枚举变量的输出
printf("%d ", myColor);
枚举可以按%d的形式输出,输出结果是一个整形数字,定义枚举类型时,默认就是从0开始的一组整形数字
在给枚举变量赋值时,也可以这样实现:
enum Color myColor2 = 2;
跟
enum Color myColor2 = Blue;
这两条效果是一样的,枚举本质上就是一个整型,使用枚举就是为了方便阅读或记忆。
3.注意
在定义枚举时,其变量的取值一般是以类型名开头,如:
enum Week {
WeekSunday,
WeekMonday,
WeekTuesday,
WeekWednsday,
WeekThursday,
WeekFriday,
WeekSaturday
};
这样在通过枚举类型定义变量时,
enum Week day1= Week...
给变量赋值时,就以类型名开头,系统会自动提示该类型的可能取值。不至于与其他的枚举类型取值弄混。
在以后学iOS开发时,会使用到系统提供的大量的枚举类型
三、动态内存分配与宏
1. 静态内存分配
程序在编译时就已经确定所要分配内存空间的大小,如常用的变量、常量等。
2. 动态内存分配
在运行时,程序员可以在堆空间中动态分配存储空间。
可以通过 malloc(100); 函数在堆中开辟100个字节大小的空间,返回该空间的首地址。需要通过指针访问该空间。
char *p = (char *) malloc(100);
因为malloc()函数的返回值是void *, 在有的编译器中需要进行强制类型转换,如上一行代码。
动态分配存储空间不一定成功,所以需要进行验证,如:
if ( !p ) {
perror ("malloc error");
}
perror打印标准报错,在命令行中,标准输出和标准报错都是终端。有时需要将标准报错设置为某个文件。
需要注意的是,堆空间本身没有数据类型,用p指向这个空间,将这个空间看作是 char *[] 数组。
可以通过指针变量p赋值如下:
p[0] = 'A';
p[30] = 'Z';
再如:
int * q = malloc(100);
if (!q) {
perror("malloc");
}
如果用int *指针指向这100个字节的堆空间,则该空间可以存储25个int类型的数据, q[0]是前4个字节, q[1]是第5到第8个字节。可以这样赋值
q[0] = 110;
q[1] = 250;
3.释放存储空间
堆空间使用完成后,需要手动释放,
free(p);
free(q);
开辟空间实际上就是在系统中注册这一段空间,我用了;释放空间就是告诉系统,我不用了。
4. 根据字符串大小动态开辟存储空间保存字符串
int i = 0;
while ( i++ < 100 ){
int n = 0;
scanf("%d", &n);
char *p = malloc( n+1 );
scanf("%s", p);
printf("%p的位置,存储的是:%s", p, s);
free(p);
}
每次循环都会动态在堆中开辟一块存储空间,空间的大小由用户输入,然后再输入字符串保存到该空间中,输出该字符串。用完后,释放该空间
5. 静态分配与动态分配的区别
存储位置不同:栈和堆;
静态分配在编译时决定,动态分配是在程序运行时决定大小;
静态分配空间的大小由编译器决定,动态分配由程序员决定
静态分配的空间自动释放,动态分配的空间需要手动释放。因此需要注意的是:如果没有手工释放动态分配的空间,堆空间的存储空间不会随着函数的调用结束而释放,在程序的运行期间会一直占用这块空间,直到程序结束。
四、宏
1. 宏就是在预处理阶段的简单的字符串替换,预处理是在编译前进行。
C语言程序编译由四步:
预处理:根据预处理指令修改源文件,宏是字符串替换,#include是把头文件复制到源文件中
gcc -o test.i test.c -E
编译,将代码翻译成中间语言或汇编代码
gcc -o test.s test.i -S
汇编,将汇编代码翻译成目标机器指令
gcc -o test.o test.s -c
链接,将有关的目标文件、库文件链接在一起。
gcc -o test test.o
执行就是
./test
2. 定义宏
#define PI 3.14159
int main(){
printf( "%f ", PI *10*10);
}
在编译前,将程序中的PI字符串用3.14159代替
一般情况下,将可能会被修改的常量定义为宏。如定义数组时,如果数组的长度可能会发生变化,可以将数组的长度用宏来代替。
在iOS开发中经常使用宏来保存url地址,在项目开发时,经常使用测试系统,上传APP Store时需要使用正式URL
如让NSLog仅在调试阶段使用,在发布时不需要,可以这样定义
#ifdef DEBUG
#define CuiLog(…) NSLog(__VA_ARGS__)
#else
#define CuiLog(…)
#endif
或者:
#ifdef DEBUG
#define CuiLog(format,...) printf(format,##__VA_ARGS__)
#else
#define CuiLog(format, ...)
#endif
3.带参数的宏
#define PI 3.14159
#define A(r) PI*r*r
#define Area(r) (PI*(r)*(r))
//带参数的宏建议变量和结果都加上小括弧
int main(){
printf("%f ", A(10));
printf("%f ", A(5+5) );
printf("%f ", Area(10) );
printf("%f ", Area(5+5));
return 0;
}
练习:把求两个数的和,两个数的积定义为宏
写一个宏求三个数的最大值
作业
1. 创建一个公司的员工的结构体,存储员工的姓名、工龄和年龄。在main函数中创建由5名员工组成的数组。要求编写函数实现:
1)从终端读入5名员工信息
2)按年龄排序
3)按工龄排序
4)按照当前顺序,打印每名员工的信息。
*)改进,尝试把按年龄排序,按工龄排序用一个函数来实现(排序函数需要一个指向函数的参数),函数原型:
void sort( Employee *employees, int count, int (*p) (Employee, Employee))
定义一个比较年龄的函数,如
compareByAge( Employee e1, Employee e2){
if ( e1. age < e2.age ){
return 1;
}else {
return 0
}
}
或直接写成
compareByYears( Eimployee e1, Employee e2 ){
return e1.years < e2.years;
}
如果还有员工编号的话,还可以继续按员工编号排序,继续进行扩展,但是sort排序函数不变,只需要增加一个按员工编号比较的函数就可以了。