目录
字符串引入:
字符串或串(String)是由数字、字母、下划线组成的一串字符。一般记为 s=“a1a2···an”(n>=0)。它是编程语言中表示文本的数据类型。在程序设计中,字符串(string)为符号或数值的一个连续序列,如符号串(一串字符)或二进制数字串(一串二进制数字)。
补充:字符串在存储上类似字符数组,它每一位单个元素都是能提取的。
定义字符串:
编程练习:如何定义字符串
#include <stdio.h>
int main()
{
//字符串既然叫做字符数组,就可以用定义数组的方法来定义它
char c = 'c'; //定义一个字符的方法
char cdata2[] = "makabaka"; //cdata作为字符串变量(指针常量)可以用来修改里边的数值
char *pchar = "makabaka"; //pchar作为字符串常量(常量指针),不可以修改里边存放的值
printf("%s",cdata2);
putchar('\n');
puts(cdata2);
printf("%s",pchar);
putchar('\n');
puts(pchar);
/*char cdata[8] = {'m','a','k','a','b','a','k','a'}; //打印时候最后可能有乱码,后面解决
char cdata2[] = "makabaka"; //前边的定义方式繁琐,采用后两种
char *pchar = "makabaka";
for(int i=0;i<8;i++)
{
printf("%c",*(pchar+i)); //遍历方式较为繁琐
}*/
/*int a[5] = {12,13,14,15,16};
for(int i=0;i<5;i++) 之前定义数组及其遍历方法
{
printf("%d ",a[i]);
}*/
/*char *p; //*p是一个野指针,对野指针的赋值会发生段错误
*p = 'm';
puts("done\n");*/
return 0;
}
注意:指针可以保存地址、修改指向、指向字符常量的地址空间;但操作野指针的地址空间是不可行的,会造成段错误!!!
常量指针本质上是一个指针,指针就是地址,它所指向的地址可变,但里边存放的值不可变;指针常量本质上是一个常量,它所指向的地址不可变,一旦定义就确定了它的地址,但里边存放的常量可以被修改。
存储方式:
字符串的存储方式:会在字符串的末尾加一个'\0'
代码验证:用sizeof计算字符串的长度
#include <stdio.h>
int main()
{
//字符串的结束标志:'\0'
char cdata[] = {'m','a','k','a','b','a','k','a','\0'}; //实际上字符在内存空间中的存储方式
//当用定义字符数组时,要注意在最后不'\0'
char cdata2[] = "makabaka"; //定义字符串时会自动在后边补'\0'
char *pchar = "makabaka";
int len1 = sizeof(cdata2)/sizeof(cdata2[0]);
int len2 = sizeof(cdata)/sizeof(cdata[0]);
int len3 = sizeof(pchar)/sizeof(pchar[0]);
printf("len1 = %d\n",len1);
printf("len2 = %d\n",len2);
printf("len3 = %d\n",len3); //最后一个是表示OS操作系统用8字节来存放一个地址
printf("%s\n",cdata); //当没有'\0'的结束标志时,输出字符串就可能有乱码
return 0;
}
strlen和sizeof的区别:
strlen是计算字符串的有效长度(不包含'\0'),sizeof是计算整个字符串的长度
编程验证:
#include <stdio.h>
#include <string.h>
void My_str()
{
}
int main()
{
char str[] = "Welcome";
char str2[128] = "makabaka";
char *p = "Welcome";
void (*p2)();
p2 = My_str;
printf("strlen of str: %d\n",strlen(str)); //计算字符串的有效长度,不包含'\0'
printf("sizeof of str: %d\n",sizeof(str)); //计算整个字符串的长度
printf("strlen of str2: %d\n",strlen(str2)); //计算字符串的有效长度,不包含'\0'
printf("sizeof of str2: %d\n",sizeof(str2)); //计算整个字符串的长度
printf("strlen of p: %d\n",strlen(p));
printf("sizeof of p: %d\n",sizeof(p)); //以下都是指针变量,OS操作系统用8字节来存放一个地址
printf("sizeof of int *: %d\n",sizeof(int *));
printf("sizeof of char *: %d\n",sizeof(char *));
printf("sizeof of p2: %d\n",sizeof(p2));
return 0;
}
动态开辟字符串
C 库函数 - malloc()
描述
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
声明
void *malloc(size_t size)
参数
- size -- 内存块的大小,以字节为单位。
返回值
该函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
C 库函数 - calloc()
描述
C 库函数 void *calloc(size_t nitems, size_t size) 分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
声明
void *calloc(size_t nitems, size_t size)
参数
- nitems -- 要被分配的元素个数。
- size -- 元素的大小。
返回值
该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL。
C 库函数 - strcpy()
描述
C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
声明
void *memset(void *str, int c, size_t n)
参数
- str -- 指向要填充的内存块。
- c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
- n -- 要被设置为该值的字符数。
返回值
该值返回一个指向存储区 str 的指针。
C 库函数 - realloc()
描述
C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
声明
void *realloc (void *ptr, size_t size)
参数
- ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
- size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
返回值
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
编程练习:用malloc和calloc动态开辟字符串,realloc重新分配内存空间
/*
用malloc分配空间并初始化,并且实现字符串的拷贝
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// void *malloc(size_t size)
//void free(void *ptr)
// void *memset(void *str, int c, size_t n)
//char *strcpy(char* dest, const char *src);
int main()
{
char *p;
p = (char *)malloc(1); //malloc开辟内存空间,p有了固定的地址,不再是野指针
*p = 'c';
free(p); //防止堆空间被耗尽,将malloc/calloc分配的空间释放掉
p = NULL;
p = (char *)malloc(10);
if(p == NULL) //如果malloc正常分配空间,p就不会返回空值
{
printf("malloc error");
exit(-1);
}
memset(p,'\0',10); //将malloc分配到的空间进行初始化,全部初始化成0
strcpy(p,"makabaka"); //将"makabaka"拷贝到p所指向的内存空间里
printf("%s\n",p);
puts("end");
/*char *p; //p是野指针,不能对其进行赋值操作,会出现段错误
*p = 'c';
printf("%c\n",*p);
puts("end");*/
return 0;
}
/*
用realloc重新分配malloc或者calloc之前所分配的内存空间
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// void *malloc(size_t size)
// void *realloc(void *ptr, size_t size)
//void free(void *ptr)
// void *memset(void *str, int c, size_t n)
//char *strcpy(char* dest, const char *src);
int main()
{
char *p;
char str[] = "balabalabalablalabalmakabakamakabakamakabakamakabna";
p = NULL;
p = (char *)malloc(10);
if(p == NULL)
{
printf("malloc error");
exit(-1);
}
memset(p,'\0',10);
int len = strlen(str); //计算有效字符的长度
int newlen = len - 10 + 1; //还需开辟新的内存空间+1(用来存放字符的结束标志)
realloc(p,newlen); //重新分配空间给p
strcpy(p,str);
printf("%s\n",p);
return 0;
}
/*
用calloc分配内存空间,而且用strcpy实现字符串的拷贝
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// void *malloc(size_t size)
// void *calloc(size_t nitems, size_t size)
//void free(void *ptr)
// void *memset(void *str, int c, size_t n)
//char *strcpy(char* dest, const char *src);
int main()
{
char *p; //野指针
p = (char *)calloc(1,sizeof(char)); //用calloc给p开辟空间,p有固定的地址,不再是野指针
*p = 'c';
free(p); //释放calloc分配的空间
p = NULL;
p = (char *)calloc(10,sizeof(char));
if(p == NULL)
{
printf("calloc error");
exit(-1);
}
strcpy(p,"makabaka");
printf("%s\n",p);
puts("end");
return 0;
}
/*
用realloc重新分配malloc或者calloc之前所分配的内存空间
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *p;
char str[] = "balabalabalablalabalmakabakamakabakamakabakamakabna";
p = (char *)calloc(1,sizeof(char));
*p = 'c';
free(p);
p = NULL;
p = (char *)calloc(10,sizeof(char));
if(p == NULL)
{
printf("calloc error");
exit(-1);
}
int len = strlen("balabalabalablalabalmakabakamakabakamakabakamakabna");
int newlen = len - 10 + 1;
realloc(p,newlen);
strcpy(p,str);
printf("%s\n",p);
puts("end");
}
字符串常用操作函数:
输出字符串
编程说明:输出字符串:puts / printf
/*
用puts和printf输出字符串
*/
#include <stdio.h>
int main()
{
char *p = "Welcome come to C world!";
puts(p);
printf("%s\n",p);
return 0;
}
获取字符串
编程说明:获取字符串:scanf / gets
/*
用scanf和gets获取字符串的输入
*/
#include <stdio.h>
int main()
{
char str[128] = {'\0'};
puts("请输入字符串");
//scanf("%s",str);
gets(str);
puts("输出字符串");
puts(str);
printf("%s\n",str);
return 0;
}
计算字符有效长度
编程说明:计算有效字符的长度:strlen(不包括'\0')
/*
用strlen计算字符串的有效长度,不包括'\0'
*/
#include <stdio.h>
#include <string.h>
int main()
{
char str[128] = "makabaka";
int len = strlen(str);
printf("len = %d\n",len);
return 0;
}
字符串拷贝
编程说明:字符串拷贝:strcpy 和 strncpy的区别
/*
用strcpy实现字符串的拷贝
*/
//char *strcpy(char *dest, const char *src)
#include <stdio.h>
#include <string.h>
int main()
{
char str[128] = {'\0'};
char *p = "makabaka handsome";
strcpy(str,p);
puts(str);
return 0;
}
/*
用strncpy实现字符串的拷贝
*/
//char *strncpy(char *dest, const char *src, size_t n)
#include <stdio.h>
#include <string.h>
int main()
{
char str[128] = {'\0'};
char *p = "makabaka handsome";
strncpy(str,p,8);
puts(str);
return 0;
}
由此可见:strcpy 和 strncpy 的区别是strncpy可以指定要拷贝多少字符到目标函数中去,strcpy只可以拷贝全部字符到目标函数中。
编程说明:自己编写函数实现strcpy 和 strncpy
/*
自己写编写函数实现strcpy的功能
*/
//char *strcpy(char *dest, const char *src)
#include <stdio.h>
char *My_strcpy(char *dest,char *src)
{
if(dest == NULL || src ==NULL)
{
return NULL;
}
char *back = dest; //定义一个备份来存放dest的地址,因为dest会偏移
while(*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = '\0'; //字符串结束标志
return back;
}
char *My_strcpy2(char *dest,char *src)
{
if(dest == NULL || src == NULL)
{
return NULL; //只要目标函数或者源有一个是空指针的话,就不做拷贝,返回空指针
}
char *back = dest;
while(*src != '\0')
{
*dest++ = *src++; //先将src中的值取出来后偏移,赋值给dest后偏移
}
*dest = '\0';
return back;
}
char *My_strcpy3(char *dest,char *src)
{
if(dest == NULL || src == NULL)
{
return NULL;
}
char *back = dest;
while((*dest++ = *src++) != '\0'); //while中的判断条件不仅有0或1,下边有举例
*dest = '\0';
return 0;
}
int main()
{
char *p = "makabaka";
char str[128] = {'\0'};
/*char a = 'm';
char b;
if((b = a) == 'm') if或者while中的判断条件不单只有0和1
{
printf("条件成立\n");
}*/
My_strcpy3(str,p);
puts(str);
return 0;
}
/*
自己写编写函数实现strncpy的功能
*/
//char *strncpy(char *dest, const char *src, size_t n)
#include <stdio.h>
char *My_strncpy(char *dest,char *src,int count)
{
if(dest ==NULL || src == NULL)
{
return NULL;
}
char *back = dest;
while(*src != '\0' && count > 0)
{
*dest++ = *src++;
count--;
}
if(count > 0)
{
while(count>0)
{
count--;
*dest++ = '\0';
}
return dest;
}
*dest = '\0';
return back;
}
int main()
{
char *p = "makabaka";
char str[128] = {'\0'};
My_strncpy(str,p,6); //指定从p中拷贝多少字符到str中
puts(str);
return 0;
}
断言(assert)
assert() 的用法像是一种"契约式编程",其表达的意思就是,程序在假设条件下,能够正常良好的运作,其实就相当于一个 if 语句。
但是这样写的话,就会有无数个 if 语句,甚至会出现,一个 if 语句的括号从文件头到文件尾,并且大多数情况下,我们要进行验证的假设,只是属于偶然性事件,又或者我们仅仅想测试一下,一些最坏情况是否发生,所以这里有了 assert()。
assert 宏的原型定义在 assert.h 中,其作用是如果它的条件返回错误,则终止程序执行。
assert 的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向 stderr 打印一条出错信息,然后通过调用 abort 来终止程序运行。
编程说明:断言(assert)函数的运用
#include <stdio.h>
#include <assert.h>
char *My_strcpy(char *dest,char *src)
{
assert(dest != NULL && src != NULL);
/*if(dest == NULL || src == NULL)
{ 之前的判断方法
return NULL;
}*/
char *back = dest;
while((*dest++ = *src++) != '\0');
*dest = '\0';
return back;
}
int main()
{
char *p = "makabaka";
//char *p = NULL; 只要目标函数和源有一个位空指针,就会调用assert中的abort来终止程序运行
char str[128] = {'\0'};
My_strcpy(str,p);
puts(str);
return 0;
}
字符串拼接
编程说明:字符串拼接 strcat
/*
用strcat实现字符串的拼接
*/
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "maka";
char *p = "baka";
strcat(str,p);
puts(str);
return 0;
}
编程说明:自己编写函数实现strcat的函数功能
/*
用strcat实现字符串的拼接
*/
//char *strcat(char *dest, const char *src)
#include <stdio.h>
#include <string.h>
#include <assert.h>
char *My_strcat(char *dest,char *src)
{
assert(dest != NULL && src != NULL);
char *back = dest;
while(*dest != '\0')
{
dest++;
}
while((*dest++ = *src++) != '\0');
*dest = '\0';
return back;
}
char *My_strcat2(char *dest,char *src)
{
assert(dest != NULL && src != NULL);
char *back = dest;
strcpy(dest+strlen(dest),src);
return back;
}
char *My_strcat3(char *dest,char *src)
{
assert(dest != NULL && src != NULL);
char *back = dest;
for(;*dest != '\0';dest++);
while((*dest++ = *src++) != '\0');
return back;
}
int main()
{
char str[] = "maka ";
char *p = "baka !";
My_strcat3(str,p);
puts(str);
return 0;
}
字符串比较
编程说明: 字符串比较:strcmp 和 strncmp 的区别
/*
strcmp的功能:比较字符串的大小
*/
//int strcmp(const char *s1,const char *s2);
#include <stdio.h>
#include <string.h>
int main()
{
char *p1 = "Welcome";
char *p2 = "welcome";
/*
若p1 = p2,则返回值n = 0;
若p1 > p2,则返回值n = 1; (根据ASCII码的大小)
若p1 < p2,则返回值n = -1;
*/
int n = strcmp(p1,p2);
printf("n = %d\n",n);
return 0;
}
/*
strncmp的功能:比较指定字符串的大小
*/
//int strncmp ( const char * str1, const char * str2, size_t n )
#include <stdio.h>
#include <string.h>
int main()
{
char *p1 = "makabakakakak";
char *p2= "makabakalllll";
/*
功能是把 str1 和 str2 进行比较,最多比较前 n 个字节,
若str1与str2的前n个字符相同,则返回0;
若s1大于s2,则返回大于0的值;
若s1 小于s2,则返回小于0的值。
*/
int n = strncmp(p1,p2,7); //指定比较前7位的字符串大小
printf("n = %d\n",n);
return 0;
}
由此可见:strcmp 和 strncmp 的区别是strncmp可以指定要比较前多少个字符,strcmp只可以比较全部字符大小,两个都是最终返回0,1, -1。
查找子字符
C 库函数 - strchr()
描述
C 库函数 char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
声明
char *strchr (const char *str, int c)
参数
- str -- 要被检索的 C 字符串。
- c -- 在 str 中要搜索的字符。
返回值
该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。
编程说明:查找子字符 : strchr
/*
strchr的功能:查找子字符:
*/
//char *strchr(const char *str, int c)
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "maka-baka";
char ch = '-';
char *ret;
ret = strchr(str,ch);
if(ret) //如果找到字符ch,则输出Found,否则输出Not Found
{
puts("Found");
}
else
{
puts("Not Found");
}
printf(" |%c| 后的字符是:|%s|\n",ch,ret);
return 0;
}
查找子字符串
C 库函数 - strstr()
描述
C 库函数 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 '\0'。
声明
char *strstr (const char *haystack , const char *needle)
参数
- haystack -- 要被检索的 C 字符串。
- needle -- 在 haystack 字符串内要搜索的小字符串。
返回值
该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。
编程说明:查找子字符串:strstr
/*
strstr的功能:查找子字符串
*/
//char *strstr(const char *haystack, const char *needle)
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "makabaka";
char de[] = "baka";
char *ret;
ret = strstr(str,de);
if(ret)
{
puts("Found");
}
else
{
puts("Not Found");
}
printf("子字符串是:|%s|",ret);
return 0;
}
字符串分割
C 库函数 - strtok()
描述
C 库函数 char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串,delim 为分隔符。如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,str指向要分解的字符串,之后再次调用要把str设成NULL。
声明
char *strtok (char *str , const char *delim)
参数
- str -- 要被分解成一组小字符串的字符串。
- delim -- 包含分隔符的 C 字符串。
说明:
当strtok()在参数str的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数str字符串,往后的调用则将参数str设置成NULL。每次调用成功则返回指向被分割出片段的指针。
返回值
该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。
编程说明:字符串分割:strtok
/*
strtok的功能:分割字符串
*/
//char *strtok(char *str, const char *delim)
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "ma.ka.ba.ka";
char de[] = ".";
char *tok;
tok = strtok(str,de);
while(tok != NULL)
{
printf("分割后的字符是:|%s|\n",tok);
tok = strtok(NULL,de);
}
return 0;
}