C語言程式設計(八)
文章目錄
- C語言程式設計(八)
- 字元串
-
- 字元數組
- 字元串
- 字元串變量
- 字元串常量
- 用指針還是數組?
- char*是字元串?
- 字元串指派?
- 字元串輸入輸出
- 安全的輸入
- 常見錯誤
- 空字元串
- 字元串數組
-
- 附加:之前用switch-case寫的月份程式可以用數組來寫
- 程式參數
- 單字元輸入輸出
-
- putchar函數
- getchar函數
- 标準庫中的字元串函數
- string.h
-
- 字元串中找字元
字元串
字元數組
- char word[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’};
- 這不是C語言的字元串,因為不能用字元串的方式做計算
word[0] | H |
---|---|
word[1] | e |
word[2] | l |
word[3] | l |
word[4] | o |
word[5] | ! |
字元串
- char word[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’}; //這才是C語言的字元串
word[0] | H |
---|---|
word[1] | e |
word[2] | l |
word[3] | l |
word[4] | o |
word[5] | ! |
word[6] | \0 |
- 以 (整數0)結尾的一串字元
- 或
是一樣的,但是和 不同\0
- 或
- 0标志字元串的結束 ,但它不是字元串的一部分
- 計算字元串長度的時候不包含這個0
- 字元串以數組的形式存在,以數組或指針的形式通路
- 更多的是以指針的形式
-
裡有很多處理字元串的函數string.h
字元串變量
- char *str = “Hello”; //定義一個指針指向字元數組
- char word[] = “Hello”; //字元數組
- char line[10] = “Hello”; //位元組長度為10的字元數組; char長度是1位元組
字元串是數組; 這些變量都是字元數組的變量
- “Hello”
- ″Hello″ 會被編譯器變成一個字元數組放在某處,這個數組的長度是6,結尾還有表示結束的 。 {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};像這樣的字元數組;是以還需加一.
- 兩個相鄰的字元串常量會被自動連接配接起來
- 行末的
表示下一行還是這個字元串常量\
字元串
- C語言的字元串是以字元數組的形态存在的
- 不能用運算符對字元串做運算
- 通過數組的方式可以周遊字元串
- 唯一特殊的地方是字元串
(雙引号)字面量可以用來初始化字元數組" "
- 以及标準庫提供了一系列字元串函數
字元串常量
-
char* s = "Hello, world!";
- s 是一個指針,初始化為指向一個字元串常量
- 由于這個常量所在的地方,是以實際上s是
,但是由于曆史的原因,編譯器接受不帶const char* s
的寫法,const
- 但是試圖對s所指的字元串做寫入會導緻嚴重的後果
- 由于這個常量所在的地方,是以實際上s是
- 如果需要修改字元串,應該用數組:
char s[] = "Hello, world!";
#include <stdio.h>
int main()
{
int i = 0;
char *s1 = "Hello World";
char *s2 = "Hello World";
char s3[] = "Hello World";
//其中指針s1和s2指向同一個空間,也就是值相同; 而s3[]的數組是重新開辟一個空間,然後把字元數組的值複制進入
printf("&i=%p\n", &i);
printf("s1=%p\n", s1);
printf("s2=%p\n", s2);
printf("s3=%p\n", s3);
for(i=0; i<(sizeof(s3)/sizeof(s3[0])); i++){
printf("%c", s3[i]);
}//周遊輸出字元數組
printf("\n");
s3[0] = 'b'; //注意字元數組中的元素要用單引号,不能是雙引号
printf("s3[0]=%c\n", s3[0]);
for(i=0; i<(sizeof(s3)/sizeof(s3[0])); i++){
printf("%c", s3[i]);
}//周遊輸出字元數組
printf("\n");
return 0;
}
輸出結果:
&i=000000000062FE0C
s1=0000000000404000
s2=0000000000404000
s3=000000000062FE00
Hello World
s3[0]=b
bello World
注意:
char *s1 = "Hello World";
s1[0] = 'b';
//會直接報錯,因為s1是一個指針,初始化為指向一個字元串常量,而這個常量是不可改變的,但是如果用數組就可以用數組:char s1[] = "Hello, world!";而數組是一種特殊的指針, 它指向一個字元串數組的首位址,
字元用單引号, 字元串用雙引号
用指針還是數組?
-
char *str = “Hello”;
- char word[] = “Hello”;`
- 數組:這個字元串在這裡
- 作為本地變量空間自動被回收
- 指針:這個字元串不知道在哪裡
- 處理參數
- 動态配置設定空間
- 是以如果要構造一個字元串用數組; 如果要處理一個字元串用指針
char*是字元串?
- 字元串可以表達為
的形式;char*
- 但
不一定是字元串char*
- 本意是指向字元的指針,可能指向的是字元的數組(就像
一樣;int*
不一定是數組; 它可能指向單個int,也可能指向數組)int*
- 隻有它所指的字元數組有結尾的0,才能說它所指的是字元串。
比如:char* word = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};
- 本意是指向字元的指針,可能指向的是字元的數組(就像
字元串指派?
char *t = “title”;
char *s;
s = t;
- 并沒有産生新的字元串,隻是讓指針s指向了t所指的字元串,對s的任何操作就是對t做的
字元串輸入輸出
char string[8];
scanf(“%s”, string);
printf(“%s”, string);
#include <stdio.h>
int main()
{
char word[8];//可以放8個char類型,超過的話,字元數組會越界
char word2[8];
while(1){
scanf("%s", word);
scanf("%s", word2);
printf("%s##%s##\n", word, word2);
}
return 0;
}
輸出:
hello world
hello##world##
hello
world
hello##world##
helloworld
hello_world
helloworld##hello_world##
可以發現%s讀入一個單詞(到空格、tab或回車為止)
-
讀入一個單詞(到空格、tab或回車為止)scanf
-
是不安全的,因為不知道要讀入的内容的長度scanf
安全的輸入
char string[8];
scanf(“%7s”, string);
- 在%和s之間的數字表示最多允許讀入的字元的數量,這個數字應該比數組的大小小一; 最後一個字元要放0.
- 下一次scanf從哪裡開始?根據個數來讀内容而不是空格或回車符.
#include <stdio.h>
int main()
{
char word[8];//可以放8個char類型,超過的話,字元數組會越界
char word2[8];
while(1){
scanf("%7s", word);//可以放8個char類型,但隻能限制輸入7個字元,最後一個字元要放0
scanf("%7s", word2);
printf("%s##%s##\n", word, word2);
printf("\n");
}
return 0;
}
helloworld
hellowo##rld##
1234
123456
1234##123456##
12345678
1234567##8##
常見錯誤
char *string;
scanf(“%s”, string);
- 以為char*是字元串類型,定義了一個字元串類型的變量string就可以直接使用了
- 由于沒有對string初始化為0,是以不一定每次運作都出錯
空字元串
-
char buffer[100]=””;
- 這是一個空的字元串,
buffer[0] == ‘\0’
- 這是一個空的字元串,
-
char buffer[] = “”;
- 這個數組的長度隻有1!
字元串數組
-
char **a
- a是一個指針,指向另一個指針,那個指針指向一個字元(串)
-
char a[][]
- a是一個二維數組,第二個次元的大小不知道,不能編譯,是以二維數組必須要有第二個次元的大小;
-
char a[][10]
- a是一個二維數組,a[x]是一個char[10]; 是指每個a[]單元最多有10個字元元素
-
char *a[]
- a是一個一維數組,a[x]是一個char*; 是一個指針
#include <stdio.h>
int main()
{
char a[][10]={
"Hello",
"World",
"1234567890"
}; //類似于數組的元素集合 ,因為是集合而不是塊,是以得用分号;
return 0;
}
這樣編譯會出錯,因為"1234567890"超過了9個char長度的大小,
char a[][10]
是指每一行都不超過9個字元元素,(因為C語言中是字元串後面是
\0
結尾占據一個空間,好比顧頭不顧腚)。
But:
#include <stdio.h>
int main()
{
char *a[]={
"Hello",
"World",
"1234567890"
}; //類似于數組的元素集合 ,因為是集合而不是塊,是以得用分号;
return 0;
}
這樣寫就沒問題, 他們的差別是:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiNx8FesU2cfdGLwczX0xiRGZkRGZ0Xy9GbvNGLwIzXlpXazxSP9EVZ1ZVblFkTxEVQClGVF5UMR9Fd4VGdsATNfd3bkFGazxycykFaKdkYzZUbapXNXlleSdVY2pESa9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLlVzN3ImZzkTO4YWO4QmM4MmNlRTNlJDO5IDM5IzM3gzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
附加:之前用switch-case寫的月份程式可以用數組來寫
-
用char month[][20]
#include <stdio.h>
int main()
{
char month[][20]= {
"null",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
};
int i;
while (1) {
scanf("%d",&i);
printf("%s\n",month[i]);
}
}
用
char *month[]
#include <stdio.h>
int main()
{
char *month[] = {
"null",
"January",
"February",
'March',
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
};
int i;
printf("請輸入月份:");
scanf("%d",&i);
printf("%s\n", month[i]);
}
程式參數
-
//括号裡的參數(一個整數 一個字元串數組); 前面的整數是說明字元串數組有多大int main(int argc, char const *argv[])
-
是指令本身argv[0]
- 當使用Unix的符号連結時,反映符号連結的名字
#include <stdio.h>
int main(int argc, char const*argv[])
{
int i;
for(i=0; i<argc; i++){
printf("%d:%s\n",i, argv);
}//周遊輸出數組
return 0;
}
- 得在Unix上使用;windows上不能直接運作出結果
單字元輸入輸出
putchar函數
-
//雖然是傳入一個int類型參數; 但是它int型能接收的隻是一個字元charint putchar(int c);
- 向标準輸出寫一個字元(每一次隻寫入一個字元) put char:輸出字元
- 傳回寫了幾個字元,
表示寫失敗EOF(-1)
每一次隻寫一個字元的意思是,每次
getchar函數
-
//沒有參數int getchar(void);
- 從标準輸入讀入一個字元 get char 讀入字元
- 傳回類型是int是為了傳回
EOF(-1)
-
Windows—>Ctrl-Z
-
Unix—>Ctrl-D
-
#include <stdio.h>
int main(int argc, char const*argv[])
{
int ch;
while( (ch = getchar()) != EOF){
putchar(ch);
}
printf("EOF\n");
return 0;
}
輸出:
1234
1234
12656
12656
ch
ch
EOF
EOF
^Z
EOF
在windows系統中
Ctrl + Z
再加回車會傳回
EOF
; 在
Unix上用Ctrl + D
其他的輸出什麼就傳回什麼.
标準庫中的字元串函數
- C的标準庫中的函數
- 頭檔案:
string.h
- 使用這些函數時得輸入
#include <string.h>
string.h
-
函數strlen
-
//傳入的字元串不會修改 (const)size_t strlen(const char *s);
- 傳回s的字元串長度(不包括結尾的0); 就是專門用來輸出數組字元串的長度, 與
有差別; (sizeof
是直接傳回占用記憶體空間的位元組長度大小; 而sizeof
呢是直接傳回字元串的個數)strlen
- strlen函數的作用是它會告訴你字元串的長度
-
#include <stdio.h>
#include <string.h>
int main(int argc, char const*argv[])
{
char line[] = "Hello";
printf("strlen=%lu\n",strlen(line));
printf("sizeof=%lu\n",sizeof(line));
return 0;
}
輸出:
strlen=5
sizeof=6
之是以sizeof=6, 是因為字元串數組中含有
/0
, 而這個不會被strlen函數記進去。
仿寫
strlen
函數
#include <stdio.h>
//#include <string.h>
size_t mylen(const char* s)
{
int idx = 0;
while (s[idx]!='\0'){
idx++;
}
return idx;
} //仿照strlen函數
int main(int argc, char const*argv[])
{
char line[] = "Hello";
// printf("strlen=%lu\n",strlen(line));
printf("sizeof=%lu\n",sizeof(line));
printf("mylen =%lu\n", mylen(line));
return 0;
}
輸出:
sizeof=6
mylen =5
其中這自己定義的mylen函數與标準庫裡的strlen函數功能一樣。
-
函數strcmp
-
int strcmp(const char *s1, const char *s2);
-
函數的作用是:比較兩個字元串,傳回:strcmp
-
0:s1==s2
-
1:s1>s2
-
-1:s1<s2
-
#include <stdio.h>
#include <string.h>
int main()
{
char s1[] = "abc";
char s2[] = "abc";
printf("%d\n", strcmp(s1, s2));//字元串相同,則傳回0
printf("%d\n", 'a'-'b');
char s3[] = "abc";
char s4[] = "ABc";
printf("%d\n", strcmp(s3, s4)); //s3和s4不同, s3>s4
printf("%d\n", 'a'-'A');
return 0;
}
輸出:
0
-1
1
32
仿寫
strcmp
函數
#include <stdio.h>
#include <string.h>
int mycmp(const char *a, const char *b)
{
int c;
while (*a == *b && *a != '\0'){
a++;
b++;
}
if (*a-*b > 0){
c = 1;
}else if(*a-*b <0){
c = -1;
}else{
c = 0;
}
return c;
}
int main(int argc, char const *argv[])
{
char s1[] = "abc";
char s2[] = "abc";
printf("%d\n", mycmp(s1, s2));
printf("%d\n", 'a'-'b');
}
輸出:
0
-1
-
函數strcpy
-
char * strcpy(char *restrict dst, const char *restrict src);
- 把
的字元串拷貝到src
//注意是将後面的參數拷貝到前面dst
-
表明restrict
和src
不重疊dst
(only C99)
-
- 傳回
dst
- 為了能鍊起代碼來
- 複制一個字元串
- 仿寫一個
函數strcpy
-
char *dst = (char*)malloc(strlen(src)+1);//先動态申請記憶體空間, , 字元串的長度要+1, 因為要包括最後的'\0'
strcpy(dst, src);
舉個栗子:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char s1[] = "abc";
char *s2 = (char*) malloc(strlen(s1)+1);//注意配置設定的記憶體大小要加1
strcpy(s2, s1); //将s1拷貝到s2,
int i;
for(i=0; i<strlen(s2); i++){
printf("%c", s2[i]);
}
return 0;
}
輸出:
abc
也可以這樣, 直接相同大小空間直接替換過去
#include <stdio.h>
#include <string.h>
int main()
{
char s1[] = "abc";
char s2[] = "ABC";
strcpy(s2, s1); //将s1拷貝到s2,
// int i;
// for(i=0; i<strlen(s2); i++){
// printf("%c", s2[i]);
// }
//可以不必要用for周遊輸出字元串數組
//直接%s就可以輸出字元串數組
printf("%s\n", s2);
return 0;
}
輸出:
abc
仿寫
strcpy
函數(隻寫部分代碼)
//第一種:
char* mycpy(char* dst, const char* src)
{
int idx = 0;
while (src[idx]){
dst[idx] = src[idx];
idx++;
}
dst[idx] = '\0';
return 0;
}//用數組定義的mycpy函數
//第二種:
char* mycpy(char* dst, const char* src)
{
char* ret = dst;//先保留起始位址
while (dst++ = src++);
dst[idx] = '\0';
return ret;
}//用指針定義的mycpy函數
-
函數strcat
-
字元串——C語言程式設計(八)C語言程式設計(八)字元串 -
char * strcat(char *restrict s1, const char *restrict s2);
- 把s2拷貝到s1的後面,接成一個長的字元串 (也就是連接配接起來)
- 傳回s1
- s1必須具有足夠的空間
-
- 安全問題
-
和strcpy
都可能出現安全問題, 可能造成數組越界, 超出存儲範圍, 就會有可能占到别的資料存放的地方, 這樣會導緻其他資料不安全,容易丢失。strcat
- 如果目的地沒有足夠的空間?
- 是以建議是盡量少用或者不用
和strcpy
strcat
-
- 安全版本(多了個n; 即最多能拷貝過去n的範圍大小)
-
char * strncpy(char *restrict dst, const char *restrict src, size_t n);
-
char * strncat(char *restrict s1, const char *restrict s2, size_t n);
-
//這裡的n是隻是讓它比較到第n個int strncmp(const char *s1, const char *s2, size_t n);
-
字元串中找字元
- strchr
-
//在s中,從左邊尋找char * strchr(const char *s, int c);
第一次出現的位置, 并傳回指針c
-
//在s中,從右邊尋找char * strrchr(const char *s, int c);
第一次出現的位置, 并傳回指針c
- 它們都是尋找單個字元的
- 傳回NULL表示沒有找到
-
舉個栗子: 一個數組字元串
hello
,找
l
的後半段
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "hello";
char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元
printf("%s\n", p); //傳回的指針賦給指針p, 從指針p所指的位址開始輸出字元串 ,是以不需要用*p ,與之前的指針指向某個變量的值不一樣, 這個指針相當于數組頭指針, 它指向的是一連串的數組空間
return 0;
}
輸出:
llo
怎樣找第二個
l
呢 ?
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "hello";
char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元
//尋找第二個l開始
p = strchr(p+1, 'l');
printf("%s\n", p);
}
輸出:
lo
也可以将找到的那部分拷貝出來
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char s[] = "hello";
char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元
char *t = (char*)malloc(strlen(p)+1);
strcpy(t, p);//将p的字元串拷貝到t中
printf("%s\n", t);
free(t);
return 0;
}
輸出:
llo
如果是要
l
的前一段呢?
這裡要用到一些小技巧
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char s[] = "hello";
char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元
char c = *p;//先儲存p所指的空間上存放的值
*p = '\0'; //再将p所指空間上的值改為結束時的'\0'
printf("%s\n", s);//這時輸出就隻有'l'的前部分
char *t = (char*)malloc(strlen(s)+1);
strcpy(t, s);//将p的字元串拷貝到t中
printf("%s\n", t);//這時輸出'l' 前面的部分
free(t);
*p = c; //将p所指的那個空間的原來的值恢複回來
printf("%s\n", s);//這時就是原來的值了
return 0;
}
輸出:
he
he
hello
附注: 要輸出字元串數組得用
%s
, (
%c
輸出不了,除非用數組周遊才可以,那就相對比較麻煩)
- strstr
- 是字元串中尋找一個字元串的
-
char * strstr(const char *s1, const char *s2);
-
char * strcasestr(const char *s1, const char *s2);
- 傳回NULL表示沒有找到
- …省略說明…