C語言03- 數組递沪、字符串

C語言03- 數組、字符串

10:數組

數組每個元素的類型相同综液,因此各個元素在內存中存放的長度也一樣款慨,即它們占用的空間是等長的,而數組名谬莹,就是這段內存的首地址檩奠。

數組是連續(xù)存儲桩了,所以它支持隨機訪問.

10.1:一維數組

image
下面關于數組的一些定義,有的是錯誤的:
int x = 10;
int a[x]; //錯誤笆凌,x是變量而不是常量

const int x1 = 10;
int a1[x1];//正確圣猎,x現在是常變量
int a2[10];//正確,10是整數常量

一些常見的一維數組定義:
int a[100];
char a[100];
int *a[10];//每個元素是一個指針
     a[i]:數組元素值
     &a[i]:第i個元素的地址
     &a[0]:首個元素的地址
    a乞而,首地址
    a5+1
    &a[0]+1

sizeof是用來計算類型和數據的長度送悔。strlen是用來計算字符串中非‘\0’的字符個數的。

  1. sizeof(a):數組的長度計算
  2. sizeof(a[0]):數組中一個元素的 長度
  3. n=sizeof(a)/sizeof(a[0]):數組元素個數
  4. #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))

10.2:二維數組

二維數組可以理解為數學中的矩陣的一個類似概念爪模。二維數組定義的一般形式為:

類型說明符 數組名[常量表達式1][常量表達式2]

二維數組的初始化可以為下面的形式:

int a[5][3]={ {80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85} };

對于二維數組的遍歷欠啤,可以先列后行(即先遍歷列,再遍歷行)屋灌,也可以先行后列(即先遍歷行洁段,再遍歷列)進行。比如共郭,對于一個m行n列的整型二維數組a[m][n]進行遍歷:
//先行后列:

for(int i = 0; i < m;i++) {
         for(int j = 0; j < n; j++) {
              printf(“%d/n”, a[i][j]);
         }
}

//輸出為:a[0][0] a[0][1] a[0][2] a[0][3]…a[0][9]...

//先列后行:
for(int i = 0; i < n;i++) {
         for(int j = 0; j < m; j++) {
                   printf(“%d/n”, a[j][i]);
         }

}
//輸出為:a[0][0] a[1][0] a[2][0] a[3][0] a[4][0]

由于數組是按行的順序存放的祠丝,同一列的數據就有可能存放在不同的頁中,那么先列后行的訪問將引起更多的缺頁中斷除嘹,降低了遍歷的效率台颠。

10.3:數組重要注意事項

int a1[10];
int a2[4][5];
//a1,&a1,a2,&a2都是數組的首地址迹卢,值相同擅腰,但是類型不同毕匀。
//類型為什么不一樣呢?
//a1:int * -
//&a1:int (*a1)[10] -包含十個元素的一維數組的指針類型
//a2:int (*a2)[5] -數組指針年缎,指向含有5個元素的數組悔捶,代表一行
//&a2:int (*a2)[4][5] -數組指針,指向還有4行5列的一個數組

--------------------------------------------

   int a[3] = {0,1,2};
   
   printf("a:%p\n", a);//a是int *類型单芜;0x7ffee02f693c
   printf("&a:%p\n", &a);//&a是int (*a)[3]類型蜕该;0x7ffee02f693c
   printf("&a[0]:%p\n", &a[0]);//首個元素的地址;0x7ffee02f693c
   //打印輸出a, &a, &a[0]值一樣缓溅;如地址0x7ffee02f693c
   
   //sizeof判斷數據類型長度符的關鍵字;其作用就是返回一個對象或者類型所占的內存字節(jié)數
   /*
    指針變量的sizeof
    學過數據結構的你應該知道指針是一個很重要的概念蛇损,它記錄了另一個對象的地址。既然是來存放地址的淤齐,那么它當然等于計算機內部地址總線的寬度。所以在32位計算機中袜匿,一個指針變量的返回值必定是4(注意結果是以字節(jié)為單位)更啄,但是,在64位系統(tǒng)中指針變量的sizeof結果為8居灯。
    */
   printf("sizeof(a):%lu\n", sizeof(a));//數組的sizeof值等于數組所占用的內存字節(jié)數
   printf("sizeof(&a):%lu\n", sizeof(&a));//
   printf("sizeof(&a[0]):%lu\n", sizeof(&a[0]));//
//    sizeof(a):12
//    sizeof(&a):8 //64位系統(tǒng)
//    sizeof(&a[0]):8 //64位系統(tǒng)
   
   printf("a:%p\n", a + 1);//a是int *類型祭务,0x7ffee2d5c940 加一是其類型加一
   printf("&a:%p\n", &a + 1);//&a是int (*a)[3]類型内狗,0x7ffee2d5c948
   printf("&a[0]:%p\n", &a[0] + 1);//首個元素的地址,0x7ffee2d5c940
   
   ---------------------------
   一般地:
   int a[m1][m2]...[mn]
   a, &a, &a[0]...[0]
   a+1//a的類型int (*a)[m2]...[mn],一行
   &a+1//&a的類型int (*a)[m1]...[mn]
    &a[0]...[0]//代表的第一個元素的類型int *

int a[5] = {1,2,3,4,5};
int *ptr1 = (int *)(&a+1);
int *ptr2 = (int *)((int)a+1);
printf("%x, %x", ptr1[-1], *ptr2) 
//打印0x5(0x00000005), 0x02000000;
/*
ptr1[-1]:ptr1類型是int *义锥,ptr1[-1]相當于ptr1-1既ptr1減去int *類型長度4的地址柳沙。
*/
內存存儲示例.png

10.3.1:數組做參數傳遞給函數,在函數內部退化為指針

void func(int a[], size_t len) {//a是數組名拌倍,len是數組元素個數赂鲤。數組做函數參數,一般都這么傳遞
    printf(“sizeof(a) in func=%d\n”, sizeof(a));
}

int a[10] = {0};
printf(“sizeof(a)=%d\n”, sizeof(a));

func(a, 10);
/*
執(zhí)行上面的代碼柱恤,你會發(fā)現輸出的兩個結果為:
sizeof(a) = 40
sizeof(a) in func=4

也就是說数初,如果數組做了函數的參數,那么在函數內部梗顺,數組就變?yōu)榱酥羔樑莺ⅰ嶋H上,數組是一個常量指針寺谤。比如:
int a[10];
a的類型為:int *const a;//a is a const pointer to int;
*/

10.3.2:數組溢出與預防

會造成程序的異陈嘏福現象;C語言編譯器對數組溢出不做檢測变屁,

image
  • 學了函數之后會發(fā)現程序1锈候,無法退出,i=16之后敞贡,又會重新被置為0。
  • 程序2摄职,a[9]不存在誊役,越界溢出

11:字符串

字符串是編程語言中表示文本的數據類型。

11.1:字符串定義

字符串是由零個或多個字符組成的有限序列谷市。C語言字符串可以定義為:”c1c2c3c4..cn\0”蛔垢。

C語言的字符串是以’\0’結尾的

程序在存放字符串的時候迫悠,會自動在字符后面加上一個’\0’作為結尾鹏漆。

11.1.1:轉義字符

C語言中常見的轉義字符如下:


image

11.2:程序中的字符串

  1. "a":字符串,存儲在靜態(tài)區(qū)创泄。實際上是2個字符艺玲,末尾包好'\0'。使用方法char *str = "a";也就是可以賦值給字符指針鞠抑,這個時候str指向"a"的首地址饭聚。
  2. 'a':字符常量,值類型搁拙,不存儲在靜態(tài)區(qū)秒梳,無內存法绵。使用char ch='a';賦值給ch變量,ch存放在哪里酪碘,'a'就存放在哪里朋譬。不能使用char *p='a';因為'a'對應的類型是char。

并不是所有的常量都會被編譯器放在常量區(qū)的兴垦,編譯器認為普通的整型徙赢、浮點型或字符型常量在使用的時候是可以通過立即數來實現的,沒有必要額外存儲到數據區(qū)滑进,如此節(jié)省了存儲空間和運行時的訪問時間犀忱。

常量區(qū)里存放的是一些不可改變的量,比如字符串常量扶关。在實際的ELF(Executable and Linkable Format阴汇,可執(zhí)行連接格式,是UNIX系統(tǒng)實驗室(USL)作為應用程序二進制接口(Application Binary Interface节槐,ABI)而開發(fā)和發(fā)布的搀庶。Linux作為類unix系統(tǒng)其程序仍然沿用了該格式。)的程序數據是分段存儲的铜异,對應的常量區(qū)就是“.rodata(只讀數據)段“哥倔。

常量區(qū)的數據被標記為只讀,也就是程序只有訪問權而沒有寫入權揍庄,因此如果開發(fā)者需要使用某些不希望被改變的數據時可以將其放入常量區(qū)咆蒿。

在C語言中常量有很多種,比如常見的:
字符常量:‘a’蚂子, ’A’沃测, ’*’。
字符串常量:”helloworld”食茎,”ilovechina”蒂破,”12345”。
整常量: 25别渔,10附迷,012,0x0a哎媚,0b00001010喇伯。
浮點常量: 3.14,123.456抄伍, 3.0E-23艘刚;

11.2.1:多字節(jié)字符串與寬字符字符串

  1. char表示一個ASCII字符占用1個字節(jié),char類型字符串以”\0”結尾截珍。
  2. wchar_t表示一個UNICODE字符占用2個或4個字節(jié)攀甚,wchar_t類型字符串以”\0\0”結尾箩朴。

多字節(jié)字符串

在多字節(jié)字符串中,每個字符的編碼寬度都不等秋度,可以是一個字節(jié)炸庞,也可以是多個字節(jié)。比如:
char *str = “Hello, world!您好荚斯,世界埠居!”。
上面是一個多字節(jié)字符串事期。其中的英文字符占一個字節(jié)滥壕,而中文字符占2個字節(jié)。

寬字符字符串

而寬字符串中兽泣,每個字母占用的字節(jié)數是一樣的绎橘。比如:
wchar_t *wstr = L“Hello, world!您好,世界唠倦!”
上面是一個寬字符串称鳞,每個字符,無論英文字母還是中文字符稠鼻,都占2個字節(jié)冈止。
scanf_s("%ws", s, 100)//不能有空格,有空格打斷
fgetws(s, 100, stdin)
fputws(s, stdout)

用wctomb()等函數將寬字符串與多字節(jié)串進行相互轉換

11.2.2:使用方式

字符串常量可以賦值給一個字符指針或者一個字符數組候齿,比如:

  1. char *str = “this is a string”;
  2. char str2[]= “this is a string”;
  3. char str3[100] = “this is a string”;

語句1將”this is a string”賦值給了字符指針str熙暴。此時,str的值為”this is a string”的第一個字符的地址慌盯≡惯洌”this is a string”這個常量字符串存儲在內存常量區(qū)。而str即指向了存儲這個常量字符串 的首地址润匙。

語句2會將常量區(qū)中的”this is a string”拷貝到數組里面。并且數組的長度將為”this is a string” (包含’\0’)的長度唉匾。

語句3會將常量區(qū)中的”this is a string”拷貝到數組里面孕讳。并且數組的長度將為100個字節(jié)。語句3和語句2的區(qū)別是語句2沒有指明數組的長度巍膘,那么數組的長度就是字符串的長度厂财。

sizeof(str);//為指針的長度,所以在X86上是4峡懈,在X64上是8璃饱。
sizeof(str2)=17;//str2數組的長度,但str2沒有顯示指出數組的長度肪康,而是按照分配給它的字符串的長度來分配荚恶。所以撩穿,值為17。
sizeof(str3)=100;//sizeof計算的是str3數組的長度谒撼,所以結果為100食寡。
strlen(str)=16;//strlen計算的是字符串的字符個數(不包含’\0’)。
strlen(str2)=16;//原因同上廓潜。
strlen(str3)=16;//原因同上抵皱。

//把字符串存放在動態(tài)分配的內存空間中
char *p = (char *)malloc(100);

if (p == NULL)
       return; 

memset(p, 0, 100);
strcpy(p, “hello world”);//以p為首地址的內存中存這個字符串。

1辩蛋,while循環(huán)遍歷

char *str=“hello world!”;

while(*str!=‘\0’) {
    printf(“%c”, *str);
    str++;
}

11.3:字符串API

  1. strlen:計算字符串中字符個數(不含結尾字符’\0’)呻畸;
  2. strstr:查找字符串中子串的位置
  3. strcmp/stricmp:比較2個字符串是否相等,stricmp()多了個i(ignore)比較時忽略大小寫
  4. strchr/strrchr:strchr從左邊開始在字符串中查找某個字符的位置悼院,strrchr()右邊開始
  5. strcpy/strcpy_s:strcpy將字符串復制到目標緩存,但不檢測目標緩存的長度是否大于字符串的長度伤为,無論是否超過,都照拷不誤;在Windows平臺推出了新的安全拷貝函數strcpy_s()樱蛤。strcpy_s將檢測字符串的長度是否超過了目標緩存的大小钮呀,如果超過,則拒絕拷貝并返回失敗昨凡。
  6. strcat/strcat_s:用于字符串的拼接爽醋,并不檢測目標緩沖的大小是否能夠容納下拼接之后的字符串,因此也容易造成緩沖區(qū)溢出漏洞便脊。strcat_s()避免了這個問題蚂四。
  7. strtok/strtok_s: 將字符串s拆分為為一組字符串,delim為分隔符哪痰。
image.png

11.4:自己實現字符串API

size_t _strlen(const char *str) {
    size_t count = 0;

    while(*str++!= '\0') {
        count++;
    }
    
    return count;
} 

char *_strcpy(char *dst, char *src) {
    char *s = dst;//如果不用臨時指針遂赠,最后dst指向尾部
    while(*s++ = *src);
    
    return dst;
}

/*
int strcmp(char *s1,char * s2),它的功能:比較字符串s1和s2:
當s1<s2時晌杰,返回值<0
當s1=s2時跷睦,返回值=0
當s1>s2時,返回值>0
*/
int _strcmp(const char *s1, const char *s2) {
    assert(s1!==NULL && s2!=NULL)
    
    while(*s1 && *s2 && (*s1==*s2)) {
        s1++;
        s2++;
    }
    
    return *s1-*s2
}

/*
strstr()的原型為:char *strstr(char *s1, char *s2)
功能是從字符串s1中尋找s2第一次出現的位置(不比較結束符NULL)肋演。
*/
char *_strstr(char *s1, char *s2) {
    if (s1== NULL || s2==NULL)
        return NULL;
    
    if (!*s2)
        return ((char *)s1);
    
    char *s3, *s4;
       
    char *p = (char *)s1;
    while(*p) {
        s3 = p;
        s4 = s2;
        
        while(*s3 && *s4 && !(*s3-*s4))//*s3-*s4相等為0
            s3++, s4++;
        
        if(!*s4)//上面while在相等之后抑诸,s4指針到達尾部
            return p;
        
        p++;
    } 
    
    return NULL;
}

/*
strtok()的原型為:char *strtok(char *s, char *delim)
功能為:分解字符串為一組字符串。s為要分解的字符串爹殊,delim為分隔符字符串蜕乡。
*/
//由于此函數的功能比較難理解,在給出實現算法前先看看它的具體使用例子:
char s[] = "Nice to meet you!";
char *d = " ";
char *p = NULL;
    
p = strtok(s, d);
    
while(p) {
    printf("%s\n",p);//”Nice” “to” “meet” “you!”
    p = strtok(NULL,d);
}
//實現暫略

/*tolower()的原型:char tolower(char ch)
功能為:將大寫字母轉換為小寫字母
*/
char _tolower(char ch) {
    if(ch>=a && ch<=z) {
        return ch;
    }
    
    if(ch>=A && ch=Z) {
        return (ch + 'a' - 'A');
    }
}

//刪除特定字符
void deleteChar(char *str, char c) {
    assert(str != NULL);
    char *tmp = NULL;
    
    while(*str != '\0') {
        if(*str != c) {
            *tmp++ = *str;
        }
        
        *str++; 
    }
}

void deleteChar(char *str, char c) {
    assert(str != NULL);
    int iDes = 0, iSrc = 0;
    
    do {
        if (str[iSrc] != c)
            str[iDes++] = str[iSrc];
    } while(str[iSrc++] != '\0');
}

//刪除特定字符組void deleteChars(char *str, char chr[], int n)
//思路從字符串str中尋找chr第一次出現的位置,然后跑步指針賦值移位梗夸。

//逆置字符串
void reverseString(char *str) {
     char c;
     int n = strlen(str);

     for (int i = 0; i < n/2; i++) {
         c = str[i];
         str[i] = str[n-1-i];
         str[n-1-i] = c;
     }
}



?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末层玲,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌辛块,老刑警劉巖畔派,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異憨降,居然都是意外死亡父虑,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門授药,熙熙樓的掌柜王于貴愁眉苦臉地迎上來士嚎,“玉大人,你說我怎么就攤上這事悔叽±绸茫” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵娇澎,是天一觀的道長笨蚁。 經常有香客問我,道長趟庄,這世上最難降的妖魔是什么括细? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮戚啥,結果婚禮上奋单,老公的妹妹穿的比我還像新娘。我一直安慰自己猫十,他們只是感情好览濒,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拖云,像睡著了一般贷笛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宙项,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天乏苦,我揣著相機與錄音,去河邊找鬼尤筐。 笑死邑贴,一個胖子當著我的面吹牛,可吹牛的內容都是我干的叔磷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奖磁,長吁一口氣:“原來是場噩夢啊……” “哼改基!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秕狰,失蹤者是張志新(化名)和其女友劉穎稠腊,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體鸣哀,經...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡架忌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了我衬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叹放。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖挠羔,靈堂內的尸體忽然破棺而出井仰,到底是詐尸還是另有隱情,我是刑警寧澤破加,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布俱恶,位于F島的核電站,受9級特大地震影響范舀,放射性物質發(fā)生泄漏合是。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一锭环、第九天 我趴在偏房一處隱蔽的房頂上張望聪全。 院中可真熱鬧,春花似錦田藐、人聲如沸荔烧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹤竭。三九已至,卻和暖如春景醇,著一層夾襖步出監(jiān)牢的瞬間臀稚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工三痰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吧寺,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓散劫,卻偏偏與公主長得像稚机,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子获搏,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內容