IOS開(kāi)發(fā)之路--C語(yǔ)言數(shù)組和字符串
概覽
數(shù)組在C語(yǔ)言中有著特殊的地位,它有很多特性,例如它的存儲(chǔ)是連續(xù)的,數(shù)組的名稱就是數(shù)組的地址等。而在C語(yǔ)言中是沒(méi)有String類型的,那么如果要表示一個(gè)字符串,就必須使用字符串?dāng)?shù)組。今天主要就介紹如下三個(gè)方面:
一維數(shù)組 多維數(shù)組 字符串
一維數(shù)組
一維數(shù)組操作比較簡(jiǎn)單,但是需要注意,數(shù)組長(zhǎng)度必須是固定的,長(zhǎng)度不能使用變量進(jìn)行初始化;如果聲明的同時(shí)進(jìn)行賦值則數(shù)組長(zhǎng)度可以省略,編譯器會(huì)自動(dòng)計(jì)算數(shù)組長(zhǎng)度;同時(shí)數(shù)組不能先聲明再一次性賦值(當(dāng)然可以對(duì)每個(gè)元素一一賦值)。
#include <stdio.h> int main(){ int len = 2; //int a[len] = { 1, 2};//錯(cuò)誤,不能使變量 int a[2];//正確 a[0] = 1; a[1] = 2; //a[2] = 3;//超過(guò)數(shù)組長(zhǎng)度,但是編譯器并不會(huì)檢查,運(yùn)行報(bào)錯(cuò) int b['a'] = {1,2,3};//'a'=97,所以可以作為數(shù)組長(zhǎng)度,但是后面的元素沒(méi)有初始化,其值默認(rèn)為0 for (int i = 0; i < 97; ++i){ printf("b[%d]=%d\n",i,b[i]); } int c[2 * 3];//2*3是固定值可以作為數(shù)組長(zhǎng)度 int d[] = { 1, 2, 3 };//如果初始化的同時(shí)賦值則數(shù)組長(zhǎng)度可以省略,當(dāng)前個(gè)數(shù)為3 }
擴(kuò)展--數(shù)組的存儲(chǔ)
數(shù)組在內(nèi)存中存儲(chǔ)在一塊連續(xù)的空間中,如果知道數(shù)組類型(int、float等)和初始地址就可以知道其他元素的地址,同時(shí)由于數(shù)組名等于數(shù)組第一個(gè)元素的地址,所以當(dāng)數(shù)組作為參數(shù)(作為參數(shù)時(shí)形參可以省略)其實(shí)是引用傳遞。
#include <stdio.h> int main(){ int const l = 3; int a[l] = { 1, 2,3 }; for (int i = 0; i < l; ++i){ //由于當(dāng)前在32位編譯器下,int型長(zhǎng)度為4個(gè)字節(jié),可以判斷出三個(gè)地址兩兩相差都是4 printf("a[%d]=%d,address=%x\n", i, a[i], &a[i]); } /*當(dāng)前輸出結(jié)果: a[0] = 1, address = c9f95c a[1] = 2, address = c9f960 a[2] = 3, address = c9f964*/ }
我們看一下上面定義的數(shù)組在內(nèi)存中存儲(chǔ)結(jié)構(gòu)
再來(lái)看一下數(shù)組(注意不是數(shù)組的元素,是數(shù)組)作為參數(shù)傳遞的情況
#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]); } /*打印結(jié)果 a[0]=10 a[1]=2 */ }
多維數(shù)組
多維數(shù)組其實(shí)可以看成是一個(gè)特殊的一維數(shù)組,只是每個(gè)元素又是一個(gè)一維數(shù)組,下面簡(jiǎn)單看一下多維數(shù)組的初始化和賦值
#include <stdio.h> int main(){ int a[2][3];//2行3列,二維數(shù)組可以看成是一個(gè)特殊的一維數(shù)組,只是它的每一個(gè)元素又是一個(gè)一維數(shù)組 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]); } } /*打印結(jié)果 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 } }; //由于數(shù)組的賦值順序是先從第一行第一列,再第一行第二列...然后第二行第一列...,所以我們也可以寫(xiě)成如下形式 int c[2][3] = { 1, 2, 3, 4, 5, 6 }; //也可以只初始化部分?jǐn)?shù)據(jù),其余元素默認(rèn)為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]); } } /*打印結(jié)果 d[0][0]=1 d[0][1]=2 d[0][2]=3 d[1][0]=4 d[1][1]=0 d[1][2]=0 */ //當(dāng)然下面賦值也可以 int e[2][3] = { {}, { 4, 5, 6 } }; //可以省略行號(hào),但是絕對(duì)不可以省略列號(hào),因?yàn)榘凑丈厦嬲f(shuō)的賦值順序,它無(wú)法判斷有多少行 int f[][3] = { {1,2,3},{4,5,6} }; }
擴(kuò)展--多維數(shù)組的存儲(chǔ)
以上面a數(shù)組為例,它在內(nèi)存中的結(jié)構(gòu)如下圖
根據(jù)上圖和一維數(shù)組的存儲(chǔ),對(duì)于二維數(shù)組可以得出如下結(jié)論:數(shù)組名就是整個(gè)二維數(shù)組的地址,也等于第一行數(shù)組名的地址,還等于第一個(gè)元素的地址;第二行數(shù)組名等于第二行第一個(gè)元素的地址。用表達(dá)式表示:
a=a[0]=&a[0][0] a[1]=&a[1][0]
關(guān)于多維數(shù)組,其實(shí)可以以此類推,在此不再贅述。
字符串
在C語(yǔ)言中是沒(méi)有字符串類型的,如果要表示字符串需要使用char類型的數(shù)組,因?yàn)樽址旧砭褪嵌鄠€(gè)字符的組合。但是需要注意的是字符串是一個(gè)特殊的數(shù)組,在它的結(jié)束位置必須要加一個(gè)”\0”(ASCII中0是空操作符,表示什么也不做)來(lái)表示字符串結(jié)束,否則編譯器是不知道什么時(shí)候字符串已經(jīng)結(jié)束的。當(dāng)直接使用字符串賦值的時(shí)候程序會(huì)自動(dòng)加上”\0”作為結(jié)束符。
// // 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); //結(jié)果:Kenshin,注意使用%s輸出字符串內(nèi)容,如果換成整形輸出格式其實(shí)輸出的是a的地址 printf("\n"); printf("address=%x", a); //結(jié)果:address=5fbff890 printf("\n"); //后面的\0絕對(duì)不能省略,如果沒(méi)有\(zhòng)0則會(huì)出現(xiàn)如下情況 char b[] = { 'I', 'a', 'm'}; printf("%s",b); //沒(méi)有按照期望輸出,多了一些垃圾數(shù)據(jù),在當(dāng)前環(huán)境打印結(jié)果:IamKenshin printf("\n"); printf("address=%x",b); //結(jié)果:address=5fbff88d printf("\n"); //直接賦值為字符串,此時(shí)不需要手動(dòng)添加\0,編譯器會(huì)自動(dòng)添加 char c[] = "Kenshin"; printf("c=%s",c); //結(jié)果:c=Kenshin printf("\n"); //二維數(shù)組存儲(chǔ)多個(gè)字符串 char d[2][3]={"Kenshin","Kaoru","Rose","Jack","Tom","Jerry"}; return 0; }
從上面代碼注釋中可以看到打印b的時(shí)候不是直接打印出來(lái)“Iam”而是打印出了“IamKenshin”,原因就是編譯器無(wú)法判斷字符串是否結(jié)束,要解釋為什么打印出“IamKenshin”我們需要了解a和b在內(nèi)存中的存儲(chǔ)。
從圖中我們不難發(fā)現(xiàn)由于a占用8個(gè)字節(jié),而定義完a后直接定義了b,此時(shí)分配的空間連續(xù),b占用3個(gè)字節(jié),這樣當(dāng)輸出b的時(shí)候由于輸出完“Iam”之后并未遇到”\0”標(biāo)記,程序繼續(xù)輸出直到遇到數(shù)組a中的“\0”才結(jié)束,因此輸出內(nèi)容為“IamKenshin”。
擴(kuò)展--字符串操作常用函數(shù)
下面簡(jiǎn)單看一下和字符和字符串相關(guān)的常用的幾個(gè)函數(shù)
// // 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'); //結(jié)果:a,putchar一次只能輸出一個(gè)字符 printf("\n"); putchar(97);//結(jié)果:a printf("\n"); char a; a=getchar();//getchar()一次只能接收一個(gè)字符,可以接收空格、tab、回車 printf("a=%c",a); printf("\n"); /*字符串操作*/ char b[]="Kenshin"; printf("b=%s",b); printf("\n"); puts(b); //puts用于輸出單個(gè)字符串,不能像printf格式化輸出,會(huì)自動(dòng)添加換行 printf("\n"); char c[10]; scanf("%s",c);//注意c沒(méi)必要寫(xiě)成&c,因?yàn)閏本身就代表了數(shù)組的地址 printf("c=%s\n",c);//注意即使你輸入的內(nèi)容大于10,也能正確輸出,但是下面的gets()函數(shù)卻不行 printf("\n"); //gets()函數(shù),注意它是不安全的,因?yàn)榻邮盏臅r(shí)候不知道它的大小容易造成溢出,建議不要使用 char d[10]; gets(d); //gets一次只能接收一個(gè)字符串,但是scanf可接收多個(gè);scanf不能接收空格、tab,gets則可以 printf("d=%s",d); printf("\n"); char e[]={'K','s','\0'}; printf("%lu",strlen(e)); //結(jié)果是:2,不是3,因?yàn)閈0不計(jì)入長(zhǎng)度 printf("\n"); char f[]={"Kenshin"}; printf("%lu",strlen(f)); //結(jié)果是:7 printf("\n"); char g[5]; strcpy(g,"hello,world!"); printf("%s",g); //結(jié)果是:hello,即使定義的g長(zhǎng)度為5,但是也能完全拷貝進(jìn)去 printf("\n"); char h[5]; char i[]={'a','b','c','\0','d','e','f','\0'}; strcpy(h,i); printf("%s",h); //結(jié)果是:abc,遇到第一個(gè)\0則結(jié)束 printf("\n"); strcat(i,"ghi"); printf("%s",i); //結(jié)果是:abcghi,注意不是abcdefghi,strcat,從i第一\0開(kāi)始使用“ghi”覆蓋,覆蓋完之后加上一個(gè)\0,在內(nèi)存中目前應(yīng)該是:{'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));//遇到第一個(gè)不相同的字符或\0則返回兩者前后之差,結(jié)果:32,-33,99 printf("\n"); return 0; }
注意在Xcode中會(huì)提示gets是不安全的,因?yàn)閄code使用的是gcc編譯器,在gcc編譯器中已經(jīng)不能正確編譯gets()函數(shù),推薦使用fgets()。
相關(guān)文章
iOS項(xiàng)目開(kāi)發(fā)鍵盤(pán)彈出遮擋輸入框問(wèn)題解決方案
大家在用IOS開(kāi)發(fā)項(xiàng)目的時(shí)候,經(jīng)常出現(xiàn)鍵盤(pán)彈出遮擋輸入框問(wèn)題,小編給大家整理的這個(gè)問(wèn)題的處理方法,一起學(xué)習(xí)下。2018-01-01iOS tableView實(shí)現(xiàn)頭部拉伸并改變導(dǎo)航條漸變色
這篇文章主要為大家詳細(xì)介紹了iOS tableView實(shí)現(xiàn)頭部拉伸并改變導(dǎo)航條漸變色,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05iOS中setValue和setObject的區(qū)別詳解
setObject:ForKey: 是NSMutableDictionary特有的;setValue:ForKey:是KVC的主要方法。接下來(lái)通過(guò)本文給大家分享iOS中setValue和setObject的區(qū)別,需要的朋友參考下2017-02-02iOS應(yīng)用內(nèi)實(shí)現(xiàn)跳轉(zhuǎn)到手機(jī)淘寶天貓的方法
這篇文章主要給大家介紹了關(guān)于iOS應(yīng)用內(nèi)如何實(shí)現(xiàn)跳轉(zhuǎn)到手機(jī)淘寶天貓的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12iOS使用音頻處理框架The Amazing Audio Engine實(shí)現(xiàn)音頻錄制播放
這篇文章主要為大家詳細(xì)介紹了iOS使用音頻處理框架The Amazing Audio Engine實(shí)現(xiàn)音頻錄制播放,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04iOS實(shí)現(xiàn)九宮格自動(dòng)生成視圖
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)九宮格自動(dòng)生成視圖的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03iOS實(shí)現(xiàn)底部彈出PopupWindow效果 iOS改變背景透明效果
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)底部彈出PopupWindow效果,iOS改變背景透明效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07