strlen - 计算字符串长度

功能:计算一个字符串的长度,即从起始地址开始到第一个空字符('\0')前的字符个数(不包括'\0'本身)。

原型

1
size_t strlen(const char *str);

参数

  • str:要计算长度的字符串的指针。

返回值

  • 返回字符串的长度,类型为 size_t(通常是无符号整数)。

重要特点

  • 它只是遍历内存,直到遇到 '\0' 为止。如果传入的指针不是指向一个以 '\0' 结尾的字符串,函数会继续访问后面的内存,导致未定义行为(如崩溃)。

示例

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

int main() {
char str[] = "Hello";
size_t len = strlen(str); // len = 5
printf("The length of '%s' is %zu.\n", str, len);
return 0;
}

strcpy - 字符串复制

功能:将一个字符串(包括结束符 '\0')复制到另一个字符数组中。

原型

1
char *strcpy(char *dest, const char *src);

参数

  • dest:目标字符数组的指针,用于存放复制后的字符串。
  • src:源字符串的指针。

返回值

  • 返回目标字符串的指针 dest

重要警告

  • strcpy 不会检查目标数组 dest 的大小是否足够容纳 src 的内容。 如果 src 的长度大于 dest 分配的空间,会导致缓冲区溢出,这是非常严重的安全漏洞。务必确保 dest 的空间足够大。

安全替代品strncpy 或非标准但更安全的 strlcpy(在某些系统中可用)。

示例

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

int main() {
char src[] = "Hello World";
char dest[20]; // 确保目标数组足够大!

strcpy(dest, src);
printf("Copied string: %s\n", dest); // 输出 "Hello World"
return 0;
}

strncpy - 受限的字符串复制

功能:将源字符串的前 n 个字符复制到目标数组。如果源字符串的长度小于 n,则会用空字符('\0')填充剩余空间。

原型

1
char *strncpy(char *dest, const char *src, size_t n);

参数

  • dest:目标数组。
  • src:源字符串。
  • n:最多复制的字符数。

返回值

  • 返回目标字符串的指针 dest

重要特点与陷阱

  • 它可能不会在目标字符串的末尾自动添加 '\0'。如果源字符串的前 n 个字符中没有 '\0',那么 dest 中的结果就不会以 '\0' 结尾。这是一个常见的错误来源。
  • 通常需要手动确保字符串以 '\0' 结尾,例如:dest[n-1] = '\0';

示例

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

int main() {
char src[] = "Hello";
char dest[10];

strncpy(dest, src, sizeof(dest));
// 手动确保终止符,因为sizeof(dest)=10 > strlen(src)=5,所以是安全的
dest[sizeof(dest) - 1] = '\0';

printf("Copied string: %s\n", dest);
return 0;
}

strcat - 字符串连接

功能:将源字符串 src 的一个副本追加到目标字符串 dest 的末尾(覆盖 dest 原有的终止空字符 '\0'),并在新字符串的末尾添加一个新的 '\0'

原型

1
char *strcat(char *dest, const char *src);

参数

  • dest:目标数组,必须包含一个有效的 C 字符串,并且有足够的空间容纳连接后的结果。
  • src:要追加的源字符串。

返回值

  • 返回目标字符串的指针 dest

重要警告

  • strcpy 一样,它不检查目标数组的剩余空间是否足够。必须由程序员保证 dest 有足够的空间容纳 src 和原来的 dest 以及一个额外的 '\0'。否则会导致缓冲区溢出。

安全替代品strncat

示例

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

int main() {
char dest[20] = "Hello"; // 注意:dest必须足够大且是有效的字符串
char src[] = " World!";

strcat(dest, src);
printf("Concatenated string: %s\n", dest); // 输出 "Hello World!"
return 0;
}

strncat - 受限的字符串连接

功能:将源字符串的前 n 个字符追加到目标字符串的末尾,并总是添加一个终止空字符 '\0'

原型

1
char *strncat(char *dest, const char *src, size_t n);

参数

  • dest:目标数组。
  • src:源字符串。
  • n:最多追加的字符数。

返回值

  • 返回目标字符串的指针 dest

重要特点

  • strcat 更安全,因为它限制了追加的字符数。
  • 它总是会在结果字符串的末尾添加一个 '\0',所以最终字符串 dest 的长度最多增加 n 个字符(n+1 个字节,包括 '\0')。这是它与 strncpy 的一个重要区别,也更不容易出错。

示例

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

int main() {
char dest[20] = "Hello";
char src[] = " My Friend!";

// 只追加最多 4 个字符(" My "),并自动添加 '\0'
strncat(dest, src, 4);
printf("Concatenated string: %s\n", dest); // 输出 "Hello My "
return 0;
}

strcmp - 字符串比较

功能:按字典顺序(ASCII 码顺序)比较两个字符串。

原型

1
int strcmp(const char *str1, const char *str2);

参数

  • str1, str2:要比较的两个字符串。

返回值

  • 如果 str1 小于 str2,则返回一个负整数
  • 如果 str1 等于 str2,则返回 0
  • 如果 str1 大于 str2,则返回一个正整数

比较规则
逐个字符比较它们的 ASCII 值,直到遇到不同的字符或遇到 '\0' 为止。

示例

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

int main() {
char str1[] = "apple";
char str2[] = "banana";

int result = strcmp(str1, str2);

if (result < 0) {
printf("'%s' is less than '%s'\n", str1, str2);
} else if (result > 0) {
printf("'%s' is greater than '%s'\n", str1, str2);
} else {
printf("'%s' is equal to '%s'\n", str1, str2);
}
// 输出:'apple' is less than 'banana'
return 0;
}

strncmp - 受限的字符串比较

功能:比较两个字符串的前 n 个字符。

原型

1
int strncmp(const char *str1, const char *str2, size_t n);

参数

  • str1, str2:要比较的字符串。
  • n:要比较的最大字符数。

返回值

  • strcmp 类似,返回负整数、0 或正整数。

示例

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

int main() {
// 比较前 3 个字符
int result = strncmp("ABC123", "ABC456", 3); // 只比较 "ABC" 和 "ABC"
printf("Result: %d\n", result); // 输出 0 (相等)

result = strncmp("ABC123", "ABC456", 6); // 比较到字符 '1' 和 '4' 不同
printf("Result: %d\n", result); // 输出一个负数 ('1' < '4')
return 0;
}

strchr - 查找字符第一次出现的位置

功能:在字符串 str 中查找第一次出现字符 c(转换为 char)的位置。

原型

1
char *strchr(const char *str, int c);

参数

  • str:要被搜索的字符串。
  • c:要查找的字符(以 int 形式传递,但内部会转换为 char)。

返回值

  • 如果找到,返回指向该字符的指针
  • 如果未找到,返回 NULL

示例

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

int main() {
char str[] = "Hello World";
char ch = 'W';

char *result = strchr(str, ch);
if (result != NULL) {
printf("Found '%c' at position: %ld\n", ch, result - str); // 计算索引
printf("The rest of string is: %s\n", result);
} else {
printf("'%c' not found.\n", ch);
}
// 输出:Found 'W' at position: 6
// The rest of string is: World
return 0;
}

strstr - 查找子字符串第一次出现的位置

功能:在字符串 haystack 中查找第一次出现子字符串 needle 的位置。

原型

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

参数

  • haystack:要被搜索的主字符串。
  • needle:要查找的子字符串。

返回值

  • 如果找到,返回指向子字符串起始位置的指针。
  • 如果未找到,返回 NULL

示例

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

int main() {
char haystack[] = "This is a simple string";
char needle[] = "simple";

char *result = strstr(haystack, needle);
if (result != NULL) {
printf("Found '%s' at position: %ld\n", needle, result - haystack);
printf("The rest of string is: %s\n", result);
} else {
printf("'%s' not found.\n", needle);
}
return 0;
}

总结与安全提示

函数 功能 主要风险
strlen 求长度 如果字符串无 '\0',则导致未定义行为
strcpy 复制 缓冲区溢出
strncpy 受限复制 可能不以 '\0' 结尾
strcat 连接 缓冲区溢出
strncat 受限连接 相对安全,但仍需注意总长度
strcmp 比较
strncmp 受限比较
strchr 查找字符
strstr 查找子串

最佳实践

  1. 始终检查目标缓冲区的大小:在使用 strcpy, strcat 等函数之前,确保目标数组足够大。使用 sizeof(dest) 来获取数组的实际大小。
  2. 优先使用“n”系列函数:如使用 strncpy, strncat 代替它们的旧版本,并正确处理终止符(特别是对于 strncpy)。
  3. 考虑使用更安全的替代函数:在一些平台或编译环境下,可以考虑使用非标准但更安全的函数,如 strlcpystrlcat
  4. 始终检查以空字符结尾:确保操作的字符串都是有效的、以 '\0' 结尾的 C 字符串。