二維數(shù)組
- 所謂二維數(shù)組就是一個(gè)一維數(shù)組的每個(gè)元素又被聲明為一 維數(shù)組,從而構(gòu)成二維數(shù)組. 可以說二維數(shù)組是特殊的一維數(shù)組亏吝。
- 示例:
- int a[2][3] = { {80,75,92}, {61,65,71}};
-
可以看作由一維數(shù)組a[0]和一維數(shù)組a[1]組成茄袖,這兩個(gè)一維數(shù)組都包含了3個(gè)int類型的元素
二維數(shù)組的定義
- 格式:
- 數(shù)據(jù)類型 數(shù)組名[一維數(shù)組的個(gè)數(shù)][一維數(shù)組的元素個(gè)數(shù)]
- 其中"一維數(shù)組的個(gè)數(shù)"表示當(dāng)前二維數(shù)組中包含多少個(gè)一維數(shù)組
- 其中"一維數(shù)組的元素個(gè)數(shù)"表示當(dāng)前前二維數(shù)組中每個(gè)一維數(shù)組元素的個(gè)數(shù)
二維數(shù)組的初始化
-
二維數(shù)的初始化可分為兩種:
- 定義的同時(shí)初始化
- 先定義后初始化
定義的同時(shí)初始化
int a[2][3]={ {80,75,92}, {61,65,71}};
- 先定義后初始化
int a[2][3];
a[0][0] = 80;
a[0][1] = 75;
a[0][2] = 92;
a[1][0] = 61;
a[1][1] = 65;
a[1][2] = 71;
- 按行分段賦值
int a[2][3]={ {80,75,92}, {61,65,71}};
- 按行連續(xù)賦值
int a[2][3]={ 80,75,92,61,65,71};
- 其它寫法
- 完全初始化,可以省略第一維的長(zhǎng)度
int a[][3]={{1,2,3},{4,5,6}};
int a[][3]={1,2,3,4,5,6};
- 部分初始化,可以省略第一維的長(zhǎng)度
int a[][3]={{1},{4,5}};
int a[][3]={1,2,3,4};
- 注意: 有些人可能想不明白捶箱,為什么可以省略行數(shù)彬碱,但不可以省略列數(shù)生年。也有人可能會(huì)問蠢终,可不可以只指定行數(shù)讳癌,但是省略列數(shù)穿稳?其實(shí)這個(gè)問題很簡(jiǎn)單,如果我們這樣寫:
int a[2][] = {1, 2, 3, 4, 5, 6}; // 錯(cuò)誤寫法
大家都知道晌坤,二維數(shù)組會(huì)先存放第1行的元素逢艘,由于不確定列數(shù)旦袋,也就是不確定第1行要存放多少個(gè)元素,所以這里會(huì)產(chǎn)生很多種情況埋虹,可能1猜憎、2是屬于第1行的,也可能1搔课、2胰柑、3、4是第一行的爬泥,甚至1柬讨、2、3袍啡、4踩官、5、6全部都是屬于第1行的
- 指定元素的初始化
int a[2][3]={[1][2]=10};
int a[2][3]={[1]={1,2,3}}
二維數(shù)組的應(yīng)用場(chǎng)景
二維數(shù)組的遍歷和存儲(chǔ)
1.二維數(shù)組的遍歷
- 二維數(shù)組a[3][4],可分解為三個(gè)一維數(shù)組,其數(shù)組名分別為:
- 這三個(gè)一維數(shù)組都有4個(gè)元素,例如:一維數(shù)組a[0]的 元素為a[0][0],a[0][1],a[0][2],a[0][3]境输。
- 所以遍歷二維數(shù)組無非就是先取出二維數(shù)組中得一維數(shù)組, 然后再從一維數(shù)組中取出每個(gè)元素的值
- 示例
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("%c", cs[0][0]);// 第一個(gè)[0]取出一維數(shù)組, 第二個(gè)[0]取出一維數(shù)組中對(duì)應(yīng)的元素
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
for (int i = 0; i < 2; i++) { // 外循環(huán)取出一維數(shù)組
// i
for (int j = 0; j < 3; j++) {// 內(nèi)循環(huán)取出一維數(shù)組的每個(gè)元素
printf("%c", cs[i][j]);
}
printf("\n");
}
注意: 必須強(qiáng)調(diào)的是,a[0],a[1],a[2]不能當(dāng)作下標(biāo)變量使用,它們是數(shù)組名,不是一個(gè)單純的下標(biāo)變量
二維數(shù)組的存儲(chǔ)
- 和以為數(shù)組一樣
- 給數(shù)組分配存儲(chǔ)空間從內(nèi)存地址大開始分配
- 給數(shù)組元素分配空間, 從所占用內(nèi)存地址小的開始分配
- 往每個(gè)元素中存儲(chǔ)數(shù)據(jù)從高地址開始存儲(chǔ)
#include <stdio.h>
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
// cs == &cs == &cs[0] == &cs[0][0]
printf("cs = %p\n", cs); // 0060FEAA
printf("&cs = %p\n", &cs); // 0060FEAA
printf("&cs[0] = %p\n", &cs[0]); // 0060FEAA
printf("&cs[0][0] = %p\n", &cs[0][0]); // 0060FEAA
return 0;
}
二維數(shù)組與函數(shù)
- 值傳遞
#include <stdio.h>
// 和一位數(shù)組一樣, 只看形參是基本類型還是數(shù)組類型
// 如果是基本類型在函數(shù)中修改形參不會(huì)影響實(shí)參
void change(char ch){
ch = 'n';
}
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs[0][0]);
printf("cs[0][0] = %c\n", cs[0][0]); // a
return 0;
}
- 地址傳遞
#include <stdio.h>
// 和一位數(shù)組一樣, 只看形參是基本類型還是數(shù)組類型
// 如果是數(shù)組類型在函數(shù)中修改形參會(huì)影響實(shí)參
void change(char ch[]){
ch[0] = 'n';
}
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs[0]);
printf("cs[0][0] = %c\n", cs[0][0]); // n
return 0;
}
#include <stdio.h>
// 和一位數(shù)組一樣, 只看形參是基本類型還是數(shù)組類型
// 如果是數(shù)組類型在函數(shù)中修改形參會(huì)影響實(shí)參
void change(char ch[][3]){
ch[0][0] = 'n';
}
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs);
printf("cs[0][0] = %c\n", cs[0][0]); // n
return 0;
}
二維數(shù)組作為函數(shù)參數(shù)注意點(diǎn)
- 形參錯(cuò)誤寫法
void test(char cs[2][]) // 錯(cuò)誤寫法
{
printf("我被執(zhí)行了\n");
}
void test(char cs[2][3]) // 正確寫法
{
printf("我被執(zhí)行了\n");
}
void test(char cs[][3]) // 正確寫法
{
printf("我被執(zhí)行了\n");
}
- 二維數(shù)組作為函數(shù)參數(shù)蔗牡,在被調(diào)函數(shù)中不能獲得其有多少行,需要通過參數(shù)傳入
void test(char cs[2][3])
{
int row = sizeof(cs); // 輸出4或8
printf("row = %zu\n", row);
}
- 二維數(shù)組作為函數(shù)參數(shù)嗅剖,在被調(diào)函數(shù)中可以計(jì)算出二維數(shù)組有多少列
void test(char cs[2][3])
{
size_t col = sizeof(cs[0]); // 輸出3
printf("col = %zd\n", col);
}
作業(yè)
玩家通過鍵盤錄入 w,s,a,d控制小人向不同方向移動(dòng),其中w代表向上移動(dòng),s代表向 下移動(dòng),a代表向左移動(dòng),d 代表向右移動(dòng),當(dāng)小人移動(dòng)到出口位置,玩家勝利
思路:
1.定義二維數(shù)組存放地圖
######
#O #
# ## #
# # #
## #
######
-
2.規(guī)定地圖的方向
- 3.編寫程序控制方向
- 當(dāng)輸入w或者W, 小人向上移動(dòng). x-1
- 當(dāng)輸入s 或者S, 小人向下. x+1
- 當(dāng)輸入a或者A, 小人向左. y-1
- 當(dāng)輸入d或者D, 小人向右. y+1
- 4.移動(dòng)小人
- 用變量記錄小人當(dāng)前的位置
- 1)如果小人將要移動(dòng)的位置是墻,則無法移動(dòng)
- 2)如果小人將要移動(dòng)的位置是路,則可以移動(dòng)
- 用變量記錄小人當(dāng)前的位置
- 5.判斷是否走出迷宮
字符串的基本概念
- 字符串是位于雙引號(hào)中的字符序列
-
在內(nèi)存中以“\0”結(jié)束,所占字節(jié)比實(shí)際多一個(gè)
-
字符串的初始化
- 在C語言中沒有專門的字符串變量,通常用一個(gè)字符數(shù)組來存放一個(gè)字符串辩越。
- 當(dāng)把一個(gè)字符串存入一個(gè)數(shù)組時(shí),會(huì)把結(jié)束符‘\0’存入數(shù)組,并以此作為該字符串是否結(jié)束的標(biāo)志。
- 有了‘\0’標(biāo)志后,就不必再用字符數(shù)組 的長(zhǎng)度來判斷字符串的長(zhǎng)度了
- 初始化
char name[9] = "lnj"; //在內(nèi)存中以“\0”結(jié)束信粮, \0ASCII碼值是0
char name1[9] = {'l','n','j','\0'};
char name2[9] = {'l','n','j',0};
// 當(dāng)數(shù)組元素個(gè)數(shù)大于存儲(chǔ)字符內(nèi)容時(shí), 未被初始化的部分默認(rèn)值是0, 所以下面也可以看做是一個(gè)字符串
char name3[9] = {'l','n','j'};
- 錯(cuò)誤的初始化方式
//省略元素個(gè)數(shù)時(shí), 不能省略末尾的\n
// 不正確地寫法黔攒,結(jié)尾沒有\(zhòng)0 ,只是普通的字符數(shù)組
char name4[] = {'l','n','j'};
// "中間不能包含\0", 因?yàn)閈0是字符串的結(jié)束標(biāo)志
// \0的作用:字符串結(jié)束的標(biāo)志
char name[] = "c\0ool";
printf("name = %s\n",name);
輸出結(jié)果: c
字符串輸出
- 如果字符數(shù)組中存儲(chǔ)的是一個(gè)字符串, 那么字符數(shù)組的輸入輸出將變得簡(jiǎn)單方便强缘。
- 不必使用循環(huán)語句逐個(gè)地輸入輸出每個(gè)字符
- 可以使用printf函數(shù)和scanf函數(shù)一次性輸出輸入一個(gè)字符數(shù)組中的字符串
- 使用的格式字符串為“%s”,表示輸入督惰、輸出的是一個(gè)字符串 字符串的輸出
-
輸出
- %s的本質(zhì)就是根據(jù)傳入的name的地址逐個(gè)去取數(shù)組中的元素然后輸出,直到遇到\0位置
char chs[] = "lnj";
printf("%s\n", chs);
- 注意點(diǎn):
- \0引發(fā)的臟讀問題
char name[] = {'c', 'o', 'o', 'l' , '\0'};
char name2[] = {'l', 'n', 'j'};
printf("name2 = %s\n", name2); // 輸出結(jié)果: lnjcool
- 輸入
char ch[10];
scanf("%s",ch);
- 注意點(diǎn):
- 對(duì)一個(gè)字符串?dāng)?shù)組, 如果不做初始化賦值, 必須指定數(shù)組長(zhǎng)度
- ch最多存放由9個(gè)字符構(gòu)成的字符串旅掂,其中最后一個(gè)字符的位置要留給字符串的結(jié)尾標(biāo)示‘\0’
- 當(dāng)用scanf函數(shù)輸入字符串時(shí),字符串中不能含有空格,否則將以空格作為串的結(jié)束符
字符串常用方法
- C語言中供了豐富的字符串處理函數(shù),大致可分為字符串的輸入赏胚、輸出、合并商虐、修改栅哀、比較、轉(zhuǎn) 換称龙、復(fù)制、搜索幾類戳晌。
- 使用這些函數(shù)可大大減輕編程的負(fù)擔(dān)鲫尊。
- 使用輸入輸出的字符串函數(shù),在使用前應(yīng)包含頭文件"stdio.h"
- 使用其它字符串函數(shù)則應(yīng)包含頭文件"string.h"
- 字符串輸出函數(shù):puts
- 格式: puts(字符數(shù)組名)
- 功能:把字符數(shù)組中的字符串輸出到顯示器。即在屏幕上顯示該字符串沦偎。
- 優(yōu)點(diǎn):
- 自動(dòng)換行
- 可以是數(shù)組的任意元素地址
- 缺點(diǎn)
- 不能自定義輸出格式, 例如 puts("hello %i");
char ch[] = "lnj";
puts(ch); //輸出結(jié)果: lnj
- puts函數(shù)完全可以由printf函數(shù)取代疫向。當(dāng)需要按一定格式輸出時(shí),通常使用printf函數(shù)
- 字符串輸入函數(shù):gets
- 格式: gets (字符數(shù)組名)
- 功能:從標(biāo)準(zhǔn)輸入設(shè)備鍵盤上輸入一個(gè)字符串咳蔚。
char ch[30];
gets(ch); // 輸入:lnj
puts(ch); // 輸出:lnj
- 可以看出當(dāng)輸入的字符串中含有空格時(shí),輸出仍為全部字符串。說明gets函數(shù)并不以空格作為字符串輸入結(jié)束的標(biāo)志,而只以回車作為輸入結(jié)束搔驼。這是與scanf函數(shù)不同的谈火。
- 注意gets很容易導(dǎo)致數(shù)組下標(biāo)越界,是一個(gè)不安全的字符串操作函數(shù)
- 字符串長(zhǎng)度
- 利用sizeof字符串長(zhǎng)度
- 因?yàn)樽址趦?nèi)存中是逐個(gè)字符存儲(chǔ)的,一個(gè)字符占用一個(gè)字節(jié),所以字符串的結(jié)束符長(zhǎng)度也是占用的內(nèi)存單元的字節(jié)數(shù)舌涨。
char name[] = "it666";
int size = sizeof(name);// 包含\0
printf("size = %d\n", size); //輸出結(jié)果:6
- 利用系統(tǒng)函數(shù)
- 格式: strlen(字符數(shù)組名)
- 功能:測(cè)字符串的實(shí)際長(zhǎng)度(不含字符串結(jié)束標(biāo)志‘\0’)并作為函數(shù)返回值糯耍。
char name[] = "it666";
size_t len = strlen(name2);
printf("len = %lu\n", len); //輸出結(jié)果:5
- 以“\0”為字符串結(jié)束條件進(jìn)行統(tǒng)計(jì)
/**
* 自定義方法計(jì)算字符串的長(zhǎng)度
* @param name 需要計(jì)算的字符串
* @return 不包含\0的長(zhǎng)度
*/
int myStrlen2(char str[])
{
// 1.定義變量保存字符串的長(zhǎng)度
int length = 0;
while (str[length] != '\0')
{
length++;//1 2 3 4
}
return length;
}
/**
* 自定義方法計(jì)算字符串的長(zhǎng)度
* @param name 需要計(jì)算的字符串
* @param count 字符串的總長(zhǎng)度
* @return 不包含\0的長(zhǎng)度
*/
int myStrlen(char str[], int count)
{
// 1.定義變量保存字符串的長(zhǎng)度
int length = 0;
// 2.通過遍歷取出字符串中的所有字符逐個(gè)比較
for (int i = 0; i < count; i++) {
// 3.判斷是否是字符串結(jié)尾
if (str[i] == '\0') {
return length;
}
length++;
}
return length;
}
- 字符串連接函數(shù):strcat
- 格式: strcat(字符數(shù)組名1,字符數(shù)組名2)
- 功能:把字符數(shù)組2中的字符串連接到字符數(shù)組1 中字符串的后面,并刪去字符串1后的串標(biāo)志 “\0”。本函數(shù)返回值是字符數(shù)組1的首地址囊嘉。
char oldStr[100] = "welcome to";
char newStr[20] = " lnj";
strcat(oldStr, newStr);
puts(oldStr); //輸出: welcome to lnj"
- 本程序把初始化賦值的字符數(shù)組與動(dòng)態(tài)賦值的字符串連接起來温技。要注意的是,字符數(shù)組1應(yīng)定義足 夠的長(zhǎng)度,否則不能全部裝入被連接的字符串。
- 字符串拷貝函數(shù):strcpy
- 格式: strcpy(字符數(shù)組名1,字符數(shù)組名2)
- 功能:把字符數(shù)組2中的字符串拷貝到字符數(shù)組1中扭粱。串結(jié)束標(biāo)志“\0”也一同拷貝舵鳞。字符數(shù)名2, 也可以是一個(gè)字符串常量。這時(shí)相當(dāng)于把一個(gè)字符串賦予一個(gè)字符數(shù)組琢蛤。
char oldStr[100] = "welcome to";
char newStr[50] = " lnj";
strcpy(oldStr, newStr);
puts(oldStr); // 輸出結(jié)果: lnj // 原有數(shù)據(jù)會(huì)被覆蓋
- 本函數(shù)要求字符數(shù)組1應(yīng)有足夠的長(zhǎng)度,否則不能全部裝入所拷貝的字符串蜓堕。
- 字符串比較函數(shù):strcmp
- 格式: strcmp(字符數(shù)組名1,字符數(shù)組名2)
- 功能:按照ASCII碼順序比較兩個(gè)數(shù)組中的字符串,并由函數(shù)返回值返回比較結(jié)果。
- 字符串1=字符串2,返回值=0;
- 字符串1>字符串2,返回值>0;
- 字符串1<字符串2,返回值<0博其。
char oldStr[100] = "0";
char newStr[50] = "1";
printf("%d", strcmp(oldStr, newStr)); //輸出結(jié)果:-1
char oldStr[100] = "1";
char newStr[50] = "1";
printf("%d", strcmp(oldStr, newStr)); //輸出結(jié)果:0
char oldStr[100] = "1";
char newStr[50] = "0";
printf("%d", strcmp(oldStr, newStr)); //輸出結(jié)果:1
練習(xí)
- 編寫一個(gè)函數(shù)char_contains(char str[],char key)套才, 如果字符串str中包含字符key則返回?cái)?shù)值1,否則返回?cái)?shù)值0
字符串?dāng)?shù)組基本概念
- 字符串?dāng)?shù)組其實(shí)就是定義一個(gè)數(shù)組保存所有的字符串
- 1.一維字符數(shù)組中存放一個(gè)字符串贺奠,比如一個(gè)名字char name[20] = “nj”
- 2.如果要存儲(chǔ)多個(gè)字符串霜旧,比如一個(gè)班所有學(xué)生的名字,則需要二維字符數(shù)組儡率,char names[15][20]可以存放15個(gè)學(xué)生的姓名(假設(shè)姓名不超過20字符)
- 如果要存儲(chǔ)兩個(gè)班的學(xué)生姓名挂据,那么可以用三維字符數(shù)組char names[2][15][20]
字符串?dāng)?shù)組的初始化
char names[2][10] = { {'l','n','j','\0'}, {'l','y','h','\0'} };
char names2[2][10] = { {"lnj"}, {"lyh"} };
char names3[2][10] = { "lnj", "lyh" };