iOS视频播放AVPlayer的视频内容拉伸设置

在iOS开发中遇到使用视频播放器的案例,使用的是别人封装好的 WMPlayer 这么一个视频播放器,他使用的是AVPlayer这个底层的视频框架来搭建的视频界面,在开发时会遇到适配适配内容比例的需求,一开始没注意到视频被拉伸过,后来遇到一个用竖屏录制的视频时才发现视频被拉伸了,于是来查找相关的设置属性。

*后查找到使用其中一个叫 videoGravity 的属性,默认设置了AVLayerVideoGravityResize,查看该属性以及相关的其他属性值发现有3种值可以设置,

AVLayerVideoGravityResizeAspect

AVLayerVideoGravityResizeAspectFill

AVLayerVideoGravityResize

逐一试用后发现了其中的一些不同,在这里分享一下,可能我说的不太标准,只是个人的一些理解。

开始把描述放到百度翻译上去翻译了一下,

1.Preserve aspect ratio; fit within layer bounds
2.Preserve aspect ratio; fill layer bounds
3.Stretch to fill layer bounds

解释是

1.保持纵横比;适合层范围内
2.保持纵横比;填充层边界
3.拉伸填充层边界

再看我分别设置了 WMPlayer这个播放器中的3种不同属性值得结果图,

1. wmPlayer.playerLayer.videoGravity =AVLayerVideoGravityResizeAspect

2. wmPlayer.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill

3. wmPlayer.playerLayer.videoGravity = AVLayerVideoGravityResize

可以看出

第1种模式AVLayerVideoGravityResizeAspect是按原视频比例显示,是竖屏的就显示出竖屏的,两边留黑;

第2种AVLayerVideoGravityResizeAspectFill是以原比例拉伸视频,直到两边屏幕都占满,但视频内容有部分就被切割了;

第3种AVLayerVideoGravityResize是拉伸视频内容达到边框占满,但不按原比例拉伸,这里明显可以看出宽度被拉伸了。

有点像UIImageView的ContentMode设置,这里是个人对这个属性值得一些理解,有不足还请包涵。

iOS使用一些特殊键盘时的运行错误解决Can’t find keyplane that supports type 4 for keyboard

Can’t find keyplane that supports type 4 for keyboard iPhone-PortraitChoco-NumberPad; using 1336863583_PortraitChoco_iPhone-Simple-Pad_Default

大概意思是找不到支持键盘iPhone画像keyplane phonep 4型,就是电脑的键盘跟iPhone的输入类型是不一样的。

意思是Xcode 6有小bug,只要设置一下就没问题了,设置的如下图:

%title插图%num
点击模拟器上面的菜单:Hardware->Keyboard->ConnectHardware Keyboard这一项不要勾选,我们的键盘就能正常显示了,但是我们发现这样电脑的键盘就不能用了,所以要不要勾选还看自己的习惯了,没有自定义键盘什么的不弹出也不会影响真机测试。

iOS设置键盘上Return按键及点击操作

想要修该iOS键盘上Return按钮的样式变为搜索,并且监听它被点击了,做出响应,

先设置Return按键的样式,

textField.returnKeyType = UIReturnKeySearch;//变为搜索按钮

textField.delegate = self;//设置代理

在代理方法中实现你想要的点击操作就可以了
– (BOOL)textFieldShouldReturn:(UITextField *)textField

{

NSLog(@”点击了搜索”);

return YES;

}

这里有一些return按键的其他样式

typedef NS_ENUM(NSInteger, UIReturnKeyType) {

UIReturnKeyDefault,

UIReturnKeyGo,//去往

UIReturnKeyGoogle,

UIReturnKeyJoin,//加入

UIReturnKeyNext,//下一步

UIReturnKeyRoute,

UIReturnKeySearch,//搜索

UIReturnKeySend,//发送

UIReturnKeyYahoo,

UIReturnKeyDone,//完成

UIReturnKeyEmergencyCall,

UIReturnKeyContinue NS_ENUM_AVAILABLE_IOS(9_0),

};

当然,要显示中文,还得设置info.plist中

Localization native development region 为 cn

 

对iOS开发有用的一些自动化处理脚本

*近编写了两个辅助iOS开发的脚本,用于一些自动化处理。

简介

objective-c 方法名格式化器
由于工作有些变动的原因,之前一段时间开发iOS时是在21寸的iMac屏幕上开发,现在自己买了macbook pro之后就在自己的笔记本上开发(其实之前更早的时候我是在19寸的黑苹果显示器下开发)。这样的屏幕尺寸变化,导致一些基于原来屏幕尺寸的代码缩进、换行在另一个屏幕上看起来显得异常混乱。

了解obj-c语法的人都知道,它的方法定义语法导致了它有时看起来比其他语言的方法签名要长得多。这样在那种13寸的笔记本上,看起来总是很别扭(虽然它支持了方法定义可以换行,但在xcode创建一个文件时,它对于包含多个参数的那些默认方法名称也是不换行的)。使用xcode默认的代码格式化器貌似也没有对方法的参数换行(就算有,对于已经存在的项目这种历史遗留问题,一个文件一个文件去手动执行格式化命令,也怪累人的)

于是,我写了个脚本,专门用来做这件事。

它根据配置的项目路径:

#TODO: change the path to your xcode project path
GLOBAL_PROJECT_PATH = ‘/Users/yanghua/Desktop/weiboDemo’

去匹配特定后缀的文件类型:
#want to match
GLOBAL_INCLUDE = [‘*.h’, ‘*.m’]

当然,为了小心起见你或许不想动那些你从外部引入的库文件,这里也对此进行了支持(你只需把需要排除的文件夹相对路径或文件的相对路径填写进去即可):
#want to exclude
GLOBAL_EXCLUDE = []

然后,它就会遍历所有匹配到的文件,打开它们,再遍历所有的行,使用正则来匹配方法签名
注:这里的正则表达式,是自己根据obj-c语法写的。用此脚本格式化了两个项目后,再进行编译没有发现语法错误。本人正则小白,如果有更好的写法,请不吝赐教!

#match method pattern
#\s :space
#\s* :0 or n space
#([\d\w]*) :0 or n number or char such as (void) / ( CGFloat)
#(([\d\w]*)\s*\**\s*): such as: (NSString* ) / ( UITableViewController *)
#\( :match left ‘(‘
#\) :match right ‘)’
GLOBAL_METHOD_PATTERN = ‘^\s*(-|\+)\s*\(\s*(([\d\w]*)\s*\**\s*)\s*\)\s*’

#match notation and postil
GLOBAL_UNUSED_PATTERN = ‘^\s*(//|//*)\s*’

几点说明:
(1)匹配所有方法名

(2)如果方法没有或只有一个参数则不进行处理

(3)如果方法多于一个参数,从第二个参数开始,任何参数的标签,如果比方法名*段字符个数多,也不进行处理(因为这样这行,无法基于冒号对齐)

(4)对于被注释的方法名,不进行任何处理

(5)对于方法调用暂时没有进行处理(太复杂了)

(6)其他的方法签名都会被匹配折行处理

效果举例:

-(CGFloat) getHeightWithText:(NSString*)text fontSize:(CGFloat)fontSize constraint:(CGSize)cSize minHeight:(CGFloat)mHeight;

to:
-(CGFloat) getHeightWithText:(NSString*)text
fontSize:(CGFloat)fontSize
constraint:(CGSize)cSize
minHeight:(CGFloat)mHeight;

svn missing-file 警告处理器

*近在写一个项目,虽然iOS端只有本人一个。但还是尝试Xcode自带的“Source Control”功能,并采用了SVN来作为版本控制。用的还行(因为也只是一个人在Check in/out),但还是遇到了些问题。
Bug产生过程:
1. Project处于svn版本控制之下
2. (可有可无)向Project中添加文件,并加入版本控制(A)
3. 从项目中Delete某个文件,并选择Move to trash
4. 编译的时候会有Wraning提示缺少了之前删除的那个文件(Missing File xxxx)
另外,一个文件以处于版本控制下,修改文件名,也会产生Missing File Warning
具体情况以及处理方式,可参考 这篇文章!
对于这个问题,参考它的解决方案,我同样写了个脚本来处理它,这个没什么好说的,就是采用了svn的命令行命令。
在 正常SVN操作策略下:如果产生了上面的问题,在Terminal下,进入项目路径,运行“svn status” 会看到“Missing”的那些file 前面会有个“?”。(注意,不只是这种情况才会产生?,所以我说在正常的SVN操作下,因为特殊情况无法控制)。脚本匹配到带有?号开头的行,但其中不包含项目文件——.xcodeproj。找到它然后进行删除!

运行

命令行运行
./Users/yanghua/Desktop/Python/objcFileHandler/objcMethodFormatter.py

Xcode 内置 per/post-action 运行【推荐】
*步:

%title插图%num
第二步:

%title插图%num
你可以对左边这些Scheme做任意的配置,他们实现了类似“AOP”思想的拦截。使得你可以在某个动作的前后增加前置(Pre-actions)和后置(post-actions)加以干涉。点击“+”选择创建运行脚本。

第三步:

%title插图%num
选择Target, 然后键入需要运行的脚本路径。这样每次你执行特定的Scheme(比如这里的Build),它就会执行一次,并且就算脚本有问题,也不会影响主要动作(Build)。
个人认为,Xcode提供这种功能真心非常不错。

写在*后
这两个脚本的源码,都提交到本人的 GitHub上去了。有需要的可以自行取用。
另外如果你有不错的idea,欢迎提供,欢迎拍砖!
https://github.com/yanghua/objc-xcode-Hack-Handlers

iOS中的layoutIfNeeded立即执行约束重置

layoutIfNeeded

如果,有需要刷新的标记,立即调用layoutSubviews进行布局

这个动画中有用到 举个栗子�� 。

如图 , 上面有个label ,中间有个按钮 , label已经被自动布局到左上角 。 然后我们那个left的constraint

  @IBOutlet weak var leftContrain:NSLayoutConstraint!

在viewDidLoad中声明好,然后在Main.storyboard中进行连线。点击按钮的时候 ,我们把左边的距离改成100 。

在按钮的点击事件里加上这句。

leftContrain.constant = 100

然后我们想要一个动画的效果。
如果这么做

  1. UIView.animateWithDuration(0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {
  2. self.leftContrain.constant = 100
  3. }, completion: nil)

你会发现然并卵 。其实这句话self.leftContrain.constant = 100只是执行了setNeedsLayout 标记了需要重新布局,但是没有立即执行。所以我们需要在动画中调用这个方法layoutIfNeeded
所以代码应该这么写

  1. leftContrain.constant = 100
  2. UIView.animateWithDuration(0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {
  3. self.view.layoutIfNeeded() //立即实现布局
  4. }, completion: nil)

所以上面不管写多少约束的改变,只需要在动画里动用 一次self.view.layoutIfNeeded() ,所有的都会已动画的方式 。如果一些变化不想动画 。在动画前执行self.view.layoutIfNeeded()

新版iTunes 不能安装 ipa 包的五种解决办法

前言:
1.新版iTunes指的是:V12.7
2.新版iTunes界面无应用按钮,即无法安装ipa包

%title插图%num

五种方法
1.使用iTools:

%title插图%num
2.使用低版本的iTunes
(1)降低iTunes版本的方法:http://bbs.feng.com/read-htm-tid-6921748.html
(2)V12.5版本安装包: https://secure-appldnld.apple.com/itunes12/031-77682-20160908-A3B6AB42-7541-11E6-901F-53F133D2D062/iTunes12.5.1.dmg

3.使用testflight:很少使用

4.蒲公英(通过扫描二维码安装)

%title插图%num

5.fir.im(类似于蒲公英)

苹果App,不用上传苹果商店,也能让其他人安装

开发App,苹果App少不了。不过,苹果商店的审核实在太烦,更新也费劲。

 

一、IOS App不通过苹果商店,有几种办法可以完成安装?

不通过苹果商店安装苹果app(即ipa)的方法较为复杂;如果是非越狱的手机,首先需要注意以下条件:

  1. 如果是299美元的企业证书打包的ipa,那没有任何限制。注意:企业证书是不能发布到苹果市场的,只有99美元那个可以。
  2. 如果是99美元的个人/公司证书打包的ipa,那必须在生成证书的时候,把需要安装ipa的手机的uuid增加到证书里面(*多可以增加100个)。

假设Studio中Native下有一个MyApp的本地应用,在dist目录下已经生成MyApp.ipa。 不通过苹果商店安装ipa有3种方法:

  1. 同android的*种方法类似,可以通过ios版本的UI浏览器扫描二维码,但要注意,默认下载后是无法安装ipa,可以安装一个叫“同步推”的工具,这样UI下载ipa后使用“同步推”打开ipa就可以安装
  2. 在PC上安装iTunes,并用数据线连接手机,ipa用iTunes打开,在iTunes中点击自己的手机后在“应用程序”页面会列出ipa,把ipa拖动到右边的页面,并点右下角的“同步”即可安装
  3. 部署https,通过一个描述文件plist 指定安装文件,在手机的safari浏览器中通过itms-services:///?action=download-manifest&url=http://域名/xxxx.plist”的方式安装。本文随后详细说明:苹果App如何通过部署HTTPS实现不经过苹果商店的在线下载安装?。

注意:上面的方法越狱和非越狱都适用,但快捷打包生成的ipa只能安装在越狱手机上

110940_Qvo8_2296277.png

二、苹果App如何通过部署HTTPS实现不经过苹果商店的在线下载安装?

部署https提供ipa的下载,步骤如下:

1. 搭建一个HTTPS服务,可以使用HTTPD或者Tomcat构建,具体可搜索相关帖子。由于https好用的证书(就是浏览器默认支持)需要付费,这里巧用github.com可以实现部署

2. 编写plist文件。plist文件中需注意提供正确的ipa文件路径,以及版本号和Bundle ID。模版: 点我下载

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN”

“http://www.apple.com/DTDs/PropertyList-1.0.dtd”>

<plist version=”1.0″>

<dict>

<key>items</key>

<array>

<dict>

<key>assets</key>

<array>

<dict>

<key>kind</key>

<string>software-package</string>

<key>url</key>

<!– http或者https链接的ipa文件地址 –>

<string>http://x5.justep.com/apps/x5.ipa</string>

</dict>

<dict>

<key>kind</key>

<string>full-size-image</string>

<key>needs-shine</key>

<true/>

<key>url</key>

<!– http或者https链接的图片地址,可直接使用app对应的icon(分辨率没强制要求) –>

<string>http://x5.justep.com/apps/x5.png</string>

</dict>

<dict>

<key>kind</key>

<string>display-image</string>

<key>needs-shine</key>

<true/>

<key>url</key>

<!– http或者https链接的小图片地址,可直接使用app对应的icon(分辨率没强制要求) –>

<string>http://x5.justep.com/apps/x5.png</string>

</dict>

</array>

<key>metadata</key>

<dict>

<key>bundle-identifier</key>

<!– BundleID,就是新建app时的包名 –>

<string>com.justep.x5.demo</string>

<key>bundle-version</key>

<!– 版本号 –>

<string>5.3.2</string>

<key>kind</key>

<string>software</string>

<key>title</key>

<!– 安装app时的提示信息 –>

<string>X5 App download</string>

</dict>

</dict>

</array>

</dict>

</plist>

3. 将plist文件放在https服务目录下。这样就有一个有效的连接地址指向这个文件,例如:

1  https://github.com/myuser/myapp/blob/master/myapp.plist

手机如果要安装app,可以在safari浏览器输入:

1  itms-services:///?action=download-manifest&url=https://xxx/…/myapp.plist

注意:是三个/,不是笔误,url后面写的就是一个有效的可访问plist的https地址,一定要https,否则无法打开

当然,你可以做一个页面,里面用一个链接指向这个地址,方便下载,例如:

1 <a οnclick=”window.location.href=’itms-services:///?action=download-manifest&url=https://xxx/…/myapp.plist'”>下载IPA</a>

苹果App Store上传应用流程详解

AppStore上传及更新文档
必要条件
上传AppStore所需的账号密码

上传准备
1.bundle identifier
对应上传AppStore证书所使用的bundleID填写
2.版本号version
如3.1.0,3在版本大规模改动时进行调整,1是在版本有新特色及较大改动时跳转,0是在bug修复或版本优化时调整
3.build
当前版本号不变的情况下,当AppStore审核失败重新上传新的ipa文件进行审核时,设置buildId,否则无法上传审核
这里写图片描述

%title插图%num

注意:当应用不支持pad或者说,仅支持一种设备时,请及时在devices一栏设置好对应设备,否则无法提交审核,如下图:
这里写图片描述

%title插图%num

打包IPA
设置后对应配置文件后打包IPA,xcode选择device的情况下,选择Product—>Archive进行打包:
这里写图片描述

%title插图%num
打包完成后的界面如下:

%title插图%num
这里写图片描述
*顶部的蓝色条为*新的ipa包,version:3.0.0(1),其中version是3.0.0,1为build版本。

下一步就是上传提交。

提交APP
有两种提交方式:一是直接点击upload to AppStore,一是点击Export导出IPA,使用Application loader进行提交
首先,直接点击upload to AppStore
1.点击之后弹出如下的界面,选择上传对应的development team ,
%title插图%num
选择之后,弹出如下:
%title插图%num
此处要注意profile是否为上传所需的,如果不是表明,证书配置有误。
接下来就是点击upload进行提交了,如果没有问题的话,就会直接提交上传了,之后就是在iTunes connect中进行添加操作了

iTunes Connect操作
首先,登录对应的开发者账号,登录成功后显示如下界面:
%title插图%num
点击《我的APP》对当前已有APP进行管理。
%title插图%num
左上角的“+”号用来添加新应用的,对于当前版本管理或新版本的管理,点击对应应用进入进行管理配置。
%title插图%num
1.名称,AppStore上显示出来的名称,内容保证在16个字符以内,但若内容与APP本身无关可能会导致审核不通过。
2.套装ID,为xcode中设置的bundle identifier,必须保持一致。
3.上图中红色箭头所指内容均可在未提交AppStore审核前设置。

其次,版本管理
%title插图%num
当前版本管理直接点击3.0.0可供销售一栏即可,若需要创建新版本,则点击【版本或平台】—>【iOS】,在弹出的对话框中输入新版本的版本号,也是要在AppStore中可见的版本号,点击创建即可。
注意,版本号要与xcode中打包时的版本号version保持一致。

创建出的新版本会将当前版本的信息全部复制使用,有需要改动的地方,需自行改动添加。
新版本会有【此版本的新功能】一说,这里的内容也会在AppStore更新界面显示出来,方便用户了解版本新功能,如下图所示:

%title插图%num

添加IPA

在【构建版本】一栏添加之前通过xcode或application loader提交的*新IPA文件。
%title插图%num
页面*底部,有选择版本上线发布方式,分自动和手动发布
选择自动发布后,APP审核通过之后会自动发布到AppStore内,不会通知开发人员;
选择手动发布,则需要开发人员在审核通过后进入iTunesConnect手动进行发布。

所有配置都完成以后,点击提交审核,此时弹出的界面有三个选项栏,均需要选择“是”和“否”,一般情况下,直接选择“否”即可,但仍需注意新版本中是否出现与之有关的内容。
*后提交。

目前App审核时间,*快24小时即可通过,慢的话4-5天时间。
若审核不通过,Apple developer会发送邮件至开发者账号对应的邮箱以及开发者账号管理者的邮箱,注意及时查询,根据所反馈的问题及时调整重新上传审核。

AppStore上传审核步骤,以上。

python小技巧:[GUI项目]:Tkinter 创建秒表

python小技巧:[GUI项目]:Tkinter 创建秒表
在python中使用Tkinter创建秒表,Tkinter是Python的标准GUI库。
Python与Tkinter相结合,提供了一种创建GUI应用程序的快速、简便的方法。
Tkinter为TKGUI工具包提供了一个强大的面向对象的界面。很容易开始使用Tkinter。
# Python program to create a
# a new window using Tkinter
# importing the required libraires
import tkinter
# creating a object ‘top’ as instance of class Tk
top = tkinter.Tk()
# This will start the blank window
top.mainloop()
产出:
使用Tkinter创建秒表程序:
秒表是一种手持式计时器,用来测量从被激活的某一特定时间到该手表被停用的时间所经过的时间。一种用于远处观看的大型数字秒表,如在运动场中,称为停车钟。
在手动计时中,时钟由一个按下按钮的人启动并停止。在全自动时间内,启动和停止都是由传感器自动触发的。
所需模块:Tkinter
源代码:
# Python program to illustrate a stop watch
# using Tkinter
#importing the required libraries
import tkinter as Tkinter
from datetime import datetime
counter = 66600
running = False
def counter_label(label):
    def count():
        if running:
            global counter
            # To manage the intial delay.
            if counter==66600:
                display=”Starting…”
            else:
                tt = datetime.fromtimestamp(counter)
                string = tt.strftime(“%H:%M:%S”)
                display=string
            label[‘text’]=display   # Or label.config(text=display)
            # label.after(arg1, arg2) delays by
            # first argument given in milliseconds
            # and then calls the function given as second argument.
            # Generally like here we need to call the
            # function in which it is present repeatedly.
            # Delays by 1000ms=1 seconds and call count again.
            label.after(1000, count)
            counter += 1
    # Triggering the start of the counter.
    count()
# start function of the stopwatch
def Start(label):
    global running
    running=True
    counter_label(label)
    start[‘state’]=’disabled’
    stop[‘state’]=’normal’
    reset[‘state’]=’normal’
# Stop function of the stopwatch
def Stop():
    global running
    start[‘state’]=’normal’
    stop[‘state’]=’disabled’
    reset[‘state’]=’normal’
    running = False
# Reset function of the stopwatch
def Reset(label):
    global counter
    counter=66600
    # If rest is pressed after pressing stop.
    if running==False:
        reset[‘state’]=’disabled’
        label[‘text’]=’Welcome!’
    # If reset is pressed while the stopwatch is running.
    else:
        label[‘text’]=’Starting…’
root = Tkinter.Tk()
root.title(“Stopwatch”)
# Fixing the window size.
root.minsize(width=250, height=70)
label = Tkinter.Label(root, text=”Welcome!”, fg=”black”, font=”Verdana 30 bold”)
label.pack()
f = Tkinter.Frame(root)
start = Tkinter.Button(f, text=’Start’, width=6, command=lambda:Start(label))
stop = Tkinter.Button(f, text=’Stop’,width=6,state=’disabled’, command=Stop)
reset = Tkinter.Button(f, text=’Reset’,width=6, state=’disabled’, command=lambda:Reset(label))
f.pack(anchor = ‘center’,pady=5)
start.pack(side=”left”)
stop.pack(side =”left”)
reset.pack(side=”left”)
root.mainloop()

数据结构与结构体

数据结构与结构体

C++ 数据结构

C/C++ 数组允许定义可存储相同类型数据项的变量,但是结构是 C++ 中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。

结构用于表示一条记录,假设您想要跟踪图书馆中书本的动态,您可能需要跟踪每本书的下列属性:

  • Title :标题
  • Author :作者
  • Subject :类目
  • Book ID :书的 ID

%title插图%num

定义结构

为了定义结构,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:

  1. struct type_name {
  2. member_type1 member_name1;
  3. member_type2 member_name2;
  4. member_type3 member_name3;
  5. .
  6. .
  7. } object_names;

type_name 是结构体类型的名称;

member_type1 member_name1 是标准的变量定义;

比如 int i; 或者 float f; 或者其他有效的变量定义。

在结构定义的末尾,*后一个分号之前,您可以指定一个或多个结构变量,这是可选的。

下面是声明一个结构体类型 Books,变量为 book

  1. struct Books
  2. {
  3.    char  title[50];
  4.    char  author[50];
  5.    char  subject[100];
  6.    int   book_id;
  7. } book;

访问结构成员

为了访问结构的成员,我们使用成员访问运算符(.)

成员访问运算符是结构变量名称和我们要访问的结构成员之间的一个句号。

下面的实例演示了结构的用法:

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. // 声明一个结构体类型 Books
  5. struct Books
  6. {
  7.    char  title[50];
  8.    char  author[50];
  9.    char  subject[100];
  10.    int   book_id;
  11. };
  12. int main( )
  13. {
  14.    Books Book1;        // 定义结构体类型 Books 的变量 Book1
  15.    Books Book2;        // 定义结构体类型 Books 的变量 Book2
  16.    // Book1 详述
  17.    strcpy( Book1.title, “C++ 教程”);
  18.    strcpy( Book1.author, “Nowcoder”);
  19.    strcpy( Book1.subject, “编程语言”);
  20.    Book1.book_id = 12345;
  21.    // Book2 详述
  22.    strcpy( Book2.title, “CSS 教程”);
  23.    strcpy( Book2.author, “Nowcoder”);
  24.    strcpy( Book2.subject, “前端技术”);
  25.    Book2.book_id = 12346;
  26.    // 输出 Book1 信息
  27.    cout << “*本书标题 : “ << Book1.title <<endl;
  28.    cout << “*本书作者 : “ << Book1.author <<endl;
  29.    cout << “*本书类目 : “ << Book1.subject <<endl;
  30.    cout << “*本书 ID : “ << Book1.book_id <<endl;
  31.    // 输出 Book2 信息
  32.    cout << “第二本书标题 : “ << Book2.title <<endl;
  33.    cout << “第二本书作者 : “ << Book2.author <<endl;
  34.    cout << “第二本书类目 : “ << Book2.subject <<endl;
  35.    cout << “第二本书 ID : “ << Book2.book_id <<endl;
  36.    return 0;
  37. }

实例中定义了结构体类似 Books 及其两个变量 Book1 和 Book2。当上面的代码被编译和执行时,它会产生下列结果:

  1. *本书标题 : C++ 教程
  2. *本书作者 : Nowcoder
  3. *本书类目 : 编程语言
  4. *本书 ID : 12345
  5. 第二本书标题 : CSS 教程
  6. 第二本书作者 : Nowcoder
  7. 第二本书类目 : 前端技术
  8. 第二本书 ID : 12346

结构作为函数参数

可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。

可以使用上面实例中的方式来访问结构变量:

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. void printBook( struct Books book );
  5. // 声明一个结构体类型 Books
  6. struct Books
  7. {
  8.    char  title[50];
  9.    char  author[50];
  10.    char  subject[100];
  11.    int   book_id;
  12. };
  13. int main( )
  14. {
  15.    Books Book1;        // 定义结构体类型 Books 的变量 Book1
  16.    Books Book2;        // 定义结构体类型 Books 的变量 Book2
  17.     // Book1 详述
  18.    strcpy( Book1.title, “C++ 教程”);
  19.    strcpy( Book1.author, “Nowcoder”);
  20.    strcpy( Book1.subject, “编程语言”);
  21.    Book1.book_id = 12345;
  22.    // Book2 详述
  23.    strcpy( Book2.title, “CSS 教程”);
  24.    strcpy( Book2.author, “Nowcoder”);
  25.    strcpy( Book2.subject, “前端技术”);
  26.    Book2.book_id = 12346;
  27.    // 输出 Book1 信息
  28.    printBook( Book1 );
  29.    // 输出 Book2 信息
  30.    printBook( Book2 );
  31.    return 0;
  32. }
  33. void printBook( struct Books book )
  34. {
  35.    cout << “书标题 : “ << book.title <<endl;
  36.    cout << “书作者 : “ << book.author <<endl;
  37.    cout << “书类目 : “ << book.subject <<endl;
  38.    cout << “书 ID : “ << book.book_id <<endl;
  39. }

当上面的代码被编译和执行时,它会产生下列结果:

  1. 书标题 : C++ 教程
  2. 书作者 : Nowcoder
  3. 书类目 : 编程语言
  4. ID : 12345
  5. 书标题 : CSS 教程
  6. 书作者 : Nowcoder
  7. 书类目 : 前端技术
  8. ID : 12346

指向结构的指针

您可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下所示:

struct Books *struct_pointer;

现在,您可以在上述定义的指针变量中存储结构变量的地址。为了查找结构变量的地址,请把 & 运算符放在结构名称的前面,如下所示:

struct_pointer = &Book1;

为了使用指向该结构的指针访问结构的成员,您必须使用 -> 运算符,如下所示:

struct_pointer->title;

让我们使用结构指针来重写上面的实例,这将有助于您理解结构指针的概念:

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. void printBook( struct Books *book );
  5. struct Books
  6. {
  7.    char  title[50];
  8.    char  author[50];
  9.    char  subject[100];
  10.    int   book_id;
  11. };
  12. int main( )
  13. {
  14.    Books Book1;        // 定义结构体类型 Books 的变量 Book1
  15.    Books Book2;        // 定义结构体类型 Books 的变量 Book2
  16.     // Book1 详述
  17.    strcpy( Book1.title, “C++ 教程”);
  18.    strcpy( Book1.author, “Nowcoder”);
  19.    strcpy( Book1.subject, “编程语言”);
  20.    Book1.book_id = 12345;
  21.    // Book2 详述
  22.    strcpy( Book2.title, “CSS 教程”);
  23.    strcpy( Book2.author, “Nowcoder”);
  24.    strcpy( Book2.subject, “前端技术”);
  25.    Book2.book_id = 12346;
  26.    // 通过传 Book1 的地址来输出 Book1 信息
  27.    printBook( &Book1 );
  28.    // 通过传 Book2 的地址来输出 Book2 信息
  29.    printBook( &Book2 );
  30.    return 0;
  31. }
  32. // 该函数以结构指针作为参数
  33. void printBook( struct Books *book )
  34. {
  35.    cout << “书标题  : “ << book->title <<endl;
  36.    cout << “书作者 : “ << book->author <<endl;
  37.    cout << “书类目 : “ << book->subject <<endl;
  38.    cout << “书 ID : “ << book->book_id <<endl;
  39. }

当上面的代码被编译和执行时,它会产生下列结果:

  1. 书标题  : C++ 教程
  2. 书作者 : Nowcoder
  3. 书类目 : 编程语言
  4. ID : 12345
  5. 书标题  : CSS 教程
  6. 书作者 : Nowcoder
  7. 书类目 : 前端技术
  8. ID : 12346

typedef 关键字

下面是一种更简单的定义结构的方式,您可以为创建的类型取一个”别名”。例如:

  1. typedef struct Books
  2. {
  3. char title[50];
  4. char author[50];
  5. char subject[100];
  6. int book_id;
  7. }BOOKS; //大写
  8. BOOKS book1, books; //定义变量,用大写更好区分。

现在,您可以直接使用 Books 来定义 Books 类型的变量,而不需要使用 struct 关键字。下面是实例:

Books Book1, Book2;

您可以使用 typedef 关键字来定义非结构类型,如下所示:

  1. typedef long int *pint32;
  2. pint32 x, y, z;
  3. x, y 和 z 都是指向长整型 long int 的指针