C语言进阶第五课-----------字符函数和字符串函数 1

简介: C语言进阶第五课-----------字符函数和字符串函数

289a638c3a6846f2b514c038571cf891.png本章前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在

常量字符串 中或者 字符数组 中。

字符串常量 适用于那些对它不做修改的字符串函数.

函数介绍

在学习函数我们会见到很多陌生的函数,怎么去认识这些函数我推荐一个网站C语言函数,废话少说,开始正题

strlen

在前面中我们已经使用过这个函数,知道这个函数的作用是计算字符串的长度,计算的是‘\0’前面的长度,不包括’\0’

参数是字符串起始的地址,也就是要从哪里开始计算字符长度的地址,返回的类型是无符号整形

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "abcd";
  char arr1[] = { 'a','b','c','d' };//这里没有'\0';
  printf("%d\n", strlen(arr));
  printf("%d\n", strlen(arr1));
  printf("%d\n", strlen("abcd"));
  return 0;
}

其实我们还可以计算字符串常量的长度 strlen(“abcd”);传入的是a的地址

如果我们来分析一下对应的返回类型就会有另一个知识点

#include<stdio.h>
#include<string.h>
int main()
{
  if (strlen("abc") - strlen("abcdef") > 0)
  {
    printf("大于");
  }
  else
  {
    printf("小于");
  }
  return 0;
}


289a638c3a6846f2b514c038571cf891.png

如果看过前面我写过的博客就会明白,无符号数的在内存的存储 ,所有都是有效位,连符号符号位也计算进去了 ,范围是0~4,294,967,295 ,上面虽然我们通过数学方法计算得到-3,但是却是无符号数,在内存的存储是

1111 1111 1111 1111 1111 1111 1111 1101

最终计算出来的是一个很大的数.

或者我们可以把-3强转成int类型,就会是真正的-3了

如果我们要模拟实现有三种方法

方法1:递归

#include<stdio.h>
size_t my_strlen(char* arr)
{
  if (!*arr)
  {
    return 0;
  }
  return 1 + my_strlen(arr + 1);
}
int main()
{
  char arr[] = "abcdwe";
  size_t a = my_strlen(arr);
  return 0;
}

方法2:计数

#include<stdio.h>
int main()
{
  char arr[] = "aaaaaaaaa";
  int a = 0;
  char* p = arr;
  while (*p++)
  {
    a++;
  }
  return 0;
}

方法三:首元素的地址减去’\0’的地址

#include<stdio.h>
int main()
{
  char arr[] = "aaaaaaaaa";
  char* p = arr;
  while (*p++)
  {
    ;
  }
  printf("%d", p - arr - 1);
  return 0;
}

strcpy

字符串拷贝

返回类型是char* ,返回strDestination,我们可以认为这是一个初始地址, 参数有两个,一个是目的地址,一个源头地址,strSource是被拷贝的起始地址,strDestination是粘贴到的起始地址

trcpy函数将strSource(包括终止的null字符)复制到strDestination指定的位置。复制或附加字符串时不执行溢出检查。如果源字符串和目标字符串重叠,则strcpy的行为是未定义的。

会把’\0’也会拷贝过去的,

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[10] = "xxxxxxxxx";
  strcpy(arr, "aaaa");
  return 0;
}

5bfb6616311847879d4483a0b11eeb78.png注意事项:

  1. 源字符串必须以 ‘\0’ 结束。
    拷贝结束是要遇到源头的结束标志,如果源头没有结束标志,就会继续拷贝直到遇到结束标志
#include<string.h>
int main()
{
  char arr[] = { 'a','b','c','d' };
  char arr1[] = "xxxxxxxx";
  strcpy(arr1, arr);
  return 0;
}

运行这段代码就会发现程序会崩溃,原因是没有遇见’\0’会一直拷贝,直到拷贝的个数超过了arr1数组的长度,越界访问,程序崩溃,

2… 会将源字符串中的 ‘\0’ 拷贝到目标空间。

3. 目标空间必须足够大,以确保能存放源字符串。

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[2] = "a";
  char arr1[5] = "asbd";
  strcpy(arr, arr1);
  return 0;
}

这里的情况和上面的情况是一样的,会越界访问,程序崩溃,这里是主要是拷贝的长度太长

  1. 目标空间必须可变。
  2. 学会模拟实现。
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* arr, const char* arr1)
{
  assert(arr && arr1);
  char* p = arr;
  char* p1 = arr1;
  while (*p++ = *p1++)
  {
    ;
  }
  return arr;
}
int main()
{
  char arr[] = "abcdhghgfg";
  char arr1[] = "xxx";
  my_strcpy(arr, arr1);
  return 0;
}

strcat

字符串追加

这里的参数和strcpy的参数是一样的,有目标地址和源头地址

将源字符串的副本追加到目标字符串。目的地中的终止空字符被源的第一个字符覆盖,并且空字符被包括在由目的地中两者的串联形成的新字符串的末尾。

简单的理解就是源字符串的第一个字符会覆盖目标字符串的’\0’,然后往后添加字符,这里也会把源字符串的’\0’追加过来

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "asfd";
  char arr1[20] = "abcde";
  strcat(arr1, arr);
  return 0;
}

db71ca2f76b7415c8b259d76aaa10608.png

可以看到arr1的e后面原来是有结束标志的,因为字符串追加,会覆盖,

所以会有一些注意事项:

1.目标空间要足够大.可修改

#include<stdio.h>
#include<string.h>
#include<assert.h>
int main()
{
  char arr[] = "asfd";
  char arr1[20] = "abcde";
  strcat(arr, arr1);
  return 0;
}

b13e0daa073c46da9fbba8a4a8f61c15.png

当我们运行出来就会发现当追加的字符串长度大于目标空间,就会追加就会发生越界访问,程序会崩溃

2. 目标字符串必须要有’\0’,源字符串也必须有’\0’

因为strcat追加是从目标字符串的’\0’开始追加的,如果没有就会无法追加,

如果源字符串没有’\0’就会一直追加

#include<stdio.h>
#include<string.h>
#include<assert.h>
int main()
{
  char arr[] = { 'a','b' };
  char arr1[10] = "qqq";
  strcat(arr1, arr);
  return 0;
}

b250080f2ae24c8088e8f0dc260cfa16.png这里还是会发生越界访问,程序会崩溃

模拟实现

#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* arr1, const char* arr)
{
  assert(arr1 && arr);
  char *p = arr;
  char* p1 = arr1;
  int sz = strlen(arr1);
  //找到目标的结束标志
  p1 = p1 + sz;
  //数据追加
  while (*p1 = *p)
  {
    p1++;
    p++; 
  }
  return arr1;
}
int main()
{
  char arr[] = "abc";
  char arr1[10] = "xx\0x111";
  my_strcat(arr1, arr);
  return 0;
}

需要注意的是如果目标字符串的’\0’后面还有字符,会直接覆盖上去的,不会扩大字符长度,

到这里可能有些小可爱会像让字符串自己给自己追加,结果发现我们模拟出来的函数程序崩溃了

下面为例:

这里是长度位20的数组,数组里面有四个元素,我们追加是通过访问内存来获取对应的值,然后覆盖上去,当我们把‘\0’覆盖了,当源字符地址找到对应的内存,但是却不是\0’而是字符a, 下一个字符是b,如此往下,没有结束标记,一直追加,最终会发生越界访问,程序崩了

但是使用库函数strcat却可以,但是我们是不建议的,

相关文章
|
11天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
27 6
|
1月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
36 10
|
24天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
29天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
59 7
|
29天前
|
存储 编译器 程序员
【c语言】函数
本文介绍了C语言中函数的基本概念,包括库函数和自定义函数的定义、使用及示例。库函数如`printf`和`scanf`,通过包含相应的头文件即可使用。自定义函数需指定返回类型、函数名、形式参数等。文中还探讨了函数的调用、形参与实参的区别、return语句的用法、函数嵌套调用、链式访问以及static关键字对变量和函数的影响,强调了static如何改变变量的生命周期和作用域,以及函数的可见性。
30 4
|
1月前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。
|
27天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
23 0
|
27天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
20 0
|
30天前
|
算法 C语言
factorial函数c语言
C语言中实现阶乘函数提供了直接循环和递归两种思路,各有优劣。循环实现更适用于大规模数值,避免了栈溢出风险;而递归实现则在代码简洁度上占优,但需警惕深度递归带来的潜在问题。在实际开发中,根据具体需求与环境选择合适的实现方式至关重要。
27 0
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3