1. 字符指针

1.1 处理字符串

字符指针是指向字符(char)数据类型的指针变量。它们可以用于处理字符串(以 NULL结尾的字符数组)和字符数组,并对其中的字符进行读取、修改和操作。以下是一个示例,展示了字符指针的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main()
{
char str[] = "Hello, World!"; // 字符数组
char* p = str; // 字符指针指向字符数组的首地址
*p = 'h';
p++;
*p = 'a';

// 通过字符指针访问字符数组中的字符
char* ptr = str;
while (*ptr != '\0')
{
printf("%c", *ptr);
ptr++; // 指针后移一个位置
}
printf("\n");

return 0;
}

在上述示例中,我们定义了一个字符数组 str,其中存储了字符串 "Hello, World!"。然后,我们定义了字符指针 pptr,将其指向字符数组的首地址。

通过字符指针,我们可以访问字符数组中的每个字符,并通过解引用操作符 * 访问当前指针位置上的字符。在循环之前,我们通过字符指针p来修改字符串中的某个字符值;在循环中,我们通过ptr输出了字符数组中的每个字符,直到遇到 NULL 终止符 \0

需要注意的是,字符指针没有固定长度,它可以指向任意长度的字符数组。在处理字符串时,字符指针的常见用途包括字符串的遍历、拷贝、比较和连接等操作。

1.2 做函数参数

字符指针做函数参数和整形指针做函数参数的使用方法是相同的,不同的就是指针指向的内存地址类型不同,内存大小也不同。

需求:编写一个函数,合并两个字符数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>

void mystrcat(char* dest, const char* src)
{
int len1 = 0;
int len2 = 0;
while (dest[len1])
{
len1++;
}
while (src[len2])
{
len2++;
}

for (int i = 0; i < len2; i++)
{
dest[len1 + i] = src[i];
}
}

int main()
{
char dst[100] = "hello dabing";
char src[] = " 123456";

mystrcat(dst, src);
printf("dst = %s\n", dst);

return 0;
}

在前面给大家特意强调过不通过通过运算符sizeof 计算指针指向的内存大小,所以需要在mystrcat函数内部先计算两个形参指向的实参数组中的元素个数,然后再通过循环进行两个数组的拼接。

根据参数的功能,可以将其分为三大类:

  1. 传入参数:将数据传入到函数体内部,进行使用
  2. 传出参数:将函数体内部数据通过参数传到函数体外部,相当于返回值
  3. 传入传出参数:先将外部数据出入到函数体内部进行处理,然后将新数据重新写入到该参数中,将数据从函数体传出,相当于返回值

通过上面的描述可以得知:

  • mystrcat函数的第一个参数dest是传入传出参数
    • 携带有用的数据到函数体内部,再将更新后的数据传到函数体外
  • mystrcat函数的第二个参数src是传入参数
    • 将外部数据传递到函数体内部
    • 为了保证外部数据不被修改,所以给参数添加了const限定

2. 带参数的main函数

C语言的入口函数main有两种形式,一种是不带参数的,一种是带参数是,参数的数量和类型都是固定的,其函数原型如下:

1
int main(int argc, char *argv[]);
  • argc代表命令行参数的数量,程序名字本身算一个参数
  • argv是命令行参数的字符串数组
  • argc也可以用来描述第二个参数argv数组的成员数量,argv数组的每个成员都是char *类型
1
2
3
4
5
6
7
8
9
10
11
12
// a.c
#include <stdio.h>

int main(int argc, char* argv[])
{
printf("argc = %d\n", argc);
for (int i = 0; i < argc; i++)
{
printf("%s\n", argv[i]);
}
return 0;
}

在终端中执行上面的代码,就可以在命令行中指定命令行参数了:

1
2
3
4
5
6
7
8
9
dabing@Linux:~/code$ gcc a.c -o app
dabing@Linux:~/code$ ./app aaa bbb ccc 1213 456
argc = 6
argv[0] = ./app
argv[1] = aaa
argv[2] = bbb
argv[3] = ccc
argv[4] = 1213
argv[5] = 456

上面在命令行中执行程序的时候,一共指定了5个参数(aaa、bbb、ccc、1213、456),分别被存储到main函数的参数argv数组中(argv[1] ~ argv[5]),argv[0] 中存储的是可执行程序的名字。

3. 字符串操作函数

3.1 strlen()

strlen 是一个 C 语言标准库函数,用于计算字符串的长度,即字符串中字符的个数(不包括结尾的空字符 \0)。它的原型如下:

1
2
#include <string.h>
size_t strlen(const char* str);

strlen 函数接受一个字符指针 str,指向一个以 \0结尾的字符数组(即字符串)。函数将返回一个 size_t 类型的无符号整数,表示字符串的长度。

以下是一个示例,展示了如何使用 strlen 函数:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <string.h>

int main()
{
char str[] = "Hello, World!";
size_t length = strlen(str);
printf("字符串的长度为: %zd\n", length);

return 0;
}

在上述示例中,我们定义了一个字符数组 str,它存储了字符串 “Hello, World!”。然后,我们使用 strlen 函数计算字符串的长度,并将结果存储在 length 变量中。

最后,我们使用 %zd 标志符打印出字符串的长度。%zd 是用于无符号整数的标志符,对应 size_t 类型。

需要注意的是,strlen 函数只会计算字符的个数,不会包括结尾的空字符 \0。因此,字符串的长度将不包括空字符。

strlen 函数在处理字符串时非常常用,可以用于判断字符串是否为空、计算字符串的长度、拷贝字符串等操作。

3.2 strcpy()

strcpy 是一个 C 语言标准库函数,用于将一个字符串拷贝到另一个字符串中。它的原型如下:

1
2
#include <string.h>
char* strcpy(char* destination, const char* source);

strcpy 函数接受两个参数:destinationsource

  • destination 是目标字符串的指针,表示将要拷贝到的位置
  • source 是源字符串的指针,表示要拷贝的字符串。

函数将会从 source 拷贝字符直到遇到结尾的空字符 \0,然后将这些字符以及结尾的空字符拷贝到 destination 所指向的位置。最终,函数执行成功返回 destination字符串首地址,失败返回 NULL。

以下是一个示例,展示了如何使用 strcpy 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>

int main()
{
char source[] = "我是要成为海贼王的男人!";
char destination[64];

strcpy(destination, source);
printf("拷贝的字符串为: %s\n", destination);

return 0;
}

在上述示例中,我们定义了两个字符数组:sourcedestinationsource 存储了源字符串,而 destination 是目标字符串的缓冲区。

然后,我们使用 strcpy 函数将源字符串拷贝到目标字符串中。注意,目标字符串的大小必须足够大以容纳源字符串及其结尾的空字符。

最后,我们打印出拷贝后的目标字符串。

需要注意的是,strcpy 函数没有提供缓冲区溢出的保护机制。因此,在使用 strcpy 函数时,需要确保目标字符串的缓冲区足够大,以避免溢出风险。为了避免这个问题,可以考虑使用更安全的函数,如 strncpy,它允许指定最大拷贝的字符数。

3.3 strncpy()

strncpy 是一个 C 语言标准库函数,用于将一个字符串的一部分拷贝到另一个字符串中。它的原型如下:

1
2
#include <string.h>
char* strncpy(char* destination, const char* source, size_t num);

strncpy 函数接受三个参数:destinationsourcenum

  • destination 是目标字符串的指针,表示将要拷贝到的位置
  • source 是源字符串的指针,表示要拷贝的字符串
  • num 是要拷贝的最大字符数。

函数将会从 source 拷贝字符直到遇到结尾的空字符 \0 或拷贝了 num 个字符为止,然后将这些字符以及结尾的空字符拷贝到 destination 所指向的位置。如果源字符串的长度小于 num,则目标字符串的剩余部分将用空字符填充。

最终,函数执行成功将返回 destination 字符串首地址,失败返回 NULL。以下是一个示例,展示了如何使用 strncpy 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>

int main()
{
char source[] = "我是要成为海贼王的男人!";
char destination[64];

strncpy(destination, source, sizeof(destination));
printf("拷贝的字符串为: %s\n", destination);

return 0;
}

在上述示例中,我们定义了两个字符数组:sourcedestinationsource 存储了源字符串,而 destination 是目标字符串的缓冲区。

然后,我们使用 strncpy 函数将源字符串的一部分拷贝到目标字符串中。sizeof(destination) 指定了最大拷贝的字符数,以确保目标字符串不会溢出。

最后,我们打印出拷贝后的目标字符串。

需要注意的是,strncpy 函数会确保目标字符串的剩余部分被填充上空字符,而不会保留源字符串后面的字符。因此,需要谨慎处理目标字符串的结尾。如果需要在目标字符串截断之后确保结尾有空字符,可以手动将目标字符串的最后一个字符设置为 \0

3.4 strcat()

strcat 是一个 C 语言标准库函数,用于将一个字符串追加到另一个字符串的末尾。它的原型如下:

1
2
#include <string.h>
char* strcat(char* destination, const char* source);

strcat 函数接受两个参数:destinationsource

  • destination 是目标字符串
  • source 是要追加的源字符串。

该函数将源字符串 source 的内容追加到目标字符串 destination 的末尾,并返回指向拼接后的目标字符串的指针。

以下是一个示例,展示了如何使用 strcat 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>

int main()
{
char destination[50] = "Hello";
const char source[] = ", World!";

strcat(destination, source);
printf("拼接后的字符串:%s\n", destination);

return 0;
}

在上述示例中,我们定义了一个字符数组 destination,并初始化为 “Hello”,以及一个源字符串 source,其中为 “, World!”。

使用 strcat 函数,我们将源字符串 source 的内容追加到目标字符串 destination 的末尾,形成 “Hello, World!”。最后,我们打印出拼接后的目标字符串。

需要注意的是,为了保证目标字符串有足够的容量来容纳两个字符串的拼接结果,destination 字符数组的大小必须足够大。如果 destination 的大小不够,会导致溢出的问题,可能破坏其他内存区域。

另外,目标字符串 destination 必须以 NULL 字符('\0')结尾,以便正确识别字符串的结尾。

3.5 strncat()

strncat 是一个 C 语言标准库函数,用于将一个字符串的指定长度追加到另一个字符串的末尾。它的原型如下:

1
2
#include <string.h>
char* strncat(char* destination, const char* source, size_t num);

strncat 函数接受三个参数:destinationsourcenum

  • destination 是目标字符串
  • source 是要追加的源字符串
  • num 是要追加的字符个数。

该函数将源字符串 source 的前 num 个字符追加到目标字符串 destination 的末尾,并返回指向拼接后的目标字符串的指针。

以下是一个示例,展示了如何使用 strncat 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <string.h>

int main()
{
char destination[50] = "Hello";
const char source[] = ", World!";
size_t num = sizeof(source);

strncat(destination, source, num);
printf("拼接后的字符串:%s\n", destination);

return 0;
}

在上述示例中,我们定义了一个字符数组 destination,并初始化为 “Hello”,以及一个源字符串 source,其中为 “, World!”,以及一个追加字符个数 num

使用 strncat 函数,我们将源字符串 source 的所有字符追加到目标字符串 destination 的末尾,形成 “Hello, World!”。最后,打印出拼接后的目标字符串。

需要注意的是,为了保证目标字符串有足够的容量来容纳两个字符串的拼接结果,destination 字符数组的大小必须足够大。如果 destination 的大小不够,会导致溢出的问题,可能破坏其他内存区域。此外,拼接的字符个数 num 应小于等于源字符串 source 的长度。

另外,目标字符串 destination 必须以 NULL 字符(’\0’)结尾,以便正确识别字符串的结尾。

3.6 strcmp()

strcmp 是一个 C 语言标准库函数,用于比较两个字符串的大小。它的原型如下:

1
2
#include <string.h>
int strcmp(const char* str1, const char* str2);

strcmp 函数接受两个参数:str1str2,它们分别是要进行比较的两个字符串。

该函数会按照字符的 ASCII 值进行比较,从两个字符串的第一个字符开始逐个比较,直到遇到不相等的字符或者其中一个字符串的结束符(NULL 字符\0)。函数返回一个整数值:

  • 如果 str1 小于 str2,则返回一个负数(通常为 -1)。
  • 如果 str1 等于 str2,则返回 0。
  • 如果 str1 大于 str2,则返回一个正数(通常为 1)。

以下是一个示例,展示了如何使用 strcmp 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>

int main()
{
const char* str1 = "Hello";
const char* str2 = "World";

int result = strcmp(str1, str2);
if (result < 0)
{
printf("'%s' 小于 '%s'\n", str1, str2);
}
else if (result == 0)
{
printf("'%s' 等于 '%s'\n", str1, str2);
}
else
{
printf("'%s' 大于 '%s'\n", str1, str2);
}

return 0;
}

在上述示例中,我们定义了两个字符串 str1str2,分别为 “Hello” 和 “World”。使用 strcmp 函数,对这两个字符串进行了比较。

需要注意的是,strcmp 函数是区分大小写的。如果希望进行大小写不敏感的比较,可以使用 strcasecmp 函数(Linux 平台上的一个扩展函数),或者先将字符串转换为统一的大小写再进行比较。

此外,为了避免可能的缓冲区溢出问题,需要确保被比较的字符串以 NULL字符(’\0’)结尾,以便函数正确识别字符串的结尾。

3.7 strncmp()

strncmp 是一个 C 语言标准库函数,用于比较两个字符串的指定长度。它的原型如下:

1
2
#include <string.h>
int strncmp(const char* str1, const char* str2, size_t num);

strncmp 函数接受三个参数:str1str2num

  • str1str2 分别是要进行比较的两个字符串
  • num 是要比较的字符的最大个数。

该函数会按照字符的 ASCII 值进行比较,从两个字符串的第一个字符开始逐个比较,直到遇到不相等的字符、其中一个字符串的结束符(NULL 字符\0)或者达到指定的字符个数。函数返回一个整数值:

  • 如果 str1 小于 str2,则返回一个负数(通常为负的 str1str2 第一个不相等字符的 ASCII 差值)。
  • 如果 str1 等于 str2,则返回 0。
  • 如果 str1 大于 str2,则返回一个正数(通常为正的 str1str2 第一个不相等字符的 ASCII 差值)。

以下是一个示例,展示了如何使用 strncmp 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>

int main()
{
const char* str1 = "Hello";
const char* str2 = "Hi";
size_t num = 3;

int result = strncmp(str1, str2, num);
if (result < 0)
{
printf("'%.*s' 小于 '%.*s'\n", num, str1, num, str2);
}
else if (result == 0)
{
printf("'%.*s' 等于 '%.*s'\n", num, str1, num, str2);
}
else
{
printf("'%.*s' 大于 '%.*s'\n", num, str1, num, str2);
}

return 0;
}

在上述示例中,我们定义了两个字符串 str1str2,分别为 “Hello” 和 “Hi”,以及一个指定的字符个数 num,在这里我们设置为 3。

使用 strncmp 函数,我们比较了这两个字符串的前 3 个字符。根据比较结果打印出相应的消息,指示字符串的大小关系。

需要注意的是,在比较的字符个数 num 处达到指定长度后,即使字符串中还有其他字符,也不会再继续比较了。

strcmp 函数一样,要确保被比较的字符串以 NULL 字符(’\0’)结尾,以便函数正确识别字符串的结尾。

strncmp 函数适用于需要比较指定长度字符串的情况,特别是在处理固定长度字符串或使用字符数组时。可以根据需求调整要比较的字符个数 num

在通过printf 打印字符串的时候用到了 %.*s ,它是 C 语言中格式化输出的一种格式控制符。使用 %.*s 可以打印指定长度的字符串。它的语法是:

1
printf("%.*s", length, str);
  • length 是一个整数,表示要打印的字符串的最大长度
  • str 是要打印的字符串

这种格式控制符允许我们在打印字符串时指定其长度,而不是将整个字符串打印出来。这对于处理固定长度的字符串或需要截断显示的字符串很有用。

3.8 strchr()

strchr 是一个 C 语言标准库函数,用于在一个字符串中查找指定字符的第一次出现。它的原型如下:

1
2
#include <string.h>
char* strchr(const char* str, int c);

strchr 函数接受两个参数:strc

  • str 是要在其中进行搜索的字符串
  • c 是要查找的字符的整数值。

该函数会在 str 字符串中搜索第一次出现字符 c 的位置,并返回一个指向该位置的指针。如果没有找到字符 c,函数将返回 NULL

以下是一个示例,展示了如何使用 strchr 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>

int main()
{
int c = 'W';
const char str[] = "Hello, World!";
char* result = strchr(str, c);

if (result != NULL)
{
printf("找到字符 '%c' 在字符串中的位置:%llu\n", c, result - str);
}
else
{
printf("未找到字符 '%c'\n", c);
}

return 0;
}

在上述示例中,我们定义了一个字符数组 str,其中存储了待搜索的字符串 “Hello, World!”,以及一个目标字符 c,其中存储了要查找的字符 ‘W’。

使用 strchr 函数,我们在 str 字符串中搜索第一次出现字符 c 的位置。如果成功找到字符 c,函数将返回一个指向 str 字符串中该字符的指针。我们可以通过计算指针之间的偏移量来获取字符 c 在原字符串中的位置。

在上述示例中,我们将找到的字符的位置打印出来。如果没有找到字符 c,函数将返回 NULL,我们可以相应地进行错误处理。

需要注意的是,strchr 函数参数 c 是一个整数类型,因为 C 语言使用整数表示字符,而字符常量可以直接用单引号括起来表示。在比较字符时,应使用整数值而不是字符常量。

此外,strchr 函数只能用于查找单个字符,而不能用于查找字符串。如果需要在字符串中查找子字符串,则可以使用 strstr 函数。

3.9 strstr()

strstr 是一个 C 语言标准库函数,用于在一个字符串中查找另一个字符串的第一次出现。它的原型如下:

1
char* strstr(const char* haystack, const char* needle);

strstr 函数接受两个参数:haystackneedle

  • haystack 是要在其中进行搜索的字符串
  • needle 是要查找的目标字符串。

该函数会在 haystack 字符串中搜索第一次出现 needle 字符串的位置,并返回一个指向该位置的指针。如果没有找到目标字符串,函数将返回 NULL

以下是一个示例,展示了如何使用 strstr 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>

int main()
{
const char haystack[] = "Hello, World!";
const char needle[] = "World";

char* result = strstr(haystack, needle);
if (result != NULL)
{
printf("目标字符串在原字符串中的位置:%llu\n", result - haystack);
}
else
{
printf("未找到目标字符串\n");
}

return 0;
}

在上述示例中,我们定义了一个字符数组 haystack,其中存储了待搜索的字符串 “Hello, World!”,以及一个目标字符串 needle,其中存储了要查找的字符串 “World”。

使用 strstr 函数,我们在 haystack 字符串中搜索第一次出现 needle 字符串的位置。如果成功找到目标字符串,函数将返回一个指向 haystack 字符串中目标字符串的指针。我们可以通过计算指针之间的偏移量来获取目标字符串在原字符串中的位置。

在上述示例中,我们将找到的目标字符串的位置打印出来。如果没有找到目标字符串,函数将返回 NULL,我们可以相应地进行错误处理。

需要注意的是,strstr 函数是区分大小写的。如果需要进行大小写不敏感的搜索,可以考虑使用其他函数,比如 strcasestr(Linux 平台上的一个扩展函数)或者自行实现相应的逻辑。

另外,当 needle 为空字符串时,strstr 函数将直接返回 haystack,因为空字符串可以被认为是任何字符串的子字符串。

3.10 strtok()

strtok 是一个 C 语言标准库函数,用于将字符串分割为多个子字符串。它的原型如下:

1
2
#include <string.h>
char* strtok(char* str, const char* delimiters);

strtok 函数接受两个参数:strdelimiters

  • str 是一个指向待分割的字符串的指针
  • delimiters 是一个指向包含分割符的字符串的指针。

函数将会按照 delimiters 中指定的分隔符将 str 字符串分割为多个子字符串。每次调用 strtok 函数,它将返回下一个分割的子字符串,并将 str 指针指向下一个未分割的位置。当没有更多的子字符串时,函数会返回空指针。

以下是一个示例,展示了如何使用 strtok 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <string.h>

int main()
{
char str[] = "Hello, World! How are you?";
const char delimiters[] = " ,!";

char* token = strtok(str, delimiters);
while (token != NULL)
{
printf("%s\n", token);
token = strtok(NULL, delimiters);
}

return 0;
}

在上述示例中,我们定义了一个字符数组 str,其中包含待分割的字符串 ,以及一个包含分隔符的字符串 delimiters,其中包含了空格、逗号和感叹号。

然后,我们使用 strtok 函数将 str 字符串按照 delimiters 中指定的分隔符进行分割。在第一次调用 strtok 时,传入 strdelimiters,它将返回第一个分割的子字符串 “Hello”,并将 str 指针指向下一个未分割的位置。接下来,在循环中继续调用 strtok,并传入 NULL 作为第一个参数,以便继续分割剩余的子字符串。

最后,我们打印出每个分割的子字符串。

需要注意的是,strtok 函数会修改传入的 str 字符串,在分割过程中会将分隔符替换为 \0 字符来标记子字符串的结尾。因此,在使用 strtok 之后,原始字符串可能已被修改。

此外,如果需要保留原始字符串不被修改,可以先创建副本,然后对副本进行分割操作。

3.11 sprintf

sprintf 是一个 C 语言标准库函数,用于将格式化的数据写入字符串中。它的原型如下:

1
int sprintf(char* str, const char* format, ...);
  • str表示要将格式化的数据写入到该数组中
  • format 是一个格式化字符串,用于指定输出的格式和要插入的数据,后面可以跟上要插入的数据,根据格式字符串的要求。

以下是一个示例,展示了如何使用 sprintf 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
char str[100];
int num = 123;
float f = 3.14f;
char* p = "hello, world!";

sprintf(str, "整数:%d,浮点数:%.2f, 字符串: %s", num, f, p);
printf("格式化后的字符串:%s\n", str);

return 0;
}

在上述示例中,定义了一个字符数组 str,大小为 100,用于存储格式化后的字符串。同时还定义了一个整数 num,值为 123,以及一个浮点数 f,值为 3.14,还有一个指针p指向一个常量字符串。

使用 sprintf 函数,我们将格式化后的内容写入到字符数组 str 中。在格式化字符串中 %d 表示整数的占位符,%f 表示浮点数的占位符,%s 表示字符串的占位符。

需要注意的是,为了保证字符数组 str 有足够的容量来存储格式化后的字符串,应该根据实际需要选择合适的大小。避免发生缓冲区溢出的问题。

此外,除了将格式化数据写入字符串数组中,还有类似的函数可以将格式化数据写入文件中(fprintf)或标准输出流中(printf)。这些函数在使用方式和格式化字符串的语法上类似。

4. 常见字符串操作

4.1 练习1

利用 strstr 标准库函数找出一个字符串中substr出现的次数。

4.1.1 使用 while

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>

int main(void)
{
int n = 0;
char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";
while ((p = strstr(p, "abcd")) != NULL)
{
// 能进来,肯定有匹配的子串
// 重新设置起点位置
p = p + strlen("abcd");
n++;

if (*p == 0) //如果到结束符
{
break;
}
}
printf("n = %d\n", n);

return 0;
}

4.1.2 使用 do…while

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>

int main(void)
{
int n = 0;
char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";
do
{
p = strstr(p, "abcd");
if (p != NULL)
{
n++; //累计个数
//重新设置查找的起点
p = p + strlen("abcd");
}
else //如果没有匹配的字符串,跳出循环
{
break;
}
} while (*p != 0); //如果没有到结尾
printf("n = %d\n", n);

return 0;
}

4.2 练习2

计算给定的字符串中剔除了两端空白之后剩余的子字符串的长度。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <string.h>

int fun(char* p, int* n)
{
if (p == NULL || n == NULL)
{
return -1;
}

int begin = 0;
int end = strlen(p) - 1;
//从左边开始
//如果当前字符为空,而且没有结束
while (p[begin] == ' ' && p[begin] != 0)
{
begin++; //位置从右移动一位
}
//从右往左移动
while (p[end] == ' ' && end > 0)
{
end--; //往左移动
}
if (end == 0)
{
return -2;
}
//非空元素个数
*n = end - begin + 1;

return 0;
}

int main(void)
{
char* p = " abcddsgadsgefg ";
int ret = 0;
int n = 0;

ret = fun(p, &n);
if (ret != 0)
{
return ret;
}
printf("非空字符串元素个数:%d\n", n);

return 0;
}

4.3 练习3

字符串逆置(翻转字符串)。

image-20231003184255553

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <string.h>

int inverse(char* p)
{
if (p == NULL)
{
return -1;
}
char* str = p;
int begin = 0;
int end = strlen(str) - 1;
char tmp;

while (begin < end)
{
//交换元素
tmp = str[begin];
str[begin] = str[end];
str[end] = tmp;

begin++; // 往右移动位置
end--; // 往左移动位置
}

return 0;
}

int main(void)
{
//char *str = "abcdefg"; // 常量字符串,内容不允许修改
char str[] = "abcdef";
int ret = inverse(str);
if (ret != 0)
{
return ret;
}
printf("翻转之后的字符串: %s\n", str);

return 0;
}