C语言字符串

目录

一、字符串的引入和注意事项

1.1 字符串定义的几种方式:

1.2 定义字符串的方法一和方法二的区别:

1.3 字符串输出的几种方式:

        1.3.1 循环下标法遍历输出字符串:

        1.3.2 转义字符%s输出字符串:

        1.3.3 使用puts函数输出字符串:

1.4 字符串初始总结:

二、字符串的内存存放方式及结束标志

2.1 回忆如何计算数组的大小及数组元素的个数:

2.2 字符串和字符数组的存储方式区别:

三、sizeof和strlen区别很重要

3.1 sizeof和strlen分别计算字符串变量:

3.2 sizeof和strlen分别计算字符串常量:

四、malloc动态开辟内存空间

4.1 malloc动态开辟内存空间函数引入:

        4.1.1 malloc函数认知和函数原型:

        4.1.2 使用malloc动态开辟一快内存空间:

4.2 strcpy函数引入:

        4.2.1 strcpy函数原型:

        4.2.2 strcpy函数应用:

4.3 free函数引入:

        4.3.1 free函数原型和作用:

        4.3.2 free函数应用:

4.3 realloc扩容函数引入:

        4.3.1 为什么要用realloc扩容函数:

        4.3.2 realloc函数原型和作用:

        4.3.3 realloc函数应用:

4.4 memset清理内存空间函数引入:

        4.4.1 memset函数原型:

        4.4.2 memset函数应用:

4.5 malloc动态开辟内存总结:

五、字符串常用API

5.1 输出字符串:

        5.1.1 puts函数输出字符串:

        5.1.2 使用printf函数输出字符串:

5.2 输入字符串:

        5.2.1 使用scanf函数输入字符串:

        5.2.2 gets函数输入字符串:

5.3 字符串拷贝函数strcpy:

        5.3.1 strcpy函数原型:

        5.3.2 字符串拷贝函数strcpy应用:

        5.3.3 自己实现字符串拷贝函数strcpy:

5.4 strncpy部分字符拷贝函数:

        5.4.1 strncpy函数原型和作用:

        5.4.2 strncpy函数应用:

        5.4.3 自己实现部分字符串拷贝函数strncpy:

5.5 assert断言函数:

        5.5.1 assert断言函数认知:

        5.5.2 assert断言函数原型:

        5.5.3 assert断言函数应用:

5.6 strcat字符串拼接函数:

        5.6.1 strcat函数原型和作用:

        5.6.2 strcat字符串拼接函数应用:

        5.6.3 自己实现strcat字符串拼接函数:

5.7 strcmp字符串比较函数:

        5.7.1 strcmp函数原型和作用:

        5.7.2 strcmp函数应用:

        5.7.3 自己实现字符串比较函数strcmp:

5.8 strncmp字符串比较函数:

        5.8.1 strncmp函数原型和作用:

        5.8.2 strncmp函数应用:


一、字符串的引入和注意事项

  • 字符串就是字符数组,虽然我不想这样说:

1.1 字符串定义的几种方式:

  • 我们都知道整型数组该如何定义:

#include <stdio.h>

int main()
{
    int arr[] = {1,2,3,4,5};	//定义一个整型数组
    
    for(int i=0; i<5; i++){
        printf("%d ",arr[i]);
    }
    return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c

E:\code\一阶段C语言\第七章_字符串>a.exe
1 2 3 4 5
*/
  • 那字符串和整型数组一个道理:

#include <stdio.h>

int main()
{
	char cdata[] = {'h','e','l','l','o'};	//定义一个字符数组
	
	for(int i=0; i<5; i++){
		printf("%c",cdata[i]);
	}
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c

E:\code\一阶段C语言\第七章_字符串>a.exe
hello
*/
  • 上面这种字符串定义方式并不好用,太麻烦了,那该如何改进呢?

  • 定义字符串方法一

#include <stdio.h>

int main()
{
	char cdata[] = "hello";		//定义一个字符串方法一
	
	for(int i=0; i<5; i++){
		printf("%c",cdata[i]);
	}
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c

E:\code\一阶段C语言\第七章_字符串>a.exe
hello
*/
  • 一般情况下我们这样定义一个字符串:

  • 定义字符串方法二

/*
	字符串的名字是一个地址,是字符串的首地址,我们可以定义一个指针来保存字符串的首地址
*/
#include <stdio.h>

int main()
{
	char *pcdata = "hello";		//定义一个字符串方法二
	
	for(int i=0; i<5; i++){
		printf("%c",*(pcdata+i));
	}
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c

E:\code\一阶段C语言\第七章_字符串>a.exe
hello
*/

1.2 定义字符串的方法一和方法二的区别:

  • 方法一是字符串变量,里面的每一个字符都是可以修改的

#include <stdio.h>

int main()
{
	char cdata[] = "hello";
	
	printf("修改前:\n");
	for(int i=0; i<5; i++){
		printf("%c",cdata[i]);	//字符串修改前打印输出
	}
	
	cdata[3] = 'm';
	printf("\n修改后:\n");
	for(int i=0; i<5; i++){
		printf("%c",cdata[i]);	//字符串修改后打印输出
	}
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c

E:\code\一阶段C语言\第七章_字符串>a.exe
修改前:
hello
修改后:
helmo
*/
  • 方法二是字符串常量,里面的每一个字符不允许被修改

#include <stdio.h>

int main()
{
	char *pcdata = "hello";
	
	printf("修改前:\n");
	for(int i=0; i<5; i++){
		printf("%c",*(pcdata+i));
	}
	
	*pcdata = 'm';					
	printf("修改后:\n");
	for(int i=0; i<5; i++){
		printf("%c",*(pcdata+i));
	}
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c

E:\code\一阶段C语言\第七章_字符串>a.exe
修改前:
hello
*/
  • 我们可以看到程序编译没有问题,但是程序执行到第12行的时候卡了一下就退出了,其实这里是发生了一个段错误:

/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c -g

E:\code\一阶段C语言\第七章_字符串>gdb a.exe
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.exe...done.
(gdb) r
Starting program: E:\code\C\_\a.exe
[New Thread 107412.0x19efc]
[New Thread 107412.0x18a5c]
修改前:
hello
Thread 1 received signal SIGSEGV, Segmentation fault.
0x00000000004015a4 in main () at demo.c:12
12              *pcdata = 'm';
(gdb)
*/

1.3 字符串输出的几种方式:

1.3.1 循环下标法遍历输出字符串:

  • 上面我们对字符串的遍历输出全部都是使用:循环访问下标的方式输出的,但是这种方法太蠢了:

#include <stdio.h>

int main()
{
	char cdata[] = "hello";
	char *pcdata = "hello";
	
	for(int i=0; i<5; i++){
		printf("%c",cdata[i]);
	}
	putchar('\n');
	for(int i=0; i<5; i++){
		printf("%c",*(pcdata+i));
	}
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c -g

E:\code\一阶段C语言\第七章_字符串>a.exe
hello
hello
*/

1.3.2 转义字符%s输出字符串:

/*
	在printf的时候使用转义字符%s输出字符串
*/
#include <stdio.h>

int main()
{
	char cdata[] = "hello";
	char *pcdata = "hello";
	
	printf("%s\n",cdata);	//这里要的是一个地址
	printf("%s",pcdata);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c -g

E:\code\一阶段C语言\第七章_字符串>a.exe
hello
hello
*/

1.3.3 使用puts函数输出字符串:

/*
	使用puts函数也可以输出字符串,puts的意思本来就是:Output String
*/
#include <stdio.h>

int main()
{
	char cdata[] = "hello";
	char *pcdata = "hello";
	
	puts(cdata);		//puts后面跟的是一个地址
	puts(pcdata);		//puts后面跟的是一个地址
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c -g

E:\code\一阶段C语言\第七章_字符串>a.exe
hello
hello
*/

1.4 字符串初始总结:

  • 定义字符串的几种方式:

/*
	定义一个字符串的几种方式:
*/
char cdata[] = {'h','e','l','l','o'};
char cdata2[] = "hello";
char *pcdata = "hello";
  • 输出字符串的几种方式:

/*循环遍历输出字符串,蠢*/
char cdata[] = "hello";

for(int i=0; i<5; i++){
    printf("%c",cdata[i]);
}
/*转义字符%s输出字符串*/
char cdata[] = "hello";

printf("%s",cdata);
/*使用puts函数输出字符串*/
char *pcdata = "hello";

puts(pcdata);
  • 字符串变量和字符串常量区别:

char cdata[] = "hello";
/*字符串变量里面的每一个字符是可以修改的*/

char *pcdata = "hello";
/*字符串常量里面的每一个字符不允许修改*/
  • 注意对指针的操作:

  • 指针可以保存地址,对字符串常量的的地址空间访问,但对野指针的内存空间操作不行

#include <stdio.h>

int main()
{
	char *p;	//野指针,并没有明确的内存指向,危险!⚠
	
	*p = 'c';
	printf("%c",*p);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo.c -g

E:\code\一阶段C语言\第七章_字符串>gdb a.exe
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.exe...done.
(gdb) r
Starting program: E:\code\C\_\a.exe
[New Thread 108284.0x1a868]
[New Thread 108284.0x1aac4]

Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401561 in main () at demo.c:7
7               *p = 'c';
*/

二、字符串的内存存放方式及结束标志

2.1 回忆如何计算数组的大小及数组元素的个数:

/*
	计算一个数组的大小和数组元素的个数
*/
#include <stdio.h>

int main()
{
	int data[] = {1,2,3,4,5};
	
	printf("数组的大小是:%d\n",sizeof(data));
	
	int len = sizeof(data) / sizeof(data[0]);
	printf("数组元素个数len = %d\n",len);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_len.c

E:\code\一阶段C语言\第七章_字符串>a.exe
数组的大小是:20
数组元素个数len = 5
*/

2.2 字符串和字符数组的存储方式区别:

/*
	分别计算字符数组和字符串,观察有什么区别:
*/
#include <stdio.h>

int main()
{
	char cdata[5] = {'h','e','l','l','o'};	//定义一个字符数组
	char cdata2[] = "hello";				//定义一个字符串
	
	int len = sizeof(cdata) / sizeof(cdata[0]);
	printf("len = %d\n",len);
	
	len = sizeof(cdata2) / sizeof(cdata2[0]);
	printf("len = %d\n",len);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_len.c

E:\code\一阶段C语言\第七章_字符串>a.exe
len = 5
len = 6
*/
  • 我们可以看到:字符数组的长度是5,而字符串的长度是6

  • 是因为字符串后面多出了一个'\0','\0'代表字符串的结束标志

/*
	在定义或者声明字符数组的时候,需要注意在后面加上一个'\0',如果编译器优化不到位就会出现乱码的情况!⚠
*/
#include <stdio.h>

int main()
{
    char cdata[6] = {'h','e','l','l','o','\0'};
    return 0;
}

/*字符串的结束标志'\0',这个东西非常重要,比如在后面的关于字符串的各种API里面都是在找'\0'*/
#include <string.h>

三、sizeof和strlen区别很重要

3.1 sizeof和strlen分别计算字符串变量:

#include <stdio.h>
#include <string.h>

int main()
{
	char cdata[] = "hello";
	
	printf("sizeof :%d\n",sizeof(cdata));	//sizeof计算字符串的所有字符
	printf("strlen :%d\n",strlen(cdata));	//strlen计算字符串的有效字符
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_sizeof_strlen.c

E:\code\一阶段C语言\第七章_字符串>a.exe
sizeof :6
strlen :5
*/
  • 我们看到sizeof的时候是6,是因为字符串的结束标志'\0'也是一个字符

  • 而strlen的时候是5,是因为strlen是计算字符串的有效字符的长度

/*
	我们知道sizeof是计算整个字符串的大小,那我把字符串改成128,那sizeof的结果是多少呢?
*/
#include <stdio.h>
#include <string.h>

int main()
{
	char cdata[128] = "hello";
	
	printf("sizeof :%d\n",sizeof(cdata));
	printf("strlen :%d\n",strlen(cdata));
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_sizeof_strlen.c

E:\code\一阶段C语言\第七章_字符串>a.exe
sizeof :128
strlen :5
*/
  • 可以看到sizeof是128,sizeof是计算整个字符串的大小,除了有效字符外,其他的系统会自动补'\0'

  • 该用sizeof的时候用sizeof,该用strlen的时候用strlen,如果该用sizeof的时候用了strlen那么数据就不对了!

3.2 sizeof和strlen分别计算字符串常量:

#include <stdio.h>
#include <string.h>

void test()
{
	
}

int main()
{
	void (*ptest)() = test;	//定义一个函数指针变量
	char *p = "hello";
	
	//p是一个char *型,用sizeof来计算的时候,得出的是计算机用多少个个字节来表示一个地址
	printf("sizeof p     :%d\n",sizeof(p));	
	printf("sizeof char* :%d\n",sizeof(char *));
	printf("sizeof int * :%d\n",sizeof(int *));
	printf("sizeof ptest :%d\n",sizeof(ptest));
	printf("sizeof char  :%d\n",sizeof(char));
	printf("strlen p     :%d\n",strlen(p));
	
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_sizeof_strlen.c

E:\code\一阶段C语言\第七章_字符串>a.exe
sizeof p     :8
sizeof char* :8
sizeof int * :8
sizeof ptest :8
sizeof char  :1
strlen p     :5
*/
  • sizeof字符串常量的时候,表示操作系统用多少个字节来表示一个地址

  • strlen字符串常量的时候,仍然是代表字符串的有效长度

四、malloc动态开辟内存空间

4.1 malloc动态开辟内存空间函数引入:

4.1.1 malloc函数认知和函数原型:

  • 通常我们定义数组或者字符数组的时候就已经把内存地址定好了,例如:

int data[] = {1,2,3,4,5};				//定义一个整型数组
char cdata[] = {'h','e','l','l','o'};	//定义一个字符数组
  • 那么现在我有一个需求:我不希望一开始就耗费这么大的空间,我想按需求开辟内存空间,引入动态开辟内存空间malloc函数

  • malloc函数就是我们在堆上面动态的开辟内存空间,它跟以前我们数组占用的使用的内存空间是不一样的,以前我们数组都是在栈区,那么堆是malloc对应的内存空间

/*malloc函数原型:*/

void* malloc(size_t size)
/*分配所需的内存空间,并返回一个指向它的指针*/

4.1.2 使用malloc动态开辟一快内存空间:

/*
	我们之前写过一个例子,对这个p进行赋值的时候会产生一个段错误,p是一个野指针,对野指针赋值我们会看到犯错,如果不会犯错程序中end会执行,你会看到end根本就不会执行,p没有具体的指向哪个内存空间
*/
#include <stdio.h>

int main()
{
	char *p;	//野指针
	
	*p = 'c';
	puts("end");
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_malloc.c -g

E:\code\一阶段C语言\第七章_字符串>gdb a.exe
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.exe...done.
(gdb) r
Starting program: E:\code\C\_\a.exe
[New Thread 107868.0x162c0]
[New Thread 107868.0x1b344]

Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401561 in main () at demo_malloc.c:7
7               *p = 'c';
*/
  • 我们使用malloc函数动态开辟内存空间,用p来承接它:

/*
	malloc动态开辟一块内存空间
*/
#include <stdio.h>
#include <stdlib.h>

int main()
{
	char *p;
	
	p = (char *)malloc(1);	//p现在就有了具体的内存指向
	*p = 'c';
	printf("%c\n",*p);
	puts("end");
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_malloc.c -g

E:\code\一阶段C语言\第七章_字符串>a.exe
c
end
*/
  • 现在的p就不是一个野指针了,p有了具体的内存指向

  • 程序中为什么需要将malloc返回的地址强制转换成char *型呢?因为malloc函数返回的地址是一个void *型,如果想用p来承接malloc返回的内存地址的话,必须强制转换成char *型

4.2 strcpy函数引入:

4.2.1 strcpy函数原型:

/*strcpy函数原型:*/
char* strcpy(char *dest, const char *src)
/*
	字符串拷贝函数strcpy(拷贝到哪里去,从哪里拷贝);
*/

4.2.2 strcpy函数应用:

  • 我们可以开辟一个内存空间,当然也可以开辟多个内存空间

#include <stdio.h>

int main()
{
	char *p;		//野指针
	p = (char *)malloc(1);
	*p = 'c';
	
	p = (char *)malloc(12);	//开辟12个内存空间
	return 0;
}
  • 这里开辟了12个内存空间,仍然用p来承接。这个时候就需要往里面放数据了,于是引入一个函数(strcpy字符串拷贝函数)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char *p;		//野指针
	p = (char *)malloc(1);
	*p = 'c';
    
	p = (char *)malloc(12);
	strcpy(p,"chenlichen");
	puts(p);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_malloc.c -g

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen
*/

⚠这边其实存在一个很严重的问题:

  • 刚开始p指向的是你开辟的一个内存空间,但是你又开辟了12字节内存空间也让p指向了它,此时p就会和之前开辟的那一块内存空间断开,并指向新开辟的12字节内存空间,此时那一块和p断开的内存就成了悬挂无用的内存了,已经找不到这个地址了,空闲在这了。像这种空闲在这的内存我们通常需要通过free将其释放

  • 因为malloc它在堆上面开辟内存空间,数组和普通的变量是在栈开辟内存空间,在函数调用结束以后它会清理栈里面的数据(回收内存),那堆只有到程序结束以后内存才会释放(有一个风险,如果你是一个死循环,不断地malloc,有可能会耗尽你计算机上堆的内存资源),所以需要通过free将其释放掉

4.3 free函数引入:

4.3.1 free函数原型和作用:

/*free函数原型:*/
void free(void *ptr);
  • free函数作用释放之前调用calloc,malloc和realloc所分配的内存空间,释放无用内存,防止内存泄漏,防止悬挂指针(悬挂指针是野指针的一种)

4.3.2 free函数应用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char *p;		//野指针
	p = (char *)malloc(1);
	*p = 'c';
	free(p);				//释放无用内存,放置内存泄露
	p = NULL;				//尽量不要悬挂指针
	p = (char *)malloc(12);
	strcpy(p,"chenlichen");
	puts(p);
	return 0;
}
  • free释放掉无用内存后,p重新变成了野指针,一般让p = NULL,尽量不要悬挂指针!

4.3 realloc扩容函数引入:

4.3.1 为什么要用realloc扩容函数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char *p;				//野指针

	p = (char *)malloc(12);
	strcpy(p,"chenlichenshuai123456789987654321");
	puts(p);
	return 0;
}
  • 比如说现在有这么多的数据需要放到内存空间中,而上面malloc只开辟了12个字节的内存空间,如果这么多数据需要放到里面的话是不行的,会发生越界,导致程序崩溃,这个时候就可以使用realloc扩容函数来解决

4.3.2 realloc函数原型和作用:

/*realloc函数原型:*/
void* realloc(void *ptr, size_t size);
/*
	字符串扩容函数realloc(原本扩容地址,要继续增大多少);
*/
  • realloc函数作用:尝试重新调整之前调用malloc和calloc所分配的ptr所指向的内存块大小

4.3.3 realloc函数应用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char *p;				//野指针

	p = (char *)malloc(12);
	printf("扩容前地址:%x\n",p);
	int len = strlen("chenlichenshuai123456789987654321");//计算字符串的有效字符大小
	int newLen = len - 12 + 1;		//实际扩容大小 = 总长度 - 原来动态开辟的12个 + 结束标志'\0'
	realloc(p,newLen);
	printf("扩容后地址:%x\n",p);
	strcpy(p,"chenlichenshuai123456789987654321");
	puts(p);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_malloc.c

E:\code\一阶段C语言\第七章_字符串>a.exe
扩容前地址:685920
扩容后地址:685920
chenlichenshuai123456789987654321
*/
  • 扩容前和扩容后的地址是一样的

4.4 memset清理内存空间函数引入:

4.4.1 memset函数原型:

/*memset函数原型:*/
void* memset(void *str, int c, size_t n);
/*
	void* memset(新开辟的内存地址,要初始化的内容,内存大小);
*/

4.4.2 memset函数应用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char *p;						//野指针

	p = (char *)malloc(12);
	memset(p,'\0',12);				//把刚开始动态开辟的内存空间全部初始化成'\0'
	printf("扩容前地址:%x\n",p);
	int len = strlen("chenlichenshuai123456789987654321");//计算字符串的有效字符大小
	int newLen = len - 12 + 1;		//实际扩容大小 = 总长度 - 原来动态开辟的12个 + 结束标志'\0'
	realloc(p,newLen);
	printf("扩容后地址:%x\n",p);
	strcpy(p,"chenlichenshuai123456789987654321");
	puts(p);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_malloc.c

E:\code\一阶段C语言\第七章_字符串>a.exe
扩容前地址:685920
扩容后地址:685920
chenlichenshuai123456789987654321
*/

4.5 malloc动态开辟内存总结:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char *p;					//野指针
	p = (char *)malloc(1);		//现在有了具体的内存指向
	*p = 'c';
	printf("%c\n",*p);
	free(p);					//释放无用内存,放置内存泄漏
	p = NULL;					//尽量不要悬挂指针
	p = (char *)malloc(12);		//使用malloc函数动态开辟了12个内存空间用p承接
	if(p == NULL){				//判断malloc是否动态开辟成功
		printf("malloc erroe!\n");
	}
	memset(p,'\0',12);			//新开辟的内存空间全部初始化成'\0'
	printf("扩容前地址:%x\n",p);
	int len = strlen("chenlichenshuai123456789987654321");
	int newLen = len - 12 + 1;
	realloc(p,newLen);			//扩容内存
	printf("扩容后地址:%x\n",p);
	strcpy(p,"chenlichenshuai123456789987654321");
	puts(p);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_malloc.c

E:\code\一阶段C语言\第七章_字符串>a.exe
c
扩容前地址:1b13e0
扩容后地址:1b13e0
chenlichenshuai123456789987654321
*/

五、字符串常用API

5.1 输出字符串:

5.1.1 puts函数输出字符串:

/*
	使用puts函数输出字符串
*/
#include <stdio.h>

int main()
{
	char cdata[] = "chenlichen shaui";
	char *pchar  = "chenlichen shuai";
	
	puts(cdata);
	puts(pchar);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_puts_gets.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen shaui
chenlichen shuai
*/

5.1.2 使用printf函数输出字符串:

/*
	在printf函数的时候利用转义字符%s输出字符串
*/
#include <stdio.h>

int main()
{
	char cdata[] = "chenlichen shuai";
	char *pchar  = "chenlichen shaui";
	
	printf("%s\n",cdata);
	printf("%s\n",pchar);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_puts_gets.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen shuai
chenlichen shaui
*/

5.2 输入字符串:

5.2.1 使用scanf函数输入字符串:

/*
	在scanf函数的时候利用转义字符%s输入字符串
*/
#include <stdio.h>

int main()
{
	char cdata[128] = {'\0'};
	
	puts("请输入字符串");
	scanf("%s",cdata);
	puts(cdata);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_puts_gets.c

E:\code\一阶段C语言\第七章_字符串>a.exe
请输入字符串
chenlichenshuai
chenlichenshuai
*/

5.2.2 gets函数输入字符串:

/*gets函数原型:*/
char* gets(char *str);
  • 因为本函数可以无限读取,易发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值

/*
	使用gets函数输入字符串
*/
#include <stdio.h>

int main()
{
	char cdata[128] = {'\0'};
	
	puts("请输入字符串");
	gets(cdata);
	puts(cdata);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_puts_gets.c

E:\code\一阶段C语言\第七章_字符串>a.exe
请输入字符串
chenlichenshuai
chenlichenshuai
*/

5.3 字符串拷贝函数strcpy:

5.3.1 strcpy函数原型:

/*strcpy函数原型:*/
char* strcpy(char *dest, const char *src)
/*    字符串拷贝函数strcpy(拷贝到哪里去,从哪里拷贝);*/

5.3.2 字符串拷贝函数strcpy应用:

#include <stdio.h>
#include <string.h>

int main()
{
	char str[128] = {'\0'};
	char *p = "chenlichen handsome";
	
	strcpy(str,p);
	puts(str);
	return 0;
}
/*程序运行结果:
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcpy.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/

5.3.3 自己实现字符串拷贝函数strcpy:

/*
	第一种自己实现字符串拷贝函数:
*/
#include <stdio.h>

char* myStrcpy(char *dest, char *src)
{
	if(dest == NULL || src == NULL){
		return NULL;
	}
	
	char *bak = dest;
	
	while(*src != '\0'){
		*dest = *src;
		dest++;
		src++;
	}
	*dest = '\0';
	return bak;
}

int main()
{
	char str[128] = {'\0'};
	char *p = "chenlichen handsome";
	
	myStrcpy(str,p);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcpy.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/
/*
	第二种自己实现字符串拷贝函数:
*/
#include <stdio.h>

char* myStrcpy2(char *dest, char *src)
{
    if(dest == NULL || src == NULL){
        return NULL;
    }
    char *bak = dest;
    
    while(*src != '\0'){
        *dest++ = *src++;
    }
    *dest = '\0';
    
    return bak;
}

int main()
{
    char str[128] = {'\0'};
    char *p = "chenlichen handsome";
    
    myStrcpy2(str, p);
    puts(str);
    return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcpy.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/
/*
	第三种自己实现字符串拷贝函数:
*/
#include <stdio.h>

char* myStrcpy3(char *dest, char *src)
{
    if(dest == NULL || src == NULL){
        return NULL;
    }
    char *bak = dest;
    
    while((*dest++ = *src++) != '\0');
    *dest = '\0';
    
    return bak
}

int main()
{
    char str[128] = {'\0'};
    char *p = "chenlichen handsome";
    
    myStrcpy3(str, p);
    puts(str);
    return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcpy.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/

5.4 strncpy部分字符拷贝函数:

5.4.1 strncpy函数原型和作用:

/*strncpy函数原型:*/
char* strncpy(char *dest, const char *src, int n);
/*    strncpy(拷贝到哪里去,从哪里拷贝,要拷贝多少);    
      strncpy函数作用:表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest*/

5.4.2 strncpy函数应用:

#include <stdio.h>
#include <string.h>

int main()
{
	char str[128] = {'\0'};
	char *p = "chenlichen handsome";
	
	strncpy(str, p, 6);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strncpy.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenli
*/

5.4.3 自己实现部分字符串拷贝函数strncpy:

#include <stdio.h>
#include <string.h>

char* myStrncpy(char *dest, char *src, int count)
{
	if(dest == NULL || src == NULL){
		return NULL;
	}
	char *bak = dest;
	
	while(*src != '\0' && count>0){
		*dest++ = *src++;
		count--;
	}
	if(count > 0){
		while(count > 0){
			count--;
			*dest++ = '\0';
		}
		return dest;
	}
	*dest = '\0';
	
	return bak;
}

int main()
{
	char str[128] = {'\0'};
	char *p = "chenlichen handsome";
	
	myStrncpy(str, p, 6);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strncpy.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenli
*/

5.5 assert断言函数:

5.5.1 assert断言函数认知:

/*assert 宏的原型定义在 assert.h 中,其作用是如果它的条件返回错误,则终止程序执行*/
#include <assert.h>
  • assert 的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向 stderr 打印一条出错信息,然后通过调用 abort 来终止程序运行。

  • 使用 assert 的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。

5.5.2 assert断言函数原型:

/*assert函数原型:*/
void assert(int expression);

5.5.3 assert断言函数应用:

#include <stdio.h>
#include <assert.h>

char* myStrcpy(char *dest, char *src)
{
	assert(dest != NULL && src != NULL);//断言函数
	
	char *bak = dest;
	
	while(*src != '\0'){
		*dest++ = *src++;
	}
	*dest = '\0';
	
	return bak;
}

int main()
{
	char str[128] = {'\0'};
	char *p = "chenlichen shuai";
	
	myStrcpy(str,p);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_assert.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen shuai
*/

5.6 strcat字符串拼接函数:

5.6.1 strcat函数原型和作用:

/*strcat函数原型:*/
char* strcat(char *dest, const char *src);
/*
	strcat(拼接到哪里去,从哪里拼接);
		strcat作用:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾	的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。
*/

5.6.2 strcat字符串拼接函数应用:

#include <stdio.h>
#include <string.h>

int main()
{
	char str[128] = "chenlichen";
	char *p = " handsome";
	
	strcat(str,p);	//strcat(拼接到哪里去,从哪里拼接);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcat.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/

5.6.3 自己实现strcat字符串拼接函数:

/*
	自己实现字符串拼接函数方法一:
*/
#include <stdio.h>
#include <assert.h>

char* myStrcat(char *dest, char *src)
{
	assert(dest != NULL && src != NULL);
	char *bak = dest;
	
	while(*dest != '\0'){
		dest++;
	}
	while(*src != '\0'){
		*dest++ = *src++;
	}
	*dest = '\0';
	
	return bak;
}

int main()
{
	char str[128] = "chenlichen";
	char *p = " handsome";
	
	myStrcat(str,p);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcat.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/
/*
	自己实现字符串拼接函数方法二:
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>

char* myStrcat2(char *dest, char *src)
{
	char *bak = dest;
	
	strcpy(dest+strlen(dest),src);	//字符串拷贝函数strcpy(拷贝到哪里去,从哪里拷贝);
	return bak;
}

int main()
{
	char str[128] = "chenlichen";
	char *p = " handsome";
	
	myStrcat2(str,p);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcat.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/
/*
	自己实现字符串拼接函数方法三:
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>

char* myStrcat3(char *dest, char *src)
{
	assert(dest != NULL && src != NULL);
	
	char *bak = dest;
	
	for( ; *dest!='\0'; dest++);
	while((*dest++ = *src++) != '\0');
	*dest = '\0';
	
	return bak;
}

int main()
{
	char str[128] = "chenlichen";
	char *p = " handsome";
	
	myStrcat3(str,p);
	puts(str);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcat.c

E:\code\一阶段C语言\第七章_字符串>a.exe
chenlichen handsome
*/

5.7 strcmp字符串比较函数:

5.7.1 strcmp函数原型和作用:

/*strcmp函数原型:*/
int strcmp(const char *str1, const char *str2);
/*
	strcmp作用:若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数
*/

5.7.2 strcmp函数应用:

/*
	如果两个字符串一样,则ret = 0;
*/
#include <stdio.h>
#include <string.h>

int main()
{
	char *str1 = "chenlichen";
	char *str2 = "chenlichen";
	int ret;
	
	ret = strcmp(str1,str2);
	if(ret == 0){
		puts("两个字符串一样");
	}
	printf("ret = %d\n",ret);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcmp.c

E:\code\一阶段C语言\第七章_字符串>a.exe
两个字符串一样
ret = 0
*/
/*
	如果str1 < str2,则ret = -1;
*/
#include <stdio.h>
#include <string.h>

int main()
{
	char *str1 = "chenlichena";
	char *str2 = "chenlichenb";
	int ret;
	
	ret = strcmp(str1,str2);
	if(str1 == str2){
		puts("两个字符串一样");
	}
	printf("ret = %d\n",ret);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcmp.c

E:\code\一阶段C语言\第七章_字符串>a.exe
ret = -1
*/
/*
	如果str1 > str2,则ret = 1;
*/
#include <stdio.h>
#include <string.h>

int main()
{
	char *str1 = "chenlichenb";
	char *str2 = "chenlichena";
	int ret;
	
	ret = strcmp(str1,str2);
	if(str1 == str2){
		puts("两个字符串一样");
	}
	printf("ret = %d\n",ret);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcmp.c

E:\code\一阶段C语言\第七章_字符串>a.exe
ret = 1
*/

5.7.3 自己实现字符串比较函数strcmp:

/*
	自己实现字符串比较函数strcmp;
*/
#include <stdio.h>

int myStrcmp(char *str1, char *str2)
{
	int ret = 0;
	int n_str1 = 0;
	int n_str2 = 0;
	char *bakstr1 = str1;
	char *bakstr2 = str2;
	
	while(*str1 != '\0' && *str2 != '\0' && (*str1 == *str2)){
		str1++;
		str2++;
	}
	if(*str1 || *str2){
		str1 = bakstr1;
		str2 = bakstr2;
		while(*str1 != '\0'){
			n_str1 = n_str1 + *str1++;
		}
		while(*str2 != '\0'){
			n_str2 = n_str2 + *str2++;
		}
	}
	ret = n_str1 - n_str2;
	if(ret < 0){
		ret = -1;
	}
	if(ret > 0){
		ret = 1;
	}
	return ret;
}

int main()
{
	char *str1 = "chbenlichena";
	char *str2 = "chaenlichenb";
	int ret;
	
	ret = myStrcmp(str1,str2);
	if(ret == 0){
		puts("两个字符串一样");
	}
	printf("ret = %d\n",ret);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strcmp.c

E:\code\一阶段C语言\第七章_字符串>a.exe
两个字符串一样
ret = 0
*/

5.8 strncmp字符串比较函数:

5.8.1 strncmp函数原型和作用:

/*strncmp函数原型:*/
int strncmp(const char *str1, const char *str2, size_t n);
/*
	作用:功能是把 str1 和 str2 进行比较,最多比较前 n 个字节,若str1与str2的前n个字符相同,则返回0;若str1大于str2,则返回大于0的值;若str1小于str2,则返回小于0的值。
*/

5.8.2 strncmp函数应用:

/*
	若两个字符串前6个字符一样,则返回0
*/
#include <stdio.h>
#include <string.h>

int main()
{
	char *str1 = "chenlichen";
	char *str2 = "chenlichen";
	int ret;
	
	ret = strncmp(str1,str2,6);
	if(ret == 0){
		puts("两个字符串前面几个字符一样");
	}
	printf("ret = %d\n",ret);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strncmp.c

E:\code\一阶段C语言\第七章_字符串>a.exe
两个字符串前面几个字符一样
ret = 0
*/
/*
	若str1前6个字符 < str2前6个字符,则返回-1
*/
#include <stdio.h>
#include <string.h>

int main()
{
	char *str1 = "achenlichen";
	char *str2 = "bchenlichen";
	int ret;
	
	ret = strncmp(str1,str2,6);
	if(ret == 0){
		puts("两个字符串前几个字符一样");
	}
	printf("ret = %d\n",ret);
	return 0;
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strncmp.c

E:\code\一阶段C语言\第七章_字符串>a.exe
ret = -1
*/
/*
	若str1前6个字符 > str2前6个字符,则返回1
*/
#include <stdio.h>
#include <string.h>

int main()
{
	char *str1 = "bchenlichen";
	char *str2 = "achenlichen";
	int ret;
	
	ret = strncmp(str1,str2,6);
	if(ret == 0){
		puts("两个字符串前几个字符一样");
	}
	printf("ret = %d\n",ret);
	return 0; 
}
/*
E:\code\一阶段C语言\第七章_字符串>gcc demo_strncmp.c

E:\code\一阶段C语言\第七章_字符串>a.exe
ret = 1
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值