天天看點

C Primer Plus:(第十一章)字元串和字元串函數

字元串和字元串函數

    • 字元串的定義
    • 字元串的輸入
      • 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(); 自定義輸入輸出函數
    • 字元串函數
    • 練習題:

字元串的定義

  1. 字元串常量(string constant)(或說 字元串字面量(string literal))是靜态存儲類别(static storage class),如果函數中使用字元串常量,該字元串常量隻會被存儲一次,且在整個程式的生命周期記憶體在
  2. 把字元串看作指針:雙引号括起來的内容視為指向該字元串存儲位置的指針

    printf("%s %p %c", "str1", "str2", *"str3");

    %s

    直接列印出字元串

    str1

    %p

    列印出字元串

    str2

    的首位址,

    %c

    列印出

    * "str3"

    指向的内容,字元 ‘s’

    char * s = "str";

  3. 字元串的聲明:

    char str1[40] = "This is a string";

    char str1[10] = " 's' , 't' , 'r' , '\0' ";

    注意第二種必須加’\0’否則就是普通的字元數組,而不是一個字元串,也可以

    char s[] = "This is a string";

    自動确定長度(隻能在初始化時,否則必須用常量定義長度)
  4. 字元串的數組形式與指針形式的差別:

    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. 字元串數組:數組形式和指針形式

指針形式:

數組形式:

同樣的:

  • 差別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++]);
	}
}
           

字元串函數

C Primer Plus:(第十一章)字元串和字元串函數
函數 函數原型 用處 備注
strlen

size_t len = strlen(const char * str)

傳回字元串長度
strcpy

char * strcpy(char * restrict str1, const char * restrict str2);

将 str2 指向的字元串(包括‘\0’)拷貝到 str1 指向的位置上 1) 傳回 str1,不用非得指向字元串開頭 2) 連帶’\0’一起拷貝過來
strncpy

char * strncpy(char * restrict str1, const char * restrict str2, size_n n);

最多指派n個字元 要注意可能沒拷貝完(’\0’沒拷貝過來)
strcat

char * strcat(char * restrict str1, const char * str2);

将 str2 的副本拼接到 str1 後面,傳回 str1 str2[0] 會覆寫掉 str1 末尾的 ‘\0’
strncat

char * strncat(char * restrict str1, const char * str2, size_t n);

第三個參數 n 是 str2 截取的最大字元數,防止str1數組溢出 不拷貝’\0’,自動添加 ‘\0’
strcmp

int strcmp(const char * str1, const char * str2)

傳回 “str1 - str2” 隻比較字元串而不是整個數組
strncmp

int strncmp(const char * str1, const char * str2, size_t n);

隻比較n個字元
strrev

extern char *strrev(char * s);

逆轉字元串s
strlwr

extern char *strlwr(char *s);

将字元串轉換為小寫
strupr

extern char *strupr(char *s);

将字元串轉換為大寫
strchr

char * strchr(const char * s, int c);

s中查找c,找到傳回第一次出現的指針,沒找到傳回NULL
strrchr

char * strrchr(const char * s, int c);

s中查找c,找到傳回最後一次出現的指針,沒找到傳回NULL 逆向查找
strstr

char * strstr(const char * str1, const char * str2);

str1 中查找 str2出現的首位置,沒找到傳回NULL
strpbrk

char * strpbrk(const char * str1, const char * str2);

如果str1中沒找到任何str2中的字元傳回’\0’,否則傳回str1

if(strpbrk(s1, s2) == '\0')

sscanf

int sscanf(const char *str, const char *format, ...)

該函數從字元串中讀取值,并将其存儲在相應的變量位址中
sprintf

int sprintf(char *str, const char *format, ...)

将不同格式變量存入字元串
atoi

int atoi(const char * restrict str);

接受一個字元串,變成整數 如"123" -> 123, “123abc” -> 123;如果沒有數字,傳回0,這種行為是未定的,同樣的還有atol(); atof();
strtol

long strtol(const char * restrict nptr, char ** restrict endptr, int base);

nptr指向待轉換字元串, endptr指向結束指針,base表示進制 , 該函數将字元串看作base(<=36)進制,并以十進制寫入long
strtoul

strtoul()

轉換為unsigned long 類型
strtod

double strtod(const char * restrict nptr, char ** restrict endptr)

轉換為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;
}
           

【注意】字元串函數傳入的都是指針,可以通過加一個偏移量來得到任意子串的結果

C Primer Plus:(第十一章)字元串和字元串函數

練習題:

  1. char sign = '$';

    sign

    占多少位元組?

    '$'

    占多少位元組?

    “$"

    占多少位元組

    答:sign為字元變量,占1個位元組;

    '$'

    為字元常量,用int編号儲存,占4個位元組(int),但隻用到第一個位元組;

    "$"

    為字元串常量占2個位元組,包括

    '$'

    '\0'