IOS开发之路--C语言构造类型

概述

在第一节中我们就提到C语言的构造类型,分为:数组、结构体、枚举、共用体,当然前面数组的内容已经说了很多了,这一节将会重点说一下其他三种类型。

结构体 枚举 共用体

结构体

数组中存储的是一系列相同的数据类型,那么如果想让一个变量存储不同的数据类型就要使用结构体,结构体定义类似于C++、C#、Java等高级语言中类的定义,但事实上它们又有着很大的区别。结构体是一种类型,并非一个变量,只是这种类型可以由其他C语言基本类型共同组成。

//
// main.c
// ConstructedType
//
// Created by Kenshin Cui on 14-7-18.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

//结构体类型Date
struct Date{
 int year;
 int month;
 int day;
};

struct Person{
 char *name;
 int age;
 struct Date birthday;//一个结构体中使用了另一个结构体类型,结构体类型变量声明前必须加上struct关键字
 float height;
};

int main(int argc, const char * argv[]) {
 struct Person p={"Kenshin",28,{1986,8,8},1.72};
 //定义结构体变量并初始化,不允许先定义再初始化,例如:struct Person p;p={"Kenshin",28,{1986,8,8},1.72};是错误的

 printf("name=%s,age=%d,birthday=%d-%d-%d,height=%.2f\n",p.name,p.age,p.birthday.year,p.birthday.month,p.birthday.day,p.height);
 //结果:name=Kenshin,age=28,birthday=1986-8-8,height=1.72,结构体的引用是通过"结构体变量.成员名称"

 printf("len(Date)=%lu,len(Person)=%lu\n",sizeof(struct Date),sizeof(struct Person));
 //结果:len(Date)=12,len(Person)=32

 return 0;
}

对于上面的例子需要做出如下说明:

可以在定义结构体类型的同时声明结构体变量;
如果定义结构体类型的同时声明结构体变量,此时结构体名称可以省略;
定义结构体类型并不会分配内存,在定义结构体变量的时候才进行内存分配(同基本类型时类似的);
结构体类型的所占用内存大型等于所有成员占用内存大小之和(如果不考虑内存对齐的前提下);

对第4点需要进行说明,例如上面代码是在64位编译器下运行的结果(int长度4,char长度1,float类型4),Date=4+4+4=12。但是对于Person却没有那么简单了,因为按照正常方式计算Person=8+4+12+4=28,但是从上面代码中给出的结果是32,为什么呢?这里不得不引入一个概念“内存对齐”,关于内存对齐的概念在这里不做详细说明,大家需要了解的是:在Mac OS X中对齐参数默认为8(可以通过在代码中添加#pragma pack(8)改变对齐参数),如果结构体中的类型不大于8,那么结构体长度就是其成员类型之和,但是如果成员变量的长度大于这个对齐参数那么得到的结果就不一定是各个成员变量之和了。Person类型的长度之所以是32,其实主要原因是因为Date类型长度12在存储时其偏移量12不是8的倍数,考虑到内存对齐的原因需要添加4个补齐长度,这里使用表格的形式列出了具体原因:

表格具体来源请观看下面的视频(注意由于录制软件的原因前几秒不清晰但是不影响分析):

接下来看一下结构体数组、指向结构体的指针:

//
// main.c
// ConstructedType
//
// Created by Kenshin Cui on 14-7-18.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

struct Date{
 int year;
 int month;
 int day;
};

struct Person{
 char *name;
 int age;
 struct Date birthday;
 float height;
};

void changeValue(struct Person person){
 person.height=1.80;
}

int main(int argc, const char * argv[]) {
 struct Person persons[]={
  {"Kenshin",28,{1986,8,8},1.72},
  {"Kaoru",27,{1987,8,8},1.60},
  {"Rosa",29,{1985,8,8},1.60}
 };
 for (int i=0; i<3; ++i) {
  printf("name=%s,age=%d,birthday=%d-%d-%d,height=%.2f\n",
    persons[i].name,
    persons[i].age,
    persons[i].birthday.year,
    persons[i].birthday.month,
    persons[i].birthday.day,
    persons[i].height);
 }
 /*输出结果:
  name=Kenshin,age=28,birthday=1986-8-8,height=1.72
  name=Kaoru,age=27,birthday=1987-8-8,height=1.60
  name=Rosa,age=29,birthday=1985-8-8,height=1.60
  */

 struct Person person=persons[0];
 changeValue(person);
 printf("name=%s,age=%d,birthday=%d-%d-%d,height=%.2f\n",
   persons[0].name,
   persons[0].age,
   persons[0].birthday.year,
   persons[0].birthday.month,
   persons[0].birthday.day,
   persons[0].height);
 /*输出结果:
  name=Kenshin,age=28,birthday=1986-8-8,height=1.72
  */

 struct Person *p=&person;
 printf("name=%s,age=%d,birthday=%d-%d-%d,height=%.2f\n",
   (*p).name,
   (*p).age,
   (*p).birthday.year,
   (*p).birthday.month,
   (*p).birthday.day,
   (*p).height);
 /*输出结果:
  name=Kenshin,age=28,birthday=1986-8-8,height=1.72
  */
 printf("name=%s,age=%d,birthday=%d-%d-%d,height=%.2f\n",
   p->name,
   p->age,
   p->birthday.year,
   p->birthday.month,
   p->birthday.day,
   p->height);
 /*输出结果:
  name=Kenshin,age=28,birthday=1986-8-8,height=1.72
  */

 return 0;
}

结构体作为函数参数传递的是成员的值(值传递而不是引用传递),对于结构体指针而言可以通过”->”操作符进行访问。

枚举

枚举类型是比较简单的一种数据类型,事实上在C语言中枚举类型是作为整形常量进行处理的,通常称为“枚举常量”。

//
// main.c
// ConstructedType
//
// Created by Kenshin Cui on 14-7-18.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

enum Season{ //默认情况下spring=0,summer=1,autumn=2,winter=3
 spring,
 summer,
 autumn,
 winter
};

int main(int argc, const char * argv[]) {
 enum Season season=summer; //枚举赋值,等价于season=1
 printf("summer=%d\n",season); //结果:summer=1

 for(season=spring;season<=winter;++season){
  printf("element value=%d\n",season);
 }
 /*结果:
  element value=0
  element value=1
  element value=2
  element value=3
  */
 return 0;
}

需要注意的是枚举成员默认值从0开始,如果给其中一个成员赋值,其它后面的成员将依次赋值,例如上面如果summer手动指定为8,则autumn=9,winter=10,而sprint还是0。

共用体

共用体又叫联合,因为它的关键字是union(貌似数据库操作经常使用这个关键字),它的使用不像枚举和结构体那么频繁,但是作为C语言中的一种数据类型我们也有必要弄清它的用法。从前面的分析我们知道结构体的总长度等于所有成员的和(当然此时还可能遇到对齐问题),但是和结构体不同的是共用体所有成员共用一块内存,顺序从低地址开始存放,一次只能使用其中一个成员,union最终大小由共用体中最大的成员决定,对某一成员赋值可能会覆盖另一个成员。

//
// main.c
// ConstructedType
//
// Created by Kenshin Cui on 14-7-20.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

union Type{
 char a;
 short int b;
 int c;
};

int main(int argc, const char * argv[]) {
 union Type t;
 t.a='a';
 t.b=10;
 t.c=65796;

 printf("address(Type)=%x,address(t.a)=%x,address(t.b)=%x,address(t.c)=%x\n",&t,&t.a,&t.b,&t.c);
 //结果:address(Type)=5fbff7b8,address(t.a)=5fbff7b8,address(t.b)=5fbff7b8,address(t.c)=5fbff7b8

 printf("len(Type)=%d\n",sizeof(union Type));
 //结果:len(Type)=4

 printf("t.a=%d,t.b=%d,t.c=%d\n",t.a,t.b,t.c);
 //结果:t.a=4,t.b=260,t.c=65796

 return 0;
}

这里需要重点解释一个问题:为什么t.a、t.b、t.c输出结果分别是4、260、65796,当然t.c等于65796并不奇怪,但是t.a前面赋值为'a'不应该是97吗,而t.b不应该是10吗?其实如果弄清这个问题共用体的概念基本就清楚了。

根据前面提到的,共用体其实每次只能使用其中一个成员,对于上面的代码经过三次赋值最终使用的其实就是t.c,而通过上面的输出结果我们也确实看到c是有效的。共用体有一个特点就是它的成员存储在同一块内存区域,这块区域的大小需要根据它的成员中长度最大的成员长度而定。由于上面的代码是在64位编译器下编译的,具体长度:char=1,short int=2,int=4,所以得出结论,Type的长度为4,又根据上面输出的地址,可以得到下面的存储信息(注意数据的存储方式:高地址存储高位,低地址存储地位):

当读取c的时候,它的二进制是“00000000 00000001 00000001 00000100”,换算成十进制就是65796;而经过三次赋值后,此时b的存储就已经被c成员的低位数据覆盖,b的长度是二,所以从起始地址取两个字节得到的二进制数据此时是“00000001 00000100”(b原来的数据已经被c低2位数据覆盖,其实此时就是c的低2位数据),换算成十进制就是260;类似的a此时的数据就是c的低一位数据”00000100”,换算成十进制就是4。

(0)

相关推荐

  • ios开发中时间转换的方法集锦

    在开发iOS程序时,有时候需要将时间格式调整成自己希望的格式,这个时候我们可以用NSDateFormatter类来处理. 例如: //实例化一个NSDateFormatter对象 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; //设定时间格式,这里可以设置成自己需要的格式 [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; //用[NSDate d

  • iOS应用开发中使用NSLocale类实现对象信息的本地化

    如何正确地格式化时间 这也是我们这两天遇到的问题,跟用户几经沟通之后,终于抓到log,发现问题竟然是格式化导致的.怎么解决呢? 这个时候NSLocale的重要性就体现出来了.NSLocale作为大家都不常用的一个类,NSLocale类是将与国家和语言相关的信息进行简单的组合,包括货币.语言.国家等的信息. 所以很简单,我们把dateFormatter的locale属性改一下即可解决这个问题.将下面代码放在dateFormatter初始化之后: 复制代码 代码如下: NSLocale *usLoc

  • 使用Reachability类判断iOS设备的当前网络连接类型

    (1). 下载 https://developer.apple.com/library/ios/samplecode/Reachability/Reachability.zip (2). 拖reachability.h,reachability.m入工程 (库非ARC) ARC:-fno-objc-arc (3) .导入SystemConfiguration.framework (4).用法 复制代码 代码如下: -(NSString*)getNetType   {          NSStr

  • iOS对象指针和基础数据类型的强转详解

    本文主要介绍了iOS中对象指针和基础数据类型如何进行强转,下面话不多说,直接来看示例详解. 一.对象指针的强转: UIView *view = [UIView new];//new一个UIView类的对象 UILabel *label = (UILabel *)view;//强转成UILabel指针 label.text = @"123";//给label的text属性赋值(调用label的setText方法) 上述代码会产生崩溃,崩溃信息如下: -[UIView setText:]:

  • cisco6509 CatOS 转换为Native IOS实际过程

    引擎:WS-SUP720-3B 主备各一块 线路卡:WS-X6148X2-45AF 五块. Cisco CompactFlash Memory Card, 128 MB 2块. 该设备原为cisco ios模式,但因当时IOS不支持WS-X6148X2-45AF 线路卡所以采用CatOS模式以支持上述线路卡,后思科推出支持的IOS版本s72033-advipservicesk9_wan-mz.122-18.SXF.bin,客户要求再次改为IOS版本.此两块 CompactFlash Memory

  • 详解iOS应用UI开发中的九宫格坐标计算与字典转换模型

    九宫格坐标计算 一.要求 完成下面的布局 二.分析 寻找左边的规律,每一个uiview的x坐标和y坐标. 三.实现思路 (1)明确每一块用得是什么view (2)明确每个view之间的父子关系,每个视图都只有一个父视图,拥有很多的子视图. (3)可以先尝试逐个的添加格子,最后考虑使用for循环,完成所有uiview的创建 (4)加载app数据,根据数据长度创建对应个数的格子 (5)添加格子内部的子控件 (6)给内部的子控件装配数据 四.代码示例 复制代码 代码如下: // //  YYViewC

  • 解析iOS开发中的FirstResponder第一响应对象

    1. UIResonder 对于C#里所有的控件(例如TextBox),都继承于Control类.而Control类的继承关系如 下: 复制代码 代码如下: System.Object System.MarshalByRefObject System.ComponentModel.Component System.Windows.Forms.Control 对于iOS里的UI类,也有类似的继承关系. 例如对于UITextField,继承于UIControl:UIControl继承于UIView,

  • iOS获取网络类型的方法汇总

    Reachability类只能区分WIFI和WWAN类型,却无法区分2G网和3G网. 网上也有些方法,却都存在Bug. 经过网上查找资料和测试,基本上总结了以下几种方法: 1.使用导航栏的方式:(私有API) 代码: 复制代码 代码如下: typedef enum {     NetWorkType_None = 0,     NetWorkType_WIFI,     NetWorkType_2G,     NetWorkType_3G, } NetWorkType; UIApplicatio

  • iOS中定位当前位置坐标及转换为火星坐标的方法

    定位和位置信息获取 定位和反查位置信息要加载两个动态库 CoreLocation.framework 和 MapKit.framework 一个获取坐标一个提供反查 复制代码 代码如下: // appDelgate.h #import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> #import <MapKit/MapKit.h>   @interface AppDelegate : UIResponder

  • iOS开发之获取系统相册中的图片与视频教程(内带url转换)

    好些天没写点东西了,最近公司要做新项目,有点小忙.不想我的坚持就此中断,我把我前些天研究的东西拿出来给大家看看. 这次整理的是AssetsLibrary和PhotoKit的使用.本人处女座,有点强迫症,之前写的项目里用的是AssetsLibrary写的调取相册内的媒体文件,但是Xcode总是报警告错误,虽然能够编译并展示效果,但是十几个警告错误挂在那,心里总不是滋味,所以我就研究了一下AssetLibrary和PhotoKit. 在 iOS 8 出现之前,开发者只能使用 AssetsLibrar

  • iOS 对象属性详细介绍

    iOS 对象属性 oc对象的一些属性: retain,strong, copy,weak,assign,readonly, readwrite, unsafe_unretained 下面来分别讲讲各自的作用和区别: retain,计数器加1, (增加一个指向内存的指针) 对应release(计数器-1) setter 方法对参数进行 release 旧值再 retain 新值,所有实现都是这个顺序 - (void)setBackView:(UIView *)backView { if (_bac

随机推荐