1 字符串基本知识
字符串 : 一串字符 , "0个或多个字符"
单个字符 ,C语言中有专门的类型
char/unsigned char ---> 保存的是单个字符的ASCII码(8bits整数)
C语言中,没有字符串的类型.
C语言的字符串是 通过 char*(字符型指针)来实现.
C语言点字符串 是一串字符(0个或多个),这些字符的内存地址是连续的.
如果我们知道"字符串"的首地址(第一个字符的地址) : 0x3000
推出字符串第二个字符的地址 : 0x3001
推出字符串第三个字符的地址 : 0x3002
......
并且我们如果知道每个字符的地址,就可以通过他的地址(char*)来访问每个字符.
通过字符串的首地址,不停地 + 1 ,就可以得到每个字符的地址,但是你通过字符的首地址,
能不能知道这个字符串有多少个字符?
不能知道
所以,C语言中规定任何字符串的末尾加一个"字符串结束标识"
null字符,空字符,0,'\0'
char*用来保存字符串的首地址,并且约定字符串结束的标志为'\0'.
你可以通过char * 来访问或操作字符串.
char s[10]; //数组名 s,当指针, &s[0],char* scanf("%s",s);//%s从键盘上输入一串字符,输入到一个char*的地址中去 scanf("%s",&s[0]);//一样的 //scnaf("%s",&s);//不行,类型不匹配 %s --> char* //typeof(&s) ==> typeof(s) * ==> char[10] * printf("%s",s);//%s输出一个字符串,后面也需要接一个 字符串类型(char*)
2 字符串常量
字符串常量是不可以被修改的字符串,只能读.
字符串常量保存在一个.rodata的内存区
如 :
C代码中,所有 " "引起来的都是字符串常量 并且 所有 有 " " 引起的表达式的值,就是这个字符串的首地址 typeof("abcd123") : const char * char *p = "abcd123";//表达式 "abcd123" 的值是 字符 'a' 的地址.&'a'
内存:
ro read only 只读区域
存储那些只能读的数据,常量, 如"abcd"
rw read & write 可读可写的区域
存储那些变量
如 : 没加const修饰的 变量,数组,指针变量...
int a;//a存储在可读可写的区域
int b[100];//a存储在可读可写的区域
char c[200];//a存储在可读可写的区域
int *p;//p存储在可读可写的区域
char *p2;//p2存储在可读可写的区域
......
3 字符串变量
把字符串(多个字符) 存储在一个可读可写的区域就可以了
char s[100] ;//s存储在可读可写的区域
你把字符串存储在s这个数组中, 字符串变量.
char *p = (char *)malloc(100);//malloc分配的空间也是 可读可写的
你把字符串存储在p指向的空间,字符串变量
char s[3]; s[0] = 'a'; s[1] = 'b'; s[2] = '\0'; ==> char s[3] = {"ab"}; s[0] = 'a'; s[1] = 'b'; s[2] = '\0';
练习 :
1.
char s1[] = {'a','b','c'}; char s2[] = {"abc"}; 请问数组 s1 的元素个数是几个? //3 s2的元素个数是几个? //4 sizeof(s1) = ? //3 sizeof(s2) = ? //4 字符串s1中字符的个数多少? //3 字符串s2中字符的个数是多少? //3 printf("%s\n",s1); //abc printf("%s\n",s2); //abc2.
char s3[5] = {'a','b','c'}; char s4[5] = {"abcd"}; sizeof(s3) = ? //5 sizeof(s4) = ? //5 printf("%s\n",s3);//abc printf("%s\n",s4);//abcd
3.分析如下程序的输出结果
char s[10] = {"abcde"}; //s += 2;//不可以的,s是一个数组名, 常量 s[2] = 'A';//可以的.数组s存储可读可写的区域 *(s + 2) = 'A';//可以的 printf("%s\n",s);4.分析如下程序的输出结果
char *s = "abcde"; s += 2;//可以的,s变量 //*(s + 2) = 'A';//不可以,s指向的空间是 只读空间 printf("%s\n",s);
5.写一个函数,求一个字符串的长度
"字符串的长度" : 指的这个字符串中有多少个字符,一个一个去数,数到\0为止(不包括\0)
int strlen_m(char *s)
{
int n = 0;
while(*s)
{
n++;
s++;
}
return n;
}
几个常用的字符串处理函数,C标准库已经为我们写好了,我们可以直接用,需要包含他的头文件
#include <string.h>
4 几个常用的字符串处理函数
-
strlen 用来求一个字符串的长度
NAME strlen - calculate the length of a string SYNOPSIS #include <string.h> size_t strlen(const char *s); DESCRIPTION The strlen() function calculates the length of the string pointed to by s, excluding the terminating null byte ('\0'). RETURN VALUE The strlen() function returns the number of characters in the string pointed to by s.
例子 :
int l; l = strlen("abcde"); l = 5 char s[10] = {"abc"}; l = strlen(s); l = 3 sizeof(s) = 10; strlen : 是一个函数,用来计算一个字符串的长度,算到\0为止 sizeof : 是一个运算符,用来求一个对象(或类型)自己本身所占内存的字节数 char s1[4] = {'1','0'}; sizeof(s1) = 4 l = strlen(s1); l = 2 char s2[4] = {'1',0,'3'}; l = strlen(s2); l = 1 l = strlen( "abc\123456\0de"); l = 7
-
strcpy 字符串拷贝函数
cpy : copy
NAME strcpy, strncpy - copy a string SYNOPSIS #include <string.h> strcpy用来把src指向的字符串,拷贝到dest指向的空间中去, 拷贝到\0为止(\0也会拷贝) char *strcpy(char *dest, const char *src); dest : 指向 "目的地" ,dest指向的空间,要保证可写,并且空间足够大. src : 指向"源字符串",把src指向的字符串拷贝到dest指向的空间中去 返回值 : 成功 返回拷贝后字符串的首地址(dest) strcpy有一个巨大的bug : 他没有考略到越界的问题,有可能会导致内存的越界访问或非法访问 strncpy 就是为了strcpy的bug而产生的. strncpy 用来把src指向的字符串拷贝到dest指向的空间中去. 但是strncoy到底拷贝了多少个字符呢? <==n (1) 遇到\0拷贝结束(此时后面会多拷贝n - strlen(src) 个 \0) (2) 一直没有遇到\0,但是已经拷贝了n个字符,拷贝结束(此时\0不会拷贝) char *strncpy(char *dest, const char *src, size_t n); dest : 指向目标空间 src : 指向源字符串 n : 规定拷贝n个字符.一般来说,n为dest指向那个的空间的最大容量 返回值 : 成功 返回拷贝后字符串的首地址(dest)
-
memcpy 内存拷贝
NAME memcpy - copy memory area SYNOPSIS #include <string.h> mencpy用来把str指向的内存中的前n个字节,拷贝到dest指向的空间中去 void *memcpy(void *dest, const void *src, size_t n); dest :指向目标内存块,要保证dest指向的空间足够大 src : 指向源内存块 n : 要拷贝的字节数 返回值 : 返回dest的这个地址 DESCRIPTION The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas must not overlap. Use memmove(3) if the memory areas do overlap. RETURN VALUE The memcpy() function returns a pointer to dest.
思考 :
strncpy 和 memcpy 有区别?
strncpy 是字符串拷贝函数,顶多拷贝n个字节(遇到\0拷贝结束,多补几个 n - i 个\0)
memcpy 是内存拷贝函数(没有类型的概念),成功的话,一定会拷贝n个字节
字符串的拷贝,最好用strncpy
其他的内存拷贝,用memcpy
char s1[8];
char s2[8];
scanf("%s",s2);
memcpy(s1,s2,strlen(s2) + 1 );
//strlen(s2) + 1 <= 8 没有问题
//strlen(s2) + 1 > 8 就会有问题,越界
//"+1" 我想把s2这个字符串后面的\0也拷贝
strncpy(s1,s2,8);
//strncpy(s2) < 8 可以完整地拷贝字符串到s1
//strncpy(s2) >= 9 ,也不至于越界,因为顶多拷贝8个
-
bzero 把一个字符串清0
NAME bzero, explicit_bzero - zero a byte string SYNOPSIS #include <strings.h> bzero 用来把s指向的空间的前面n个字节 清0 void bzero(void *s, size_t n);
-
strcat/strncat 连接两个字符串
NAME strcat, strncat - concatenate two strings SYNOPSIS #include <string.h> strcat用来把src指向的字符串,连接到dest指向的字符串的末尾去."尾部连接" strcat先找到dest指向的那个字符串末尾(\0处),从\0处开始连接src 指向的字符串,最后加一个\0 char *strcat(char *dest, const char *src); dest :指向目标字符串,空间要足够大 src : 指向原始字符串 返回值 : 返回 连接后字符串的首地址(dest) strcat 也有bug,没有考虑到dest指向的空间,是否足够大的问题 ...越界风险 strncat及时用来修复strcat这个bug的 strncat 也是把src指向的字符串拷贝到dest的末尾 但是它顶多拷贝n个字符 (1) 遇到\0拷贝结束 <= n (2) 即使没有遇到\0,但是已经拷贝了n个字符,也结束(此时\0不会拷贝) char *strncat(char *dest, const char *src, size_t n);
-
strcmp/strncmp 比较两个字符
两个字符串如何比较?
一个一个字符PK,PK他们的ASCII码.
while(第一个字符串没有结束 || 第二个字符串没有结束) { if 第一个字符串的第i个字符 < 第二个字符串的第i个字符 return -1; //表示第一个字符串 小 else if(第一个字符串的第i个字符 > 第二个字符串的第i个字符) return 1;//表示第一个字符串 大 else //第一个字符串的第i个字符 == 第二个字符串的第i个字符 i++; //比较下一个 } return 0;//表示两个字符串相等 ,一模一样
NAME strcmp, strncmp - compare two strings SYNOPSIS #include <string.h> int strcmp(const char *s1, const char *s2); strncmp只比较前面的n字符 int strncmp(const char *s1, const char *s2, size_t n); DESCRIPTION The strcmp() function compares the two strings s1 and s2. It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2. The strncmp() function is similar, except it compares only the first (at most) n bytes of s1 and s2.
例子 :
int m = strcmp("123","ABC"); m < 0 m = strcmp("123","123\0abc"); m == 0 m = strcmp("1234","123"); m > 0 m = strcmp("123","12345",3) m == 0