嵌入式八股C语言---指针与数组篇

数组

  1. 数组是什么
    数组算是定义了一块连续的空间,数组名就是这块连续空间首地址的名字
    这块空间多大?—数组的长度乘以元素的类型得到 或者使用sizeof也行
    如何访问?—数组的起始地址 + 对应的偏移量
    数组的起始地址可以用数组名得到
  2. 一维数组和二维数组
  • 2.1 一维数组
    int a[2] = {1,2,3,4};//不会报错但是会warning 越界了
    从汇编也能看出来 确实只分配了8个字节 所以数组越界行为的后果都是未定义的
    .global a
    .data
    .align 2
    .type a, %object
    .size a, 8
  • 2.2二维数组
    int a[4][2] = {1,2,3}; //明确了大小 就是 4 * 2 * 4个字节,虽然部分没填满
    int b[][2] = {1,2}; // 告诉编译器自己推导大小是多大 根据给定的数据
    int c[][] = {5,6}; // 错误的初始化
        .global	a
        .data
        .align	2
        .type	a, %object
        .size	a, 32

        .global	b
    `	.align	2
        .type	b, %object
        .size	b, 8
  1. []运算符 与 *运算符
    a[x]运算符本质上就是
    (a + x)运算符
    不过这里注意 指针的++可不是单纯的递增1 而是和数据类型有关
  • 3.1 []里面的数可以是负数吗?
    当然可以 只要这个[]里面的表达式最后能算出来数 是啥都行
  • 3.2二维数组的运算
    数组名可以看做是这块连续内存的起始地址的别名
    • 对于一维数组
        int main()
        {
          	int array[4] = {1,2,3};
            int count = 2;
            printf("%d %d %d\r\n",array[count],*(array + count),(array - count)[count]);  // 结果是 3 3 0
            printf("%d\r\n",a[3][0]);
            return 0;
        }
  • 二维数组呢?
    int a[4][2] = {1,2,3};
    a++相当于移动了多少个字节呢
    printf("%p %p\r\n",a,a + 1); // 0x55eae2a01020 0x55eae2a01028
    相当于移动了8个字节 此时a可以看做是这样类型 int (* a)[2];
  1. 数组指针与指针数组
  • 数组指针
    本质上是个指针 指向了一个数组
    int (*a)[2]; // 这是一个指针 指向一个数组 数组的大小是2 元素类型是int
  • 指针数组
    本质是个数组 只不过里面元素类型是指针
    int a[2]; // 这是一个长度为2的数组 数组里元素的类型是 int *
    因为指针的操作 每次++都是按照指针的类型自增地址的
    int p[8] = {1,2,3,4,5,6,7};
    int a[2];
    a = p;
    printf("%d %d %d",
    (a),
    (++a),a[2]); // []等于*
    所以我们可以通过指针数组实现跳着访问一维数组
        void test1()
        {
            int p[8] = {1,2,3,4,5,6,7};
            int (*a)[2] = (int (*) [2])p;
            int (*b)[2] = (int (*) [2])p;
            printf("%d %d %d\r\n",*(a)[0],*(++b)[0],a[2][0]);   // 答案为 1 3 5
        }
  1. 字符、字符串与字符数组
  • 字符串与字符数组
    实际上我们是用字符数组存储字符串 如果字符串是用""定义的 会自动往后面补上’\0’
    但是如果是 char a[]= {‘a’, ‘b’, ‘c’} 不手动添加’\0’的话是不会有的
    但是,字符数组会自动补0,so有时候就会发蒙就是因为这个
        #include <stdio.h>
        void test1()
        {
            char a[] = {'a','b','c'};   // 编译器认为字符数组长度为3
            char b[6] = {'a','b','c'};  // 长度为6 不够的全补0了
            char * c = "abc";
            printf("%d %d %d\r\n",strlen(a), strlen(b), strlen(c)); // 6 3 3
            printf("%d %d %d\r\n",sizeof(a), sizeof(b), sizeof(c)); // 3 6 8
        }

        int main()
        {
            test1();
        }
        // 为什么stelen(b)算对了呢 ,**后面没初始化的自动补0**了 你看a就没算对
        movb	$97, -33(%rbp)
        movb	$98, -32(%rbp)
        movb	$99, -31(%rbp)
        movl	$0, -30(%rbp)       // 补为0了
        movw	$0, -26(%rbp)
        movb	$97, -30(%rbp)
        movb	$98, -29(%rbp)
        movb	$99, -28(%rbp)
  • 转义字符
    我们最常用的就是 \r\n 和 \
    • \x转义字符–表示16进制
      char a = ‘\x15’; // 表示值为 0x15 = 21
      所以’\x’有范围 为’\x00 ---- \xff’
      '\xffff’就是不对的
      char sub1 = ‘\x15’;
      char sub2 = ‘\x1555’;
      printf("%d %d\r\n",sub1, sub2); // suib2 = 85 还是得看编译器怎么做
    • 八进制 \105 反斜杠后面的就默认是8进制的
      printf("\150\n"); //答案就是105
  1. 数组与指针的区别
  • 数组是连续的区间 指针指向的区间大小可不一定,也不一定连续(链表)
  • 同种类型指针之间可以直接赋值,数组只能一个个元素赋值
  • 概念不同
    数组:是同种类型的集合
    指针:里面保存的地址的值
  • 所占用的字节不同
  • 修改内容的方式不同
        char * c = "abc";
        char d[] = "abc";
        void test2()
        {
            c[0] = '5';
            d[0] = '5';
            printf("修改已完成");

        }
        // 编译不报错 但是直接段错误 为啥会段错误呢
        // 因为 char * c = "abc";是在只读段 而 char d[]是在数据段
        .globl	c
        .section	.rodata
        .................
        .globl	d
        .data
        .type	d, @object
        .size	d, 4
  1. 数组作为参数传递----直接当做指针处理
    void test1(int array[]) {
        int n = sizeof(array) / sizeof(array[0]);  
        // 大错特错 因为这样传参相当于传入了一个指针 sizeof(array)会是4(32位)
    }
    int array[10] = {1,2}; 
    // 可以把 array的类型看做是 int[10] 所以sizeof(array)就能算出来大小是 4 * 10
    // &array 就变成了 int (*)[0] 此时array + 1 就相当于走过了40个字节
  1. 实现排序
    什么冒泡啊 快排啊之类的

指针

  1. 指针是什么,指针的类型
    指针其实也是个变量,只不过这个变量里面存储的是内存地址
    这个内存地址指向了一片内存 通过指针 + */[] 就可以操作这块内存
    • 指针的类型
      int * char; // 指针类型为 int *
      函数指针 指针类型就是它
  2. 指针的运算
    一定注意只有同类型的指针才可以运算
    比如一个指针指向float 一个指针指向int 是不可以进行运算的
  • 2.1 指针的自增/减
    int *p;
    p ++;
    此时不是单纯的数值 + 1 而是 + siezeof(type),这里是int 所以打印++p的结果%p会发现地址增加了4
  • 2.2 两个指针相减–表示它们在内存中的距离
    相减的结果以数据类型的长度sizeof(type)为单位,而
        int main (void)
        {
            int a[10];int *p1,*p2;p1 = &a[1];p2 = &a[2];
            printf("%d\n", p2 - p1);return 0;  // 结果是1 而不是4 
        }
        #define mysizeof2(name) ((size_t)(&(name)+ 1) - (size_t)(&name))     // 自己定义的sizeof
  • 2.3 比较运算符
    p < q:指针p所指的数在q所指数据的前面。
  1. &运算符和运算符,[]运算符
    只要了解[]运算符和
    运算符的本质木有区别就行
  2. 强制类型转换
    int num = 0x12345679;
    char * p = (char*)&num;
    printf("%02x %02x %02x %02x\r\n",p[0],p[1],p[2],p[3]);
  1. 大端与小端
    大端:是高字节序的存放在低地址
    小端:是高字节序的存放在高地址
    什么是高字节序 int a = 0x1234;// 34是低字节序 12是高字节序
    注意:栈是从高地址向低地址生长的
    // 打印 p[0] = 0x79 说明低地址存放低字节序 所以是小端
  2. 空指针与野指针与void*
    野指针:是指指针指向的地址是不确定的;
    原因:释放内存之后,指针没有及时置空
    避免:
    • 初始化置 NULL
    • 申请内存后判空
    • 指针释放后置 NULL
      空指针就是指向了地址为0的地方
    • void*表示一种通用的指针 但注意不能直接解引用
        void *my_memcpy(void *dest, const void *src, size_t n) {
            void * res = dest;
            while (n--) {
                *dest++ = *src++;  // 直接报错了 因为对void*的使用不合法
            }
            return res;
        }
  1. 二级指针
  • 7.1二级指针与指针数组–传入参数
    char *a[4] = {“123”,“456”,“789”,“101”};
    void test(char *a[],int len);
    void test2(char **a,int len); //使用起来没有区别
  • 7.2二级指针与二维数组
        void test3()  // 你可以以任何灵活的方式访问这块连续的空间
        {
            int array[5][10] = {1,2,3,4,5,6,7,8,9,10,11,123};  
            // 对于二维数组 编译器认为的一块连续的 5 * 10 * 4的空间
            int * p = array;        // 所以你还用int *操作没问题 毕竟每次地址偏移4就行了
            printf("%d \r\n",p[6]);  // 非常神奇 竟然正确的访问了7?
            // 编译器就warning了  此时int **的话 你可以认为每次 + 1是地址偏移了8个(64位操作系统) 
            // 编译器只是认为地址需要偏移8之后取出来值 不一定是错误的呀
            int ** p2 = array;
            printf("%d  %d %d\r\n",p2[0],p2[1], p2[2]);
            int (*p3)[10] = array;      // 这样看起来标准多了 但是没人规定非得如此标准
            printf("%p %p %d %d %d\r\n",p3, p3 + 1, *(p3)[0], *(p3 + 1)[0],p3[1][1]);

            int (*p4)[2] = array;      // 也warning 但是没错
            printf("%p %p %d %d %d\r\n",p4, p4 + 1, *(p4)[0], *(p4 + 1)[0],p4[1][1]);
        }
  1. 指针和引用的区别
    1. 指针是实体,而引用是别名。
    2. 指针和引用的自增(++)运算符意义不同,指针是对内存地址自增,引用是对值的自增。
    3. 引用使用时无需解引用(*),指针需要解引用;
    4. 引用只能在定义时被初始化一次,之后不可变;指针可变。
    5. 引用不能为空,指针可以为空。
    6. sizeof的结果不同
嵌入式C语言八股文是指在嵌入式系统开发中常见的基本知识点和技能要求的简要总结。下面是嵌入式C语言八股文的主要内容: 1. 数据类型:包括基本数据类型(如int、char、float等)和派生数据类型(如数组、结构体、枚举等),掌握各种数据类型的使用方法和特点。 2. 运算符:熟悉各种算术运算符、逻辑运算符、位运算符等,掌握它们的优先级和结合性,能够正确使用运算符完成各种计算任务。 3. 控制语句:包括条件语句(if-else语句)、循环语句(for、while、do-while循环)、选择语句(switch-case语句)等,掌握这些语句的使用方法和注意事项。 4. 函数:了解函数的定义和调用,能够编写函数并正确使用函数参数和返回值,理解函数的作用域和生命周期。 5. 数组指针:掌握数组指针的定义和使用,了解数组指针在内存中的存储方式,能够通过指针进行数组的访问和操作。 6. 文件操作:了解文件操作的基本流程,包括文件的打开、读写和关闭,理解文件指针和文件访问模式的概念。 7. 中断处理:了解中断的基本概念和原理,能够编写中断服务程序(ISR)并正确处理中断请求。 8. 程序调试:掌握常用的调试技巧和工具,能够使用调试器进行程序的单步执行、观察变量值等操作,能够分析程序运行过程中的错误和异常。 以上是嵌入式C语言八股文的主要内容,掌握这些知识和技能,可以帮助你在嵌入式系统开发中更好地应对各种任务和挑战。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值