嵌入式学习日志——C语言(七)

7   学习字符数组和二维数组的定义和引用

1   字符数组(字符串处理)

1.1  基础概念

        1. 定义: char s[];  中  []  是类型说明符,用于文本处理,可存储字符串内容。定义时若直接赋值字符串(如  char s[] = "Hello";  ),系统默认在末尾追加  '\0'  作为字符串结束标志;若用字符逐个初始化(如  char s[] = {'H','e','l','l','o','\0'};  ),需手动加  '\0'  。

        2. 特性:字符串常量(如  "abcd123_20204045"  )会被编译为 ASCII 码,存储在只读区域,运行中不可修改,具有单引号(字符)、双引号(字符串)、结尾  '\0'  标识的特性。

1.2  字符串操作函数

1. 输出函数

        puts(const char *s)  :向标准输出打印字符串,自动换行。参数  s  是字符数组名(本质是指针,指向字符型变量 ),如  puts(s);  等价于  puts(&s[0]);  ,会从首地址开始输出,遇到  '\0'  停止。

while (*s != '\0')
{
    putchar(*s);
    s++;
}
putchar('\n');

2. 输入函数

        1)gets(char *s)  :从标准输入读入一行字符串,不检查越界,可能导致缓冲区溢出,C11 标准已弃用。

        2)fgets(char *s, int size, FILE *stream)  :更安全的输入函数, size  限制读入字符数(实际存  size - 1  个有效字符 +  '\0'  ), stream  一般填  stdin  (标准输入 )。会根据数组大小决定存储内容,若输入超长,剩余字符留在输入缓冲区。

        3)scanf("%s", s)  :读入字符串时,遇到空白字符(空格、换行、制表符 )停止,不存储  '\n'  ,且不检查越界,需确保数组空间足够。

3. 长度与比较

        1)strlen(const char *s)  :计算字符串长度,统计到  '\0'  为止(不包含  '\0'  ),返回值类型是  unsigned long  ,使用需包含头文件  <string.h>  。注意  sizeof(s)  计算的是数组总字节数(包含  '\0'  及未使用空间 ),与  strlen  结果不同。

        2)strcmp(const char *s1, const char *s2)  :比较两个字符串,按字典序逐字符对比 ASCII 码。返回值: s1 > s2  时为正数, s1 < s2  时为负数, s1 == s2  时为 0 。字符串比较不能直接用关系运算符(如  ==   >  ),需用  strcmp  ;给字符数组赋值也不能用赋值运算符(如  s2 = s1;  ),需用  strcpy  。

        示例(找较大字符串):

char max[100], s1[100], s2[100];
scanf("%s %s", s1, s2);
if (strcmp(s1, s2) > 0)
{
    strcpy(max, s1);
}
 else
{
    strcpy(max, s2);
}
puts(max);

4. 拷贝与拼接

        1)strcpy(char *dest, const char *src)  :将  src  字符串拷贝到  dest  数组,会覆盖  dest  原有内容,需保证  dest  空间足够大(至少能存  src  内容 +  '\0'  ),使用需包含  <string.h>  。

        简易实现逻辑:

while (*src != '\0')
{
    *dest = *src;
    dest++;
    src++;
}
*dest = '\0';

        2)strcat(char *dest, const char *src)  :将  src  字符串拼接到  dest  字符串末尾, dest  需有足够空间容纳拼接后内容(原  dest  长度 +  src  长度 ),拼接前会自动找到  dest  的  '\0'  位置,从该位置开始覆盖。

        示例:

char s1[100] = "Hello", s2[] = "World";
strcat(s1, s2);
puts(s1); // 输出 HelloWorld

1.3  字符数组注意点

        1)'\0'  区分: '0'  是字符 0 的 ASCII 码(值为 48 ), '\0'  是字符串结束标志(值为 0 ); "0"  实际存储为  '0'  +  '\0'  , ""  是空字符串,仅存  '\0'  。

        2)数组与指针差异:字符数组  char s[3] = {"Beijing"};  是直接给数组初始化内容(本质是逐个元素赋值 );若用指针  char *p = "Beijing";  , p  是指向字符串常量的指针,字符串常量存储在只读区, p  可改指向,但不能通过  p  修改字符串内容(否则程序崩溃 )。

        3)赋值限制:字符数组不能直接整体赋值(如  s2 = s1;  非法 ),需用  strcpy  ;遍历赋值可通过下标逐个字符操作,如:

char s1[10], s2[10] = "Hello";
int i = 0;
while (s2[i] != '\0')
{
    s1[i] = s2[i];
    i++;
}
s1[i] = '\0'; // 手动补结束符
puts(s1);

    2   二维数组

    2.1  基础概念

            1. 定义: 类型说明符 数组名[常量表达式][常量表达式];  ,如  int a[3][4];  ,可用于图像处理(存储像素矩阵 )、数学矩阵运算(如行列式计算 )等场景。

            2.  本质:可理解为“一维数组的数组”, a[0]   a[1]   a[2]  分别是三个一维数组(长度为 4 ), a  是指向这些一维数组的指针(数组指针 ), a[0][0]  是具体元素, &a[0][0]  是元素指针, &a[0]  是一维数组指针,二者在数值上可能相同,但类型不同。

    2.2  使用与操作

            1. 遍历:通过嵌套循环实现,外层控制行,内层控制列。若需按“内层列、外层行”(内列外排 )或“内层行、外层列”(内行外列 )逻辑处理,调整循环顺序即可。

            示例(按行输出,行列下标从 0 开始 ):

    int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
    for (int i = 0; i < 3; i++)
    { // 行循环
        for (int j = 0; j < 4; j++)
        { // 列循环
            printf("%d ", a[i][j]);
        }
        puts(""); // 每行结束换行
    }

            示例(按列赋值,从 1 开始递增 ):

    int a[3][4], t = 1;
    for (int j = 0; j < 4; j++)
    { // 列循环(外列)
        for (int i = 0; i < 3; i++)
        { // 行循环(内行)
            a[i][j] = t++;
        }
    }
    // 输出验证
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            printf("%d ", a[i][j]);
        }
        puts("");
    }

            2. 初始化:可按行初始化(如  int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};  ),也可省略内层大括号(按顺序填充,如  int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};  )。若初始化值不足,剩余元素默认填 0 。初始化时只能省略行号,不能省略列号 ,因为列号决定每个一维数组的长度。

            3. 行列数计算:

        1)行数: int rows = sizeof(a) / sizeof(a[0]);  , sizeof(a)  是整个二维数组字节数, sizeof(a[0])  是一行(一维数组 )的字节数,相除得行数。

            2)列数: int cols = sizeof(a[0]) / sizeof(a[0][0]);  , sizeof(a[0])  是一行字节数, sizeof(a[0][0])  是单个元素字节数,相除得列数。

    2.3  应用扩展(三阶行列式计算示例)

            三阶行列式可通过二维数组存储矩阵,用循环实现计算。以下是基于“对角线法则”的循环版代码:

    #include <stdio.h>
    int main(void)
    {
        int a[3][3] = {1,2,3,4,5,6,7,8,9}; // 三阶矩阵
        int det = 0;
        // 外层循环控制符号(主副对角线)
        for (int sign = 1; sign >= -1; sign -= 2)
        {
            for (int i = 0; i < 3; i++)
            {
                // 生成行列偏移,覆盖行列式六项乘积
                int offset1 = (i + 1) % 3;
                int offset2 = (i + 2) % 3;
                if (sign == 1)
                {
                    // 主对角线方向累加
                    det += a[0][i] * a[1][offset1] * a[2][offset2];
                }
                else
                {
                    // 副对角线方向累减
                    det -= a[0][i] * a[1][offset2] * a[2][offset1];
                }
            }
        }
        printf("三阶行列式结果:%d\n", det);
        return 0;
    }

            逻辑说明:通过  sign  控制主( +  )、副( -  )对角线乘积的加减; offset1   offset2  生成循环偏移,遍历行列式展开所需的元素组合,模拟对角线法则的六项乘积计算,最终得到行列式结果。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值