字元串和字元串函數
-
- 字元串的定義
- 字元串的輸入
-
- 1. 配置設定空間
- 2. 讀入字元串:gets(); fgets(); scanf();
-
- 2-1 gets(); / puts();:no-safe!
- 2-2 fgets(); / fputs();
- 2-3 gets_s(char str[], int len);
- 2-4 scanf("%s", str);
- 2-5 用getchar(); / putchar(); 自定義輸入輸出函數
- 字元串函數
- 練習題:
字元串的定義
- 字元串常量(string constant)(或說 字元串字面量(string literal))是靜态存儲類别(static storage class),如果函數中使用字元串常量,該字元串常量隻會被存儲一次,且在整個程式的生命周期記憶體在
- 把字元串看作指針:雙引号括起來的内容視為指向該字元串存儲位置的指針
:printf("%s %p %c", "str1", "str2", *"str3");
直接列印出字元串%s
,str1
列印出字元串%p
的首位址,str2
列印出%c
指向的内容,字元 ‘s’* "str3"
char * s = "str";
- 字元串的聲明:
或char str1[40] = "This is a string";
注意第二種必須加’\0’否則就是普通的字元數組,而不是一個字元串,也可以char str1[10] = " 's' , 't' , 'r' , '\0' ";
自動确定長度(隻能在初始化時,否則必須用常量定義長度)char s[] = "This is a string";
- 字元串的數組形式與指針形式的差別:
&char s[];
char * s;
- 差別1:數組形式數組名不可以++,指針形式可以
數組形式中: s數組名,是位址常量,不能更改,否則就改變了數組的存儲位置,可以用
s + 1
這樣的操作,但不允許
++s
,遞增運算符隻能用于變量名前(可修改的左值),不能用于常量,見上一章練習題3
char s1[] = "Hello World";
++s1;
[Error] lvalue required as increment operand
而指針形式:會額外為指針變量留出一個存儲位置,可以對指針++
總之:數組名是常量(右值),指針是變量(左值),數組名可以指派給指針,反之不行
- 差別2:數組可以修改字元串内容,而指針形式不可
字元串字面量:如
"Hello World"
被視為const資料
指針形式:初始化時隻是将其位址賦給指針,相等于指針指向的是const型字元串,不可對齊修改(是一種未定義行為:編譯器可能會将是以一樣字面量的字元串存在同一位置(母體),指針可以修改的化,一下把所有的都改了)
char * s2 = "Byebye World";
s2[2] = '3';
[Warning] deprecated conversion from string constant to 'char*' [-Wwrite-strings]
建議使用: const char * s = "str";
來用指針初始化
而數組形式:初始化時将整個字元串從靜态存儲區拷貝到數組中(首位址改變),可以對其修改
靜态資料使用的記憶體 (str) 與 動态記憶體(數組s) 區域不同
#include <stdio.h>
#define str "Hello World"
int main(){
char s[] = str;
const char * ptr = str;
printf("add_str = %p;\nadd_ptr = %p;\nadd_s = %p;", str, ptr, s);
return 0;
}
輸出結果:
add_str = 0000000000404000;
add_ptr = 0000000000404000;
add_s = 000000000062FE30;
- 字元串數組:數組形式和指針形式
指針形式:
數組形式:
同樣的:
- 差別1:指針形式指向母體,不可修改,數組形式是拷貝來的副本,可以修改
- 差別2:指針形式是存了
個num_str
的數組,大小是char*指針
,而數組形式是存了num_str * sizeof(char *)
個字元串的數組,大小是num_str
num_str * max_str_len
指針形式:效率高;
數組形式:可更改
字元串的輸入
1. 配置設定空間
char * s;
scanf("%s", s);
這樣是不安全的,未提前指定字元串長度,可能會擦除掉其他資料
【注意】聲明數組将配置設定儲存資料的空間,而聲明指針隻配置設定一個位址的空間
正确的配置設定方法有:
1) 顯示聲明數組大小
cahr s[100];
2) 庫函數
char * s = (char *) malloc(len);
2. 讀入字元串:gets(); fgets(); scanf();
2-1 gets(); / puts();:no-safe!
code.
char s[10];
gets(s);
puts("s is :");
puts(s);
gets();:讀取整行,丢棄換行符,自動添’\0’
puts();:輸出字元串(從傳入的指針位置開始,到空字元結束),并添加換行符
gets()不安全!因為gets()隻有一個參數s,即數組首位址,如果一行過長,可能導緻緩沖區溢出(buffer overflow),即段錯誤(segmentation fault),在C11中廢除了gets();
可以用
fgets();
或 C11中新增的
gets_s();
代替
gets();
2-2 fgets(); / fputs();
一般用于處理檔案(file)輸入
- 與
/gets();
差別:puts();
/fgets();
不處理換行符,直接讀入讀出fputs();
char s[SIZE];
fgets(s, SIZE, stdin);
fputs(s, SIZE, stdout);
tips. 第2個參數是讀入字元串大小(包含’\0’),超出部分不讀入
tips. 第3個參數指明“檔案”, stdin是标準輸入(鍵盤),stdout是标準輸出(顯示器)
fgets();
傳回指向char的指針,但讀到檔案結尾時傳回空指針NULL
code. 讀取字元串,并删除換行符,若沒有換行符丢棄剩餘部分
char s[SIEZ];
int i;
while(fgets(s, SIZE, stdin) != NULL && s[0] != '\n'){
i = 0;
while(s[i] != '\n' && s[i] != '\0'){
i++;
}
if(s[i] == '\n'){
word[i] = '\0';
}else{
while(getchar() != '\n'){
continue;
}
}
}
空指針與空字元都是0,但類型不同,空指針是指針類型占8位元組(位址大小),空字元是字元類型占1位元組
2-3 gets_s(char str[], int len);
gets_s(char str[], int len);
隻處理标準輸入,相對fgets()沒有第三個參數,超過長度中止或退出程式(除非有相應的處理函數)
【注意】如果輸入字元超過數組大小,餘下的字元需要處理,否則會留着緩沖區,影響後面的輸入
2-4 scanf("%s", str);
scanf("%s", str);
更像是擷取“單詞”函數,讀取到下一個空白字元(空行,空格,制表符,換行符)結束,也可以指定字元串寬度 如
%10s
2-5 用getchar(); / putchar(); 自定義輸入輸出函數
//指針形式
const char * s = "str";
void Put(const char * s){
while(*s){ //等價于 *s != '\0'
putchar(*s++);
}
}
//數組形式
char s[] = "str";
void Put(const char * s){
int i = 0;
while(*(s + i)){
putchar(s[i++]);
}
}
字元串函數
函數 | 函數原型 | 用處 | 備注 |
---|---|---|---|
strlen | | 傳回字元串長度 | |
strcpy | | 将 str2 指向的字元串(包括‘\0’)拷貝到 str1 指向的位置上 | 1) 傳回 str1,不用非得指向字元串開頭 2) 連帶’\0’一起拷貝過來 |
strncpy | | 最多指派n個字元 | 要注意可能沒拷貝完(’\0’沒拷貝過來) |
strcat | | 将 str2 的副本拼接到 str1 後面,傳回 str1 | str2[0] 會覆寫掉 str1 末尾的 ‘\0’ |
strncat | | 第三個參數 n 是 str2 截取的最大字元數,防止str1數組溢出 | 不拷貝’\0’,自動添加 ‘\0’ |
strcmp | | 傳回 “str1 - str2” | 隻比較字元串而不是整個數組 |
strncmp | | 隻比較n個字元 | |
strrev | | 逆轉字元串s | |
strlwr | | 将字元串轉換為小寫 | |
strupr | | 将字元串轉換為大寫 | |
strchr | | s中查找c,找到傳回第一次出現的指針,沒找到傳回NULL | |
strrchr | | s中查找c,找到傳回最後一次出現的指針,沒找到傳回NULL | 逆向查找 |
strstr | | str1 中查找 str2出現的首位置,沒找到傳回NULL | |
strpbrk | | 如果str1中沒找到任何str2中的字元傳回’\0’,否則傳回str1 | |
sscanf | | 該函數從字元串中讀取值,并将其存儲在相應的變量位址中 | |
sprintf | | 将不同格式變量存入字元串 | |
atoi | | 接受一個字元串,變成整數 | 如"123" -> 123, “123abc” -> 123;如果沒有數字,傳回0,這種行為是未定的,同樣的還有atol(); atof(); |
strtol | | nptr指向待轉換字元串, endptr指向結束指針,base表示進制 , 該函數将字元串看作base(<=36)進制,并以十進制寫入long | |
strtoul | | 轉換為unsigned long 類型 | |
strtod | | 轉換為double類型,隻以10進制 |
code:strtol()用法
int main(){
char str2[] = "ayy";
char * end;
long int ans = strtol(str2, &end, 16);
printf("%ld %p %c", ans, end, *end);
// 10 000000000072fe41 y
return 0;
}
【注意】字元串函數傳入的都是指針,可以通過加一個偏移量來得到任意子串的結果
練習題:
-
char sign = '$';
占多少位元組?sign
占多少位元組?'$'
“$"
占多少位元組
答:sign為字元變量,占1個位元組;
為字元常量,用int編号儲存,占4個位元組(int),但隻用到第一個位元組;'$'
為字元串常量占2個位元組,包括"$"
和'$'
'\0'