C语言中的结构体

一结构体

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排序函数不变,只需要增加一个按员工编号比较的函数就可以了。

原文地址:https://www.cnblogs.com/ljcgood66/p/5281279.html