3.数组基础

4.1 冒泡排序

冒泡排序是一种简单的排序算法,其基本思想是重复遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就交换它们的位置。遍历数列的工作重复进行直到没有再需要交换。

冒泡排序算法

步骤:

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。完成后,最后的元素就是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

示例代码:

#include <stdio.h>

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        for (int j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                // 交换 arr[j] 和 arr[j+1]
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr)/sizeof(arr[0]);
    bubbleSort(arr, n);
    printf("Sorted array: \n");
    printArray(arr, n);
    return 0;
}

输出:

Sorted array: 
11 12 22 25 34 64 90 

4.2 memset

memset 是一个标准C库函数,用于设置一块内存区域中的所有字节为指定的值。它的原型在头文件 string.h 中声明:

void *memset(void *s, int c, size_t n);

参数说明

  • s:指向要填充的内存块的指针。
  • c:要设置的值。注意,该值会被转换为无符号字符,然后用于填充内存块。
  • n:要填充的字节数。

返回值

  • memset 返回指向内存块 s 的指针。

基本用法

memset 通常用于初始化数组或结构体,或将一块内存区域清零。

示例1:初始化字符数组

#include <stdio.h>
#include <string.h>

int main() {
    char buffer[10];
    
    // 将 buffer 的前 10 个字节设置为字符 'A'
    memset(buffer, 'A', sizeof(buffer));
    
    // 打印 buffer 内容
    for (int i = 0; i < sizeof(buffer); i++) {
        printf("%c ", buffer[i]);
    }
    printf("\n");
    
    return 0;
}

输出:

A A A A A A A A A A 

示例2:清零整数数组

#include <stdio.h>
#include <string.h>

int main() {
    int numbers[10];
    
    // 将 numbers 数组的内存区域设置为 0
    memset(numbers, 0, sizeof(numbers));
    
    // 打印 numbers 数组内容
    for (int i = 0; i < 10; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    return 0;
}

输出:

0 0 0 0 0 0 0 0 0 0 

示例3:初始化结构体

#include <stdio.h>
#include <string.h>

struct Data {
    int id;
    char name[20];
    float value;
};

int main() {
    struct Data item;
    
    // 将结构体 item 的内存区域清零
    memset(&item, 0, sizeof(item));
    
    // 打印结构体成员的初始值
    printf("id: %d, name: %s, value: %.2f\n", item.id, item.name, item.value);
    
    return 0;
}

输出:

id: 0, name: , value: 0.00

注意事项

  1. 值转换:参数 c 被转换为无符号字符,因此它的实际效果是将每个字节设置为 c 的最低 8 位。这意味着,如果 c 超过了 0-255 的范围,高位部分将被忽略。
  2. 性能考虑memset 通常是非常高效的,因为它是一个库函数,通常会针对特定硬件进行优化。
  3. 安全性:确保 n 不超过目标内存区域的大小,否则会导致缓冲区溢出,可能引发严重的错误。

4.3 字符数组

在C语言中,scanf函数用于从标准输入读取数据,并根据指定的格式存储到变量中。不同的格式说明符(如 %c%s)在处理输入时有不同的行为。

4.3.1scanf 中的 %c 格式说明符

  • %c 格式说明符用于读取单个字符。
  • 它可以读取包括空格、换行符在内的任何字符。
  • 如果前面没有添加空格或其他特殊处理,%c 将读取输入缓冲区中的下一个字符,不会跳过空白字符。

示例1:读取单个字符

#include <stdio.h>

int main() {
    char ch;

    printf("Enter a character: ");
    scanf("%c", &ch);
    
    printf("You entered: '%c'\n", ch);

    return 0;
}

示例2:读取多个字符,包括空格和换行符

#include <stdio.h>

int main() {
    char ch1, ch2;

    printf("Enter two characters separated by a space: ");
    scanf("%c%c", &ch1, &ch2);
    
    printf("You entered: '%c' and '%c'\n", ch1, ch2);

    return 0;
}

4.3.2 scanf 中的 %s 格式说明符

  • %s 格式说明符用于读取字符串。
  • 它会自动跳过任何前导空白字符,并从第一个非空白字符开始读取。
  • %s 读取字符串时,以空格、换行符或制表符作为分隔符,并在遇到这些分隔符时结束读取。

示例3:读取字符串

#include <stdio.h>

int main() {
    char str[100];

    printf("Enter a string: ");
    scanf("%s", str);
    
    printf("You entered: '%s'\n", str);

    return 0;
}

结合使用

有时我们需要结合使用 %c%s 格式说明符来处理更复杂的输入场景。

示例4:读取包含空格的字符串

如果需要读取包含空格的字符串,可以使用 fgets 函数,而不是 scanffgets 可以读取整行输入,包括空格和换行符。

#include <stdio.h>

int main() {
    char str[100];

    printf("Enter a string: ");
    fgets(str, sizeof(str), stdin);
    
    // 去掉字符串末尾的换行符(如果有)
    str[strcspn(str, "\n")] = '\0';
    
    printf("You entered: '%s'\n", str);

    return 0;
}

处理输入中的空白字符

当使用 scanf 读取字符时,如果希望跳过空白字符,可以在格式说明符前添加一个空格。

示例5:跳过空白字符

#include <stdio.h>

int main() {
    char ch;

    printf("Enter a character: ");
    scanf(" %c", &ch);  // 在%c前加一个空格,跳过空白字符
    
    printf("You entered: '%c'\n", ch);

    return 0;
}

组合示例

以下示例展示了如何读取字符和字符串,并处理输入中的空白字符:

#include <stdio.h>

int main() {
    char ch;
    char str[100];

    printf("Enter a character: ");
    scanf(" %c", &ch);  // 跳过前导空白字符,读取单个字符
    printf("You entered: '%c'\n", ch);

    // 清空输入缓冲区,确保后续输入不受前一次输入影响
    while (getchar() != '\n');

    printf("Enter a string: ");
    fgets(str, sizeof(str), stdin);
    str[strc

spn(str, "\n")] = '\0';  // 去掉换行符
    printf("You entered: '%s'\n", str);

    return 0;
}

4.3.3 gets 输入,puts 输出

  • gets 用来输入一行字符串,以换行符作为输入结束。
  • puts 输出结束后自动跟上一个换行符。

对比

下面是一个表格详细描述了 getsgetcharscanf 的结束方式:

函数 描述 结束条件 是否包含空白字符
gets 从标准输入读取一行 读取到换行符('\n'
getchar 从标准输入读取一个字符 读取到一个字符
scanf 从标准输入读取指定格式的数据 取决于格式说明符和输入 取决于格式说明符

详细说明

  1. gets

    • 描述:从标准输入读取一行字符串,直到遇到换行符。gets 会将换行符替换为字符串终止符('\0')。
    • 结束条件:遇到换行符('\n')。
    • 是否包含空白字符:包含。gets 会读取空白字符(如空格、制表符)作为字符串的一部分。
    • 注意gets 函数由于无法控制输入长度,容易引发缓冲区溢出问题,已在C11标准中被弃用。推荐使用 fgets 代替。
  2. getchar

    • 描述:从标准输入读取一个字符并返回。
    • 结束条件:读取到一个字符。
    • 是否包含空白字符:包含。getchar 会读取任何字符,包括空白字符(如空格、换行符、制表符)。
  3. scanf

    • 描述:从标准输入读取格式化输入数据。
    • 结束条件:取决于格式说明符和输入。scanf 根据格式说明符解析输入,并在遇到与格式不匹配的字符或空白字符时停止。
    • 是否包含空白字符:取决于格式说明符。
      • %c:读取单个字符,包括空白字符。
      • %s:读取字符串,遇到空白字符(如空格、换行符、制表符)时结束。
      • 其他(如 %d, %f):根据输入格式,空白字符会被跳过。

示例代码

gets(不推荐使用)
#include <stdio.h>

int main() {
    char str[100];

    printf("Enter a string: ");
    gets(str);
    printf("You entered: '%s'\n", str);

    return 0;
}
fgets(推荐使用)
#include <stdio.h>

int main() {
    char str[100];

    printf("Enter a string: ");
    fgets(str, sizeof(str), stdin);
    str[strcspn(str, "\n")] = '\0';  // 去掉换行符
    printf("You entered: '%s'\n", str);

    return 0;
}
getchar
#include <stdio.h>

int main() {
    char ch;

    printf("Enter a character: ");
    ch = getchar();
    printf("You entered: '%c'\n", ch);

    return 0;
}
scanf

读取单个字符

#include <stdio.h>

int main() {
    char ch;

    printf("Enter a character: ");
    scanf(" %c", &ch);  // 跳过前导空白字符
    printf("You entered: '%c'\n", ch);

    return 0;
}

读取字符串

#include <stdio.h>

int main() {
    char str[100];

    printf("Enter a string: ");
    scanf("%s", str);  // 读取字符串,遇到空白字符结束
    printf("You entered: '%s'\n", str);

    return 0;
}

4.3.4 字符数组存放方式

在C语言中,字符数组(即字符串)的存储方式有几种重要的特性需要注意,其中最重要的是以空字符(\0)作为字符串的结束标志。

结束符 \0

在C语言中,字符串实际上是以空字符 \0 结尾的字符数组。这种结尾字符用于标识字符串的结束,这对于字符串操作函数(如 strlenstrcpystrcat 等)非常重要,因为这些函数依赖 \0 来判断字符串的边界。

示例

#include <stdio.h>

int main() {
    // 定义一个字符数组并初始化为一个字符串
    char str[] = "Hello, World!";
    
    // 手动查看字符数组的每个元素
    for (int i = 0; i < sizeof(str); i++) {
        printf("str[%d] = '%c' (ASCII: %d)\n", i, str[i], str[i]);
    }
    
    return 0;
}

输出:

str[0] = 'H' (ASCII: 72)
str[1] = 'e' (ASCII: 101)
str[2] = 'l' (ASCII: 108)
str[3] = 'l' (ASCII: 108)
str[4] = 'o' (ASCII: 111)
str[5] = ',' (ASCII: 44)
str[6] = ' ' (ASCII: 32)
str[7] = 'W' (ASCII: 87)
str[8] = 'o' (ASCII: 111)
str[9] = 'r' (ASCII: 114)
str[10] = 'l' (ASCII: 108)
str[11] = 'd' (ASCII: 100)
str[12] = '!' (ASCII: 33)
str[13] = '\0' (ASCII: 0)

注意事项

  1. 分配空间:定义字符数组时,必须为 \0 预留空间。例如,要存储 “Hello” 字符串,需要一个长度为 6 的字符数组,因为它需要一个额外的空间来存储 \0

  2. 初始化:初始化字符数组时,如果使用字符串常量初始化,编译器会自动添加 \0。例如,char str[] = "Hello"; 自动会在字符串的末尾添加 \0

  3. 操作字符串:使用字符串处理函数时,必须确保字符串以 \0 结束,否则会导致未定义行为,因为这些函数会一直读取,直到找到 \0

手动添加 \0

有时候需要手动操作字符数组,在这种情况下,必须确保在适当的位置添加 \0 以保证字符串正确结束。

示例:

#include <stdio.h>

int main() {
    char str[6];  // 为 "Hello" 字符串分配空间,包含 \0
    str[0] = 'H';
    str[1] = 'e';
    str[2] = 'l';
    str[3] = 'l';
    str[4] = 'o';
    str[5] = '\0';  // 手动添加 \0 结束符
    
    printf("String is: %s\n", str);
    
    return 0;
}

输出:

String is: Hello

注意

如果不使用scanf%s或者gets函数输入字符串,如使用getchar输入,则一定要输入的每个字符串后面加‘/0’,否则printf和puts输出字符串会因为无法识别字符串末尾二输出一大堆乱码

示例代码:使用 getchar 输入字符串

#include <stdio.h>

int main() {
    char str[100];
    int i = 0;
    char ch;

    printf("Enter a string: ");
    while (i < sizeof(str) - 1) { // 确保不超出字符数组的大小
        ch = getchar();
        if (ch == '\n' || ch == EOF) {
            break; // 遇到换行符或文件结束符时结束输入
        }
        str[i++] = ch;
    }
    str[i] = '\0'; // 手动添加字符串结束符

    printf("You entered: '%s'\n", str);
    puts(str); // 使用 puts 输出字符串

    return 0;
}

详细步骤

  1. 定义字符数组char str[100]; 定义一个大小为 100 的字符数组,用于存储输入的字符串。
  2. 逐字符读取输入:使用 getchar() 逐字符读取输入,直到遇到换行符或文件结束符(EOF)。
  3. 添加字符串结束符:在字符数组的最后一个有效字符位置后添加 \0 结束符,确保字符串正确结束。
  4. 输出字符串:使用 printfputs 输出字符串。

关键点

  • 数组大小检查:在读取输入时,检查数组的大小,确保不会超出数组边界,避免缓冲区溢出。
  • 手动添加 \0 结束符:在输入结束后,手动在数组最后添加字符串结束符 \0,确保字符串正确结束。
  • 处理特殊字符:处理换行符(\n)和文件结束符(EOF),确保输入正确结束。

示例代码:处理空格的字符串输入

如果需要处理输入中包含空格的字符串,可以使用以下方法:

#include <stdio.h>

int main() {
    char str[100];
    int i = 0;
    char ch;

    printf("Enter a string (including spaces): ");
    while (i < sizeof(str) - 1) {
        ch = getchar();
        if (ch == '\n' || ch == EOF) {
            break;
        }
        str[i++] = ch;
    }
    str[i] = '\0'; // 添加字符串结束符

    printf("You entered: '%s'\n", str);
    puts(str);

    return 0;
}

示例输出

Enter a string (including spaces): Hello, World!
You entered: 'Hello, World!'
Hello, World!

通过正确处理输入字符和手动添加字符串结束符,可以确保使用 getchar 进行字符串输入时的正确性和安全性。

4.4 string.h 头文件

4.4.1 strlen()

strlen 函数用于计算字符串的长度(不包括末尾的空字符)。它的原型在头文件 string.h 中声明:

size_t strlen(const char *str);

参数说明

  • str:指向以空字符 \0 结尾的字符串。

返回值

  • 返回字符串的长度(不包括末尾的空字符)。

示例代码

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";
    size_t len = strlen(str);
    
    printf("Length of the string '%s' is: %zu\n", str, len);
    
    return 0;
}

输出:

Length of the string 'Hello, World!' is: 13

注意事项

  • strlen 不计算字符串末尾的空字符 \0
  • 如果传递给 strlen 的字符串不是以空字符结尾的,将会引发未定义行为。

4.4.2 sscanf与sprintf

sscanf == string + scanf
sprintf == string + printf

相关推荐

  1. 3.数组基础

    2024-07-11 10:34:02       16 阅读
  2. Vue基础3)监听数据

    2024-07-11 10:34:02       25 阅读
  3. go 语言程序设计第3章--基础数据类型

    2024-07-11 10:34:02       50 阅读
  4. 学习大数据,所需要的SQL基础3

    2024-07-11 10:34:02       39 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 10:34:02       53 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 10:34:02       56 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 10:34:02       46 阅读
  4. Python语言-面向对象

    2024-07-11 10:34:02       57 阅读

热门阅读

  1. Docker 日志丢失 - 解决方案

    2024-07-11 10:34:02       17 阅读
  2. 3D Web开发新篇章:threelab探索之旅

    2024-07-11 10:34:02       18 阅读
  3. 外科休克病人的护理

    2024-07-11 10:34:02       16 阅读
  4. axios get 请求发送 FormData 数据

    2024-07-11 10:34:02       21 阅读
  5. 数据库的更新方式有哪些

    2024-07-11 10:34:02       19 阅读
  6. VSCode, 请在windows下使用git bash终端

    2024-07-11 10:34:02       21 阅读
  7. R 数据重塑

    2024-07-11 10:34:02       14 阅读