概览

数组在C语言中有着特殊的地位,它有很多特性,例如它的存储是连续的,数组的名称就是数组的地址等。而在C语言中是没有String类型的,那么如果要表示一个字符串,就必须使用字符数组。今天主要就介绍如下三个方面:

  1. 一维数组
  2. 多维数组
  3. 字符串

一维数组

一维数组操作比较简单,但是需要注意,数组长度必须是固定的,长度不能使用变量进行初始化;如果声明的同时进行赋值则数组长度可以省略,编译器会自动计算数组长度;同时数组不能先声明再一次性赋值(当然可以对每个元素一一赋值)。

#include <stdio.h>

int main(){
int len = 2;
//int a[len] = { 1, 2};//错误,不能使变量
int a[2];//正确
a[0] = 1;
a[1] = 2;
//a[2] = 3;//超过数组长度,但是编译器并不会检查,运行报错
int b['a'] = {1,2,3};//'a'=97,所以可以作为数组长度,但是后面的元素没有初始化,其值默认为0
for (int i = 0; i < 97; ++i){
printf("b[%d]=%d\n",i,b[i]);
}
int c[2 * 3];//2*3是固定值可以作为数组长度
int d[] = { 1, 2, 3 };//如果初始化的同时赋值则数组长度可以省略,当前个数为3
}

扩展--数组的存储

数组在内存中存储在一块连续的空间中,如果知道数组类型(int、float等)和初始地址就可以知道其他元素的地址,同时由于数组名等于数组第一个元素的地址,所以当数组作为参数(作为参数时形参可以省略)其实是引用传递。

#include <stdio.h>

int main(){
int const l = 3;
int a[l] = { 1, 2,3 };
for (int i = 0; i < l; ++i){
//由于当前在32位编译器下,int型长度为4个字节,可以判断出三个地址两两相差都是4
printf("a[%d]=%d,address=%x\n", i, a[i], &a[i]);
}
/*当前输出结果:
a[0] = 1, address = c9f95c
a[1] = 2, address = c9f960
a[2] = 3, address = c9f964*/
}

我们看一下上面定义的数组在内存中存储结构

再来看一下数组作为参数传递的情况,数组作为参数传递的是数组的地址

#include <stdio.h>

void changeValue(int a[]){ a[0] = 10;
} int main(){ int a[2] = {1,2};
changeValue(a); for (int i = 0; i < 2; ++i){
printf("a[%d]=%d\n",i,a[i]);
} /*打印结果
a[0]=10
a[1]=2
*/
}

多维数组

多维数组其实可以看成是一个特殊的一维数组,只是每个元素又是一个一维数组,下面简单看一下多维数组的初始化和赋值

#include <stdio.h>

int main(){
int a[2][3];//2行3列,二维数组可以看成是一个特殊的一维数组,只是它的每一个元素又是一个一维数组
a[0][0] = 1;
a[0][1] = 2;
a[0][2] = 3;
a[1][0] = 4;
a[1][1] = 5;
a[1][2] = 6;
for (int i = 0; i < 2; ++i){
for (int j = 0; j < 3; ++j){
printf("a[%d][%d]=%d,address=%x\n", i, j, a[i][j], &a[i][j]);
}
}
/*打印结果
a[0][0]=1,address=f8fb24
a[0][1]=2,address=f8fb28
a[0][2]=3,address=f8fb2c
a[1][0]=4,address=f8fb30
a[1][1]=5,address=f8fb34
a[1][2]=6,address=f8fb38
*/
//初始化并直接赋值
int b[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
//由于数组的赋值顺序是先从第一行第一列,再第一行第二列...然后第二行第一列...,所以我们也可以写成如下形式
int c[2][3] = { 1, 2, 3, 4, 5, 6 };
//也可以只初始化部分数据,其余元素默认为0
int d[2][3] = { 1, 2, 3, 4 };
for (int i = 0; i < 2; ++i){
for (int j = 0; j < 3; ++j){
printf("d[%d][%d]=%d\n", i, j, d[i][j]);
}
}
/*打印结果
d[0][0]=1
d[0][1]=2
d[0][2]=3
d[1][0]=4
d[1][1]=0
d[1][2]=0
*/
//当然下面赋值也可以
int e[2][3] = { {}, { 4, 5, 6 } };
//可以省略行号,但是绝对不可以省略列号,因为按照上面说的赋值顺序,它无法判断有多少行
int f[][3] = { {1,2,3},{4,5,6} };
}

扩展--多维数组的存储

以上面a数组为例,它在内存中的结构如下图

根据上图和一维数组的存储,对于二维数组可以得出如下结论:数组名就是整个二维数组的地址,也等于第一行数组名的地址,还等于第一个元素的地址;第二行数组名等于第二行第一个元素的地址。用表达式表示:

  1. a=a[0]=&a[0][0]

  2. a[1]=&a[1][0]

同样可以得出a[i][j]=a[i]+j。关于三维数组、四维数组等多维数组,其实可以以此类推,在此不再赘述。

字符串

在C语言中是没有字符串类型的,如果要表示字符串需要使用char类型的数组,因为字符串本身就是多个字符的组合。但是需要注意的是字符串是一个特殊的数组,在它的结束位置必须要加一个”\0”(ASCII中0是空操作符,表示什么也不做)来表示字符串结束,否则编译器是不知道什么时候字符串已经结束的。当直接使用字符串赋值的时候程序会自动加上”\0”作为结束符。

//
// main.c
// ArrayAndString
//
// Created by KenshinCui on 14-7-06.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
// #include <stdio.h> int main(int argc, const char * argv[])
{ char a[] = {'K','e','n','s','h','i','n','\0'};
printf("%s",a); //结果:Kenshin,注意使用%s输出字符串内容,如果换成整形输出格式其实输出的是a的地址
printf("\n");
printf("address=%x", a); //结果:address=5fbff890
printf("\n");
//后面的\0绝对不能省略,如果没有\0则会出现如下情况
char b[] = { 'I', 'a', 'm'};
printf("%s",b); //没有按照期望输出,多了一些垃圾数据,在当前环境打印结果:IamKenshin
printf("\n");
printf("address=%x",b); //结果:address=5fbff88d
printf("\n");
//直接赋值为字符串,此时不需要手动添加\0,编译器会自动添加
char c[] = "Kenshin";
printf("c=%s",c); //结果:c=Kenshin
printf("\n"); //二维数组存储多个字符串
char d[2][3]={"Kenshin","Kaoru","Rose","Jack","Tom","Jerry"}; return 0;
}

从上面代码注释中可以看到打印b的时候不是直接打印出来“Iam”而是打印出了“IamKenshin”,原因就是编译器无法判断字符串是否结束,要解释为什么打印出“IamKenshin”我们需要了解a和b在内存中的存储。

从图中我们不难发现由于a占用8个字节,而定义完a后直接定义了b,此时分配的空间连续,b占用3个字节,这样当输出b的时候由于输出完“Iam”之后并未遇到”\0”标记,程序继续输出直到遇到数组a中的“\0”才结束,因此输出内容为“IamKenshin”。

扩展--字符串操作常用函数

下面简单看一下和字符和字符串相关的常用的几个函数

//
// main.c
// ArrayAndString
//
// Created by Kenshin Cui on 14-7-04.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
// #include <stdio.h> int main(int argc, const char * argv[])
{
/*字符操作*/
putchar('a'); //结果:a,putchar一次只能输出一个字符
printf("\n");
putchar(97);//结果:a
printf("\n");
char a;
a=getchar();//getchar()一次只能接收一个字符,可以接收空格、tab、回车
printf("a=%c",a);
printf("\n"); /*字符串操作*/
char b[]="Kenshin";
printf("b=%s",b);
printf("\n");
puts(b); //puts用于输出单个字符串,不能像printf格式化输出,会自动添加换行
printf("\n"); char c[10];
scanf("%s",c);//注意c没必要写成&c,因为c本身就代表了数组的地址
printf("c=%s\n",c);//注意即使你输入的内容大于10,也能正确输出,但是下面的gets()函数却不行
printf("\n"); //gets()函数,注意它是不安全的,因为接收的时候不知道它的大小容易造成溢出,建议不要使用
char d[10];
gets(d); //gets一次只能接收一个字符串,但是scanf可接收多个;scanf不能接收空格、tab,gets则可以
printf("d=%s",d);
printf("\n"); char e[]={'K','s','\0'};
printf("%lu",strlen(e)); //结果是:2,不是3,因为\0不计入长度
printf("\n");
char f[]={"Kenshin"};
printf("%lu",strlen(f)); //结果是:7
printf("\n"); char g[5];
strcpy(g,"hello,world!");
printf("%s",g); //结果是:hello,即使定义的g长度为5,但是也能完全拷贝进去
printf("\n");
char h[5];
char i[]={'a','b','c','\0','d','e','f','\0'};
strcpy(h,i);
printf("%s",h); //结果是:abc,遇到第一个\0则结束
printf("\n"); strcat(i,"ghi");
printf("%s",i); //结果是:abcghi,注意不是abcdefghi,strcat,从i第一\0开始使用“ghi”覆盖,覆盖完之后加上一个\0,在内存中目前应该是:{'a','b','c','g','h','i','\0','f','\0'}
printf("\n"); char j[]="abc";
char k[]="aBc";
char l[]="acb";
char m[]={'a','\0'};
printf("%d,%d,%d",strcmp(j,k),strcmp(k,l),strcmp(l,m));//遇到第一个不相同的字符或\0则返回两者前后之差,结果:32,-33,99
printf("\n"); return 0;
}

注意:

1.在Xcode中会提示gets是不安全的,推荐使用fgets()。

2.strlen()只用于计算字符串长度,由于在C语言中字符串使用字符数组长度表示,所以它可以计算带有’\0’结尾的字符数组长度,但是它并不能计算其他类型的数组长度。

iOS开发系列--C语言之数组和字符串的更多相关文章

  1. iOS开发系列--Swift语言

    概述 Swift是苹果2014年推出的全新的编程语言,它继承了C语言.ObjC的特性,且克服了C语言的兼容性问题.Swift发展过程中不仅保留了ObjC很多语法特性,它也借鉴了多种现代化语言的特点,在 ...

  2. iOS开发系列--C语言之基础知识

    概览 当前移动开发的趋势已经势不可挡,这个系列希望浅谈一下个人对IOS开发的一些见解,这个IOS系列计划从几个角度去说IOS开发: C语言 OC基础 IOS开发(iphone/ipad) Swift ...

  3. iOS开发环境C语言基础 数组 函数

    1 求数组元素的最大值 1.1 问题 创建程序,实现查询数组中最大值的功能,需求为:创建一个长度为10的数组,数组内放置10个0~99之间(包含0,包含99)的随机数作为数组内容,要求查询出数组中的最 ...

  4. iOS开发系列--C语言之指针

    概览 指针是C语言的精髓,但是很多初学者往往对于指针的概念并不深刻,以至于学完之后随着时间的推移越来越模糊,感觉指针难以掌握,本文通过简单的例子试图将指针解释清楚,今天的重点有几个方面: 什么是指针 ...

  5. iOS开发系列--C语言之存储方式和作用域

    概述 基本上每种语言都要讨论这个话题,C语言也不例外,因为只有你完全了解每个变量或函数存储方式.作用范围和销毁时间才可能正确的使用这门语言.今天将着重介绍C语言中变量作用范围.存储方式.生命周期.作用 ...

  6. iOS开发系列--C语言之构造类型

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

  7. iOS开发系列--C语言之预处理

    概述 大家都知道一个C程序的运行包括编译和链接两个阶段,其实在编译之前预处理器首先要进行预处理操作,将处理完产生的一个新的源文件进行编译.由于预处理指令是在编译之前就进行了,因此很多时候它要比在程序运 ...

  8. iOS开发系列文章(持续更新……)

    iOS开发系列的文章,内容循序渐进,包含C语言.ObjC.iOS开发以及日后要写的游戏开发和Swift编程几部分内容.文章会持续更新,希望大家多多关注,如果文章对你有帮助请点赞支持,多谢! 为了方便大 ...

  9. iOS开发系列--Swift进阶

    概述 上一篇文章<iOS开发系列--Swift语言>中对Swift的语法特点以及它和C.ObjC等其他语言的用法区别进行了介绍.当然,这只是Swift的入门基础,但是仅仅了解这些对于使用S ...

随机推荐

  1. CozyRSS开发记录-中断

    接下来会对CozyRSS做一堆的修改,包括界面和功能.因为比较杂乱,而且大多是体力活,所以就不记录了.等下次再发的时候,见到的就是一个有可用性的CozyRSS了!

  2. WEB安全:XSS漏洞与SQL注入漏洞介绍及解决方案(转)

    对web安全方面的知识非常薄弱,这篇文章把Xss跨站攻击和sql注入的相关知识整理了下,希望大家多多提意见. 对于防止sql注入发生,我只用过简单拼接字符串的注入及参数化查询,可以说没什么好经验,为避 ...

  3. Asp.Net中使用OpenRowSet操作Excel表,导入Sql Server(实例)

    有两种接口可供选择:Microsoft.Jet.OLEDB.4.0(以下简称 Jet 引擎)和Microsoft.ACE.OLEDB.12.0(以下简称 ACE 引擎). Jet 引擎大家都很熟悉,可 ...

  4. python 学习第二天

    由于换了博客,第一篇没有在博客园写,写在了开源中国上,链接地址为http://my.oschina.net/u/254063/blog/719289,大家有兴趣可以看看 一, python 数据类型 ...

  5. Service and controller in angularJs

    Separation of concern is at the heart while designing an AngularJS application. Your controller must ...

  6. Python 学习手册, char 14 - 15

    Char 14 迭代器和解析器 可迭代的 : 支持iter的一个对象 迭代器  : iter 所返回的一个支持next(I)的对象 Python迭代工具会自动调用这些函数,我们也可以手动地应用迭代协议 ...

  7. 商贸食品车销成功应用PDA抄单 现场开单 打印销售单安卓智能手持POS应用

    中小超市配送食品,酒水饮料,业务员以往是挨家挨户抄每个超市需要哪些东西,晚上回公司再统计,打到软件里面,开单配货. 选用PDA后,人手一台,直接在超市里面抄好货物,通过网络传输到公司软件上面,加快了工 ...

  8. servlet jsp jdbc bootstrarp mvc分层模式实现的第一个项目

    登录注册界面 这是一个注册和登录的界面 用到了前端页面中自带的一点H5的标签和属性---巩固下 邮箱格式 :type="email"  不能为空:  required=" ...

  9. # git 操作拾遗

    如何删除本地库 find . -name ".git" | xargs rm -Rf fatal: refusing to merge unrelated histories gi ...

  10. 通过LDAP管理VSFTP帐户

    yum install -y openldap openldap-servers openldap-clients pam_ldap nss-pam-ldapd vsftpd slappasswd # ...