c语言中字符串值得注意的问题

一、如何逆转字符串

一下是逆转字符串可以使用的方法

方法1:使用临时变量交换字符

#include 
#include 

void reverseString(char* str) {
    int length = strlen(str);
    for (int i = 0; i < length / 2; i++) {
        char temp = str[i];
        str[i] = str[length - i - 1];
        str[length - i - 1] = temp;
    }
}

int main() {
    char str[] = "Hello, World!";
    printf("Original string: %s\n", str);
    
    reverseString(str);
    printf("Reversed string: %s\n", str);
    
    return 0;
}

方法2:使用指针交换字符

#include 
#include 

void reverseString(char* str) {
    char* start = str;
    char* end = str + strlen(str) - 1;
    
    while (start < end) {
        char temp = *start;
        *start = *end;
        *end = temp;
        
        start++;
        end--;
    }
}

int main() {
    char str[] = "Hello, World!";
    printf("Original string: %s\n", str);
    
    reverseString(str);
    printf("Reversed string: %s\n", str);
    
    return 0;
}

方法3:使用递归(不推荐用于长字符串)

#include 
#include 

void reverseStringRecursive(char* str, int start, int end) {
    if (start >= end) return;
    
    char temp = str[start];
    str[start] = str[end];
    str[end] = temp;
    
    reverseStringRecursive(str, start + 1, end - 1);
}

void reverseString(char* str) {
    reverseStringRecursive(str, 0, strlen(str) - 1);
}

int main() {
    char str[] = "Hello, World!";
    printf("Original string: %s\n", str);
    
    reverseString(str);
    printf("Reversed string: %s\n", str);
    
    return 0;
}

上面三种方法都是用函数体的方法解决的

方法1:使用临时变量交换字符(在main中直接实现)

#include 
#include 

int main() {
    char str[] = "Hello, World!";
    printf("Original string: %s\n", str);
    
    int length = strlen(str);
    for (int i = 0; i < length / 2; i++) {
        char temp = str[i];
        str[i] = str[length - i - 1];
        str[length - i - 1] = temp;
    }
    
    printf("Reversed string: %s\n", str);
    return 0;
}

方法2:使用指针交换字符(在main中直接实现)

#include 
#include 

int main() {
    char str[] = "Hello, World!";
    printf("Original string: %s\n", str);
    
    char *start = str;
    char *end = str + strlen(str) - 1;
    
    while (start < end) {
        char temp = *start;
        *start = *end;
        *end = temp;
        
        start++;
        end--;
    }
    
    printf("Reversed string: %s\n", str);
    return 0;
}

方法3:不使用string.h库函数

如果你不想使用strlen()函数,也可以这样实现:

#include 

int main() {
    char str[] = "Hello, World!";
    printf("Original string: %s\n", str);
    
    // 先计算字符串长度
    int length = 0;
    while (str[length] != '\0') {
        length++;
    }
    
    // 逆转字符串
    for (int i = 0; i < length / 2; i++) {
        char temp = str[i];
        str[i] = str[length - i - 1];
        str[length - i - 1] = temp;
    }
    
    printf("Reversed string: %s\n", str);
    return 0;
}

方法4:使用数组下标直接操作

#include 

int main() {
    char str[] = "Hello, World!";
    printf("Original string: %s\n", str);
    
    int i = 0, j = 0;
    // 找到字符串末尾
    while (str[j] != '\0') j++;
    j--; // 指向最后一个字符
    
    // 交换字符
    while (i < j) {
        char temp = str[i];
        str[i] = str[j];
        str[j] = temp;
        i++;
        j--;
    }
    
    printf("Reversed string: %s\n", str);
    return 0;
}

首先我要解释的是striing.h库函数中的strlen函数,这是用来计算字符串长度的,strlen(这里面填字符串)这样就能计算出括号里面字符串的长度了。而strlen函数的返回值有几点需要我们注意:

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

  • 不包含 结尾的空字符 '\0'

  • 如果传入空指针(NULL),行为未定义(通常会导致程序崩溃

  • 对多字节字符返回的是字节数而非字符数

这里我们需要区分一下字节数和字符数:

  • 字节(Byte):计算机存储的基本单位,1字节=8比特(bit)

  • 字符(Character):人类可读的文字符号(如字母、数字、汉字等)

那么肯定又有好奇宝宝问了,明明我之前使用strlen函数时他输出的结果值都和字符数是对的上的啊,这怎么又不对了?且听我慢慢和你解释,我想你之前计算的肯定都是用ASCII编码的字符串吧。在ASCII编码中一个字符就等于一个字节所以你计算出来的结果当然和字符数是一样的。但是当你使用UTF-8中文来编码时你会发现一个中文汉字占三个字节那么你肯定最后你所计算出的结果一定就不等于字符数了。

其次我要解释一下这串代码 for (int i = 0; i < length / 2; i++) { char temp = str[i]; str[i] = str[length - i - 1]; str[length - i - 1] = temp; }为这么这里是i逆转字符串只需要把前面半段和后面半段调换位置即可所以我们需要将length除以二。然后这个 str[length - i - 1]就是找出数组第一个元素所对应的最后一个元素,第二个元素所对应的倒数第二个元素的一个计算式子,需要注意的是数组是从零开始的而不是一

然后肯定就有好奇宝宝要问了,如果我想要那个字符串是我自己输入的,我想让他是什么就是什么那我要怎么弄呢?那么下面就是让你自己循环输入字符串的方法。

方法1:使用fgets()循环输入(推荐)

#include 
#include 

#define MAX_LEN 100  // 定义最大长度

int main() {
    char str[MAX_LEN];
    
    while(1) {
        printf("请输入字符串(输入quit退出): ");
        fgets(str, MAX_LEN, stdin);
        
        // 去除换行符
        str[strcspn(str, "\n")] = '\0';
        
        // 检查退出条件
        if(strcmp(str, "quit") == 0) {
            break;
        }
        
        printf("你输入的是: %s\n", str);
        printf("长度为: %zu\n", strlen(str));
    }
    
    return 0;
}

方法2:使用scanf()循环输入

#include 

#define MAX_LEN 100

int main() {
    char str[MAX_LEN];
    
    while(1) {
        printf("请输入字符串(输入quit退出): ");
        scanf("%99s", str);  // 限制读取长度
        
        if(strcmp(str, "quit") == 0) {
            break;
        }
        
        printf("你输入的是: %s\n", str);
    }
    
    return 0;
}

方法3:动态内存分配(处理不定长字符串)

#include 
#include 
#include 

int main() {
    char *str = NULL;
    size_t len = 0;
    
    while(1) {
        printf("请输入字符串(输入quit退出): ");
        ssize_t read = getline(&str, &len, stdin);
        
        if(read == -1) break;
        
        // 去除换行符
        if(read > 0 && str[read-1] == '\n') {
            str[read-1] = '\0';
        }
        
        if(strcmp(str, "quit") == 0) {
            free(str);
            break;
        }
        
        printf("你输入的是: %s\n", str);
    }
    
    return 0;
}

方法4:处理多行输入直到空行

#include 
#include 

#define MAX_LEN 1000

int main() {
    char line[MAX_LEN];
    
    printf("请输入多行文本(空行结束):\n");
    
    while(1) {
        if(fgets(line, MAX_LEN, stdin) == NULL) break;
        
        // 检查是否为空行(只有换行符)
        if(strcmp(line, "\n") == 0) break;
        
        // 去除换行符
        line[strcspn(line, "\n")] = '\0';
        
        printf("读取到: %s\n", line);
    }
    
    return 0;
}

方法1:固定次数的循环输入

#include 
#include 

#define MAX_LEN 100
#define NUM_STRINGS 5  // 要输入的字符串数量

int main() {
    char strings[NUM_STRINGS][MAX_LEN];
    
    // 使用for循环输入固定数量的字符串
    for(int i = 0; i < NUM_STRINGS; i++) {
        printf("请输入第%d个字符串(最多%d字符): ", i+1, MAX_LEN-1);
        fgets(strings[i], MAX_LEN, stdin);
        
        // 去除换行符
        strings[i][strcspn(strings[i], "\n")] = '\0';
    }
    
    // 打印所有输入的字符串
    printf("\n您输入的字符串:\n");
    for(int i = 0; i < NUM_STRINGS; i++) {
        printf("%d: %s\n", i+1, strings[i]);
    }
    
    return 0;
}

方法2:带条件的for循环输入

#include 
#include 
#include 

#define MAX_LEN 100

int main() {
    char str[MAX_LEN];
    int continue_input = 1;  // 控制循环的标志
    
    // 使用for循环实现带条件的输入
    for(int i = 1; continue_input; i++) {
        printf("请输入第%d个字符串(输入'quit'退出): ", i);
        fgets(str, MAX_LEN, stdin);
        
        // 去除换行符
        str[strcspn(str, "\n")] = '\0';
        
        // 检查退出条件
        if(strcmp(str, "quit") == 0) {
            continue_input = 0;  // 终止循环
        } else {
            printf("您输入了: %s\n\n", str);
        }
    }
    
    return 0;
}

方法3:使用for循环处理字符串数组

#include 
#include 

#define MAX_STRINGS 10
#define MAX_LENGTH 50

int main() {
    char strings[MAX_STRINGS][MAX_LENGTH];
    int actual_count = 0;
    
    // 使用for循环但提前退出的方式
    for(int i = 0; i < MAX_STRINGS; i++) {
        printf("输入字符串%d/%d(空行结束): ", i+1, MAX_STRINGS);
        fgets(strings[i], MAX_LENGTH, stdin);
        
        // 检查是否为空行
        if(strings[i][0] == '\n') {
            strings[i][0] = '\0';  // 设置为空字符串
            break;
        }
        
        // 去除换行符
        strings[i][strcspn(strings[i], "\n")] = '\0';
        actual_count++;
    }
    
    // 输出结果
    printf("\n有效输入的字符串(%d个):\n", actual_count);
    for(int i = 0; i < actual_count; i++) {
        printf("%d: %s\n", i+1, strings[i]);
    }
    
    return 0;
}

方法4:使用for循环和动态内存分配

#include 
#include 
#include 

#define INITIAL_SIZE 5

int main() {
    char **strings = NULL;
    int capacity = INITIAL_SIZE;
    int count = 0;
    
    strings = (char **)malloc(capacity * sizeof(char *));
    if(!strings) {
        perror("内存分配失败");
        return 1;
    }
    
    printf("请输入字符串(每行一个,空行结束):\n");
    
    for(;;) {
        char buffer[256];
        if(fgets(buffer, sizeof(buffer), stdin) == NULL || buffer[0] == '\n') {
            break;
        }
        
        // 去除换行符
        buffer[strcspn(buffer, "\n")] = '\0';
        
        // 检查是否需要扩容
        if(count >= capacity) {
            capacity *= 2;
            char **temp = (char **)realloc(strings, capacity * sizeof(char *));
            if(!temp) {
                perror("内存重新分配失败");
                break;
            }
            strings = temp;
        }
        
        // 分配内存并复制字符串
        strings[count] = strdup(buffer);
        if(!strings[count]) {
            perror("字符串复制失败");
            break;
        }
        count++;
    }
    
    // 输出结果
    printf("\n输入的字符串(%d个):\n", count);
    for(int i = 0; i < count; i++) {
        printf("%d: %s\n", i+1, strings[i]);
        free(strings[i]);  // 释放每个字符串
    }
    free(strings);  // 释放指针数组
    
    return 0;
}

在上述代码中我们看见fgets,其实这个函数的作用和scanf是差不多的,但是为什么我们不用scanf而是用的 fgets他呢?是我的scanf不香了吗?还请听我细细说来。

在这里我们使用scanf函数是有很多风险的,而如果使用fgets函数就能让代码更加安全。

1. 缓冲区溢出风险(最主要问题)

char str[10];
scanf("%s", str);  // 危险!
  • 问题:如果用户输入超过9个字符(因为需要1个字符留给'\0'),会导致缓冲区溢出

  • 后果:可能覆盖相邻内存,导致程序崩溃或安全漏洞(如栈溢出攻击)

2. 无法限制输入长度

  • %s 格式说明符没有内置的长度限制

  • 即使声明了 char str[10],用户输入"123456789012345"仍然会被完整写入内存

3. 不会处理空格

  • %s 遇到空格、制表符或换行符就会停止读取

  • 例如输入 "hello world" 只会读取 "hello"

4. 输入缓冲区问题

  • 如果输入过长,多余字符会留在输入缓冲区

  • 可能影响后续的输入操作

但是肯定就有和我一样的犟种说了,我就是要用scanf你能不能让我安全的使用scanf呢,能当然能啊。下面就是安全使用scanf的方法

方案1:使用字段宽度限制(推荐)

char str[10];
scanf("%9s", str);  // 最多读取9个字符+自动添加'\0'

方案3:使用 scanf 的高级格式

char str[10];
scanf(" %9[^\n]", str);  // 读取直到换行符,最多9字符

一下是一些小小的建议

  1. 永远不要使用无长度限制的 scanf("%s", str)

  2. 优先使用 fgets + sscanf 的组合

  3. 如果必须用 scanf,一定要指定最大长度:

你可能感兴趣的:(c语言,算法,数据结构)