C 字符串
C - 字符串
Section titled “C - 字符串”在 C 语言中,字符串(string)不是像 int 或 float 那样内建的基本类型(built-in fundamental type)。相反,字符串通常被表示为一个字符数组(array of characters),并由一个特殊的空字符(null character),即 ‘\0’(其 ASCII 值为 0)终止。
这通常被称为“空终止字符串”(“null-terminated string”)或“C 风格字符串”(“C-style string”)。空字符(null character)标志着字符串数据在数组中的结束位置。
声明和初始化字符串
Section titled “声明和初始化字符串”有几种方法可以声明和初始化字符串:
**1. 使用字符串字面量(String Literal):**这是最常见的方法。编译器会自动为字符内容加上空终止符(null terminator)分配足够的空间。
char message[] = "Hello"; // Compiler allocates 6 chars ('H','e','l','l','o','\0')char *str_ptr = "World"; // Creates a string literal (often in read-only memory), // str_ptr points to its first character.**警告(Warning):**尝试修改由指针(pointer)指向的字符串字面量(如 str_ptr[0] = ‘J’;)会导致未定义行为(undefined behavior)(通常是程序崩溃)。
**2. 指定大小并使用字符串字面量:**确保指定的大小足够大,能容纳字符串内容以及空终止符。
char greeting[10] = "Hi"; // Allocates 10 chars. Initialized to 'H','i','\0'. // Remaining 7 chars are initialized to '\0'.3. 使用字符初始化列表:如果使用此方法,你必须明确包含空终止符。
char word[4] = {'b', 'y', 'e', '\0'}; // Correctly null-terminatedchar bad_word[3] = {'o', 'o', 'p'}; // NOT a valid C string! Missing '\0'.字符串输入和输出
Section titled “字符串输入和输出”标准输入/输出库(<stdio.h>)提供了用于处理字符串的函数:
- **
printf函数配合%s:**打印一个空终止字符串(null-terminated string)。 - **
scanf函数配合%s:**读取一串非空白字符到一个字符数组中。**警告:**极其不安全,因为它不检查缓冲区边界(buffer boundaries)。容易导致缓冲区溢出(buffer overflows)。避免不指定宽度限制地使用scanf(“%s”, …)。 - **
fgets函数:**从流(stream)(如stdin)中读取一行文本(包括空格,直到换行符或达到指定限制)到一个字符数组中。比scanf安全得多,因为它将缓冲区大小(buffer size)作为参数。 - **
puts函数:**打印一个字符串后跟一个换行符。
使用 fgets 和 printf 的示例:
#include <stdio.h>#include <string.h> // For strcspn
#define BUFFER_SIZE 50
int main(void) { char name[BUFFER_SIZE];
printf("Enter your name: ");
// Safely read input using fgets if (fgets(name, BUFFER_SIZE, stdin) != NULL) { // Remove trailing newline character if present (fgets includes it) name[strcspn(name, "\n")] = 0;
printf("Hello, %s!\n", name); } else { printf("Error reading input.\n"); }
return 0;}常用字符串操作函数(<string.h>)
Section titled “常用字符串操作函数(<string.h>)”C 标准库在 <string.h> 中提供了一组用于常用字符串操作的函数。对不接受缓冲区大小参数的函数要非常小心,因为如果目标缓冲区不够大,它们很容易导致缓冲区溢出。
| 函数(Function) | 描述(Description) | 安全注意事项(Safety Considerations) |
|---|---|---|
strlen(s) | 返回字符串 s 的长度(不包括空终止符)。 | 如果 s 正确空终止,通常是安全的。 |
strcpy(dest, src) | 将字符串 src(包括 \0)复制到 dest 中。 | **不安全(UNSAFE)。**不检查 dest 是否足够大。容易导致缓冲区溢出。避免使用。 |
strncpy(dest, src, n) | 最多复制 n 个字符从 src 到 dest。 | 比 strcpy 安全,但有陷阱:如果 src 的长度 >= n,可能不会在 dest 中添加空终止符。总是手动添加空终止符:如果大小是 n,使用 dest[n-1] = ‘\0’;。 |
strcat(dest, src) | 将字符串 src 追加到字符串 dest 的末尾。 | **不安全(UNSAFE)。**不检查 dest 是否有足够的空间容纳合并后的字符串 + \0。避免使用。 |
strncat(dest, src, n) | 最多追加 n 个字符从 src 到 dest。会添加一个空终止符。 | 比 strcat 安全,但要确保 dest 缓冲区大小能容纳原始长度 + n + 1。 |
strcmp(s1, s2) | 按字典顺序比较字符串 s1 和 s2。如果相等返回 0,如果 s1<s2 返回 <0,如果 s1>s2 返回 >0。 | 如果两个字符串都正确空终止,通常是安全的。 |
strncmp(s1, s2, n) | 最多比较 s1 和 s2 的前 n 个字符。 | 用于比较可能未空终止的缓冲区或前缀的更安全版本。 |
strchr(s, c) | 返回字符 c 在字符串 s 中第一次出现位置的指针(pointer);如果未找到,返回 NULL。 | 如果 s 空终止,则是安全的。 |
strstr(haystack, needle) | 返回字符串 needle 在字符串 haystack 中第一次出现位置的指针;如果未找到,返回 NULL。 | 如果两个字符串都空终止,则是安全的。 |
**最佳实践(Best Practice):**尽可能使用包含大小参数的函数(如 strncpy, snprintf, fgets),并始终注意你的缓冲区大小以防止溢出。在使用不安全函数之前手动检查长度,或动态分配(dynamically allocate)足够的内存(memory)。
使用更安全实践的示例:
#include <stdio.h>#include <string.h>#include <stddef.h> // For size_t
int main(void) { char firstName[] = "Ada"; char lastName[] = "Lovelace"; char fullName[50]; // Ensure buffer is large enough size_t lenFirstName = strlen(firstName); size_t lenLastName = strlen(lastName); size_t bufferSize = sizeof(fullName);
// Use snprintf for safe concatenation (recommended) int written = snprintf(fullName, bufferSize, "%s %s", firstName, lastName);
if (written > 0 && (size_t)written < bufferSize) { printf("Full Name (snprintf): %s\n", fullName); printf("Length: %zu\n", strlen(fullName)); // Or use 'written' } else { fprintf(stderr, "Error creating full name or buffer too small.\n"); }
// --- Alternative using strncpy/strncat (more manual) --- char fullNameManual[50]; // Ensure space for first name, space, last name, null terminator if (lenFirstName + 1 + lenLastName + 1 <= sizeof(fullNameManual)) { strncpy(fullNameManual, firstName, sizeof(fullNameManual) - 1); fullNameManual[sizeof(fullNameManual) - 1] = '\0'; // Ensure null termination
strncat(fullNameManual, " ", sizeof(fullNameManual) - strlen(fullNameManual) - 1);
strncat(fullNameManual, lastName, sizeof(fullNameManual) - strlen(fullNameManual) - 1);
printf("Full Name (manual): %s\n", fullNameManual); printf("Length: %zu\n", strlen(fullNameManual)); } else { fprintf(stderr, "Manual buffer too small.\n"); }
return 0;}输出 (Output):
Full Name (snprintf): Ada LovelaceLength: 11Full Name (manual): Ada LovelaceLength: 11在 C 语言中,正确处理字符串至关重要,以避免安全漏洞(security vulnerabilities)和程序崩溃(program crashes)。