C语言 字符串

283 阅读4分钟

目录

1.字符串的定义和使用
2.字符串的内存存放方式及结束标志
3.sizeof和strlen的区别
4.动态开辟字符串
4.1 malloc
4.2 free
4.3 realloc
4.4 memset
5.几种常用字符串的API
5.1输出字符串与获取字符串
5.2strcpy
5.3strncpy
6.assert函数

1.字符串的定义和使用

表示字符串的几种方式:

1.和整形数组表示一样

 int main()
 {
  //int data[] = { 1,2,3,4,5 };
  char cdata[] = { 'H','e','l','l','o' };
  for (int i = 0;i < 5;i++)
  {
	  printf("%c", cdata[i]);
  }
  return 0;
 } 

2.字符串变量(可以对某个字符进行操作)

int main()
{
   char str[] = "hello";
   for (int i = 0;i < 5;i++)
   {
   	printf("%c", str[i]);
   }
   return 0;
}

3.字符串常量(只能用,不允许被操作)

int main()
{
	char* pchar = "hello";
	for (int i = 0;i < 5;i++)
	{

		printf("%c", *pchar++);
	}
	return 0;
}
//另一种写法
   int main()
{
	char* pchar = "hello";
    printf("%s",pchar);
	return 0;
} 

4. putchar 和puts

int main()
{
	putchar('a');
	putchar('\n');
	putchar('b');
	return 0;
}
输出结果:
a
b

putchar( )只能输出一个字符, 换行也算作一个字符。

int main()
{
	char* m = "abcd";
	puts(m);
	puts("hh");
	return 0;
}

puts( )自带换行效果,可用输出字符串,且字符串可以用指针替代。

2.字符串的内存存放方式及结束标志

int main()
{
  //字符串和字符数组的区别:
  int data[] = { 1,2,3,4,5 };
  char cdata[]={'h','e','l','l','o'};
  char cdata2[] = "hello";

  int len;
  //计算数组data的元素个数,不同类型的数据记得用对应的方式
  len = sizeof(data) / sizeof(data[0]);
  printf("%d\n", len);

  len = sizeof(cdata) / sizeof(char);//注意这里是char
  printf("%d\n", len);
  
  len = sizeof(cdata2) / sizeof(char);//注意这里是char
  printf("%d\n", len);

  return 0;
}
输出结果:
5
5
6

字符串类型, 默认多一个'\0',这是字符串的结束标志

3.sizeof和strlen的区别

int main()
{
  char cdata[] = "hello";//带了一个\0

  printf("sizeof: %d\n", sizeof(cdata));
  printf("strlen: %d\n", strlen(cdata));

  return 0;
}
输出结果为:
6
5

strlen能计算有效字符的长度

int main()
{
  char cdata[128] = "hello";//带了一个\0

  printf("sizeof: %d\n", sizeof(cdata));//siezeof计算的是整个空间长度
  printf("strlen: %d\n", strlen(cdata));

  return 0;
}
输出结果为:
sizeof:128
strlen: 5

记住:该用siezof( ) 和该用strlen( )的地方!

int main()
{
	char* p = "hello";

	printf("sizeof:p       : %d\n", sizeof(p));
	printf("sizeof:char*   : %d\n", sizeof(char*));
    printf("sizeof:int*    : %d\n", sizeof(int*));//不管是什么类型的指针,就是用4个字节来表示
	printf("sizeof:char    : %d\n", sizeof(char));
	printf("strlen         : %d\n", strlen(p));

	return 0;
}
计算结果为:
sizeof:p       : 4
sizeof:char\*  : 4
sizeof:int\*   : 4
sizeof:char    : 1
strlen         : 5

因为p是一个 char* 的指针,而sizeof计算时,得出的是计算机用多少地址来表示一个地址

4.动态开辟字符串

4.1malloc

函数原型 void *malloc(size_t size),malloc可以按需开辟字符串空间。
C库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。

int main()
{
	char* p;//野指针,会报错
	*p = 'c';
	return 0;
}

用上malloc后,p有了具体的指向空间。同理,我们能开辟多个字符串空间

int main()
{
	char* p;
	p = (char*)malloc(1);//p有了具体的指向空间
	*p = 'c';
	printf("%c", *p);
	return 0;
}

4.2 free

C 库函数 void free(void *ptr) 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
作用1:释放,防止内存泄漏。 作用2:防止指针悬挂

int main()
{
	char* p;
	p = (char*)malloc(1);//p有了具体的指向空间
	*p = 'c';

	free(p);

	p = (char*)malloc(12);
	strcpy(p, "qmzhaha");
	puts(p);

	return 0;
}

对于上面的代码,由于p已经指向'c'占用了空间,后面又让p指向'qmzhaha',前面的p的空间就被挂起, 用free( )可以释放不使用的数据。(malloc在堆上开辟空间,普通的数组在栈上开辟空间)

4.3realloc

其次,"qmzhaha"小于12个字节,但当超出12时怎么办?我们需要用到realloc
函数原型 void *realloc(void *ptr, size_t size)能起到扩容作用
C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。

int main()
{
	char* p;
	p = (char*)malloc(12);
	int len = strlen("qmahahha2312213141421123");
	int newlen=len-12+1;
	realloc(p, newlen);
	strcpy(p, "qmahahha2312213141421123");
	puts(p);

	return 0;
}

4.4 memset

p = (char*)malloc(12);
memset(p, '\0', 12);

能把malloc申请到的12个空间都初始化成'\0'

5.几种常用字符串的API

5.1输出字符串与获取字符串

puts(); printf("%s",p); scanf("%s",p);gets()

int main()
{
	char* p = "i like running!";
	char str[128] = { '\0' };


	puts(p);
	printf("%s\n", p);

	strncpy(str, p, 6);//拷贝
	puts(str);

	puts("请输入字符串");
	//scanf("%s",str);
	gets(str);
	puts(str);

	return 0;
}

5.2strcpy

需要头文件**#include <string.h>**

5.2.1覆盖拷贝函数strcpy

函数原型:char* strcpy(char* destination, const char* resourse);

将source全覆盖拷贝到destination,返回值:char* 类型的 数组首地址

注意:
1.strcpy只用于字符串复制,遇到'\0'时停止,还会复制字符串的结束符'\0'; 所以源字符串必须以'\0'结束,也会将源字符串的'\0'拷贝到目标空间。
2.destination的空间必须>=source的空间。

5.2.2自己实现字符串拷贝函数

在学习strncpy前,自己先拟一个myStrcpy()函数来达到同样的效果,实现过程如下:

char* myStrcpy(char* des, char* src)
{
	if (des == NULL || src == NULL)
	{
		return NULL;
	}

	char* bake ;//char一个备份地址去保存目标地址
    bake = des ;

	while (*src != '\0')
	{
		*des = *src;//拷贝
		des++;//拷贝完最后一个字母后,地址都++,再次进入这个循环,则满足条件了
		src++;
	}
	*des = '\0';
	return bake;
}

int main()
{
	char str[128] = { '\0' };
	char* p = "i need a job!";
	myStrcpy(str, p);//自己做一个函数,达到strncpy的效果
	puts(str);

	return 0;
}

关于while内其他两种写法 image.png

写法1

	while (*src != '\0')
	{
	  *des++=*src++;
	}
写法2
    while ((*des++=*src++)!= '\0')
    

5.3strncpy

5.3.1 覆盖拷贝函数strncpy

函数原型:char * strncpy(char *des, const char *src, size_t n);

拷贝字符串前n个字符到des,返回值为:des字符串起始地址

5.3.2自己实现strncpy

char* myStrncpy(char* des, char* src,int count)//赋值多少个字符
{
	if (des == NULL || src == NULL)
	{
		return NULL;
	}

	char* bake = des;//char 一个备份地址去保存目标地址

	while (*src != '\0'&& count>0)
	{
		*des++ = *src++;//拷贝
		count--;
	}
	if (count > 0)
	{
		while (count > 0)
		{
			count--;
			*des++ = '\0';
		}
		return des;
	}
	*des = '\0';

	return bake;
}

int main()
{
	char str[128] = { '\0' };
	char* p = "i need a job!";
	myStrncpy(str, p,6);//自己做一个函数,达到strncpy的效果
	puts(str);

	return 0;
}

6.assert函数

需要包含断言头文件#include<assert.h>,其相当于一个if语句。

if(假设成立)
{
程序正常运行;
}
else
{
报错&&终止程序!(避免由程序运行引起更大的错误)  
}

assert作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向 stderr 打印一条出错信息,然后通过调用 abort 来终止程序运行。因此不能让(条件)为假

if (des == NULL || src == NULL)
{
  return NULL;
}

将上方代码转化为assert为:

asssert(des != NULL && src != NULL)