iOS开发系列--C语言之数组和字符串

简介:

概览

数组在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*/
}

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

arrayAddress1.2

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

#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数组为例,它在内存中的结构如下图

arrayAddress2.1

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

  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在内存中的存储。

arrayAddress3.1

从图中我们不难发现由于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’结尾的字符数组长度,但是它并不能计算其他类型的数组长度。

目录
相关文章
|
10天前
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
96 66
|
21天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
24天前
|
传感器 算法 安全
【C语言】两个数组比较详解
比较两个数组在C语言中有多种实现方法,选择合适的方法取决于具体的应用场景和性能要求。从逐元素比较到使用`memcmp`函数,再到指针优化,每种方法都有其优点和适用范围。在嵌入式系统中,考虑性能和资源限制尤为重要。通过合理选择和优化,可以有效提高程序的运行效率和可靠性。
78 6
|
25天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
28天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
46 6
|
27天前
|
存储 前端开发 Swift
探索iOS开发:从新手到专家的旅程
本文将带您领略iOS开发的奇妙之旅,从基础概念的理解到高级技巧的掌握,逐步深入iOS的世界。文章不仅分享技术知识,还鼓励读者在编程之路上保持好奇心和创新精神,实现个人成长与技术突破。
|
28天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
54 5
|
28天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
26天前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
43 1
|
26天前
|
NoSQL 编译器 C语言
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。高级技巧包括内存检查、性能分析和符号调试。通过实践案例学习如何有效定位和解决问题,同时注意保持耐心、合理利用工具、记录过程并避免过度调试,以提高编程能力和开发效率。
43 1