《C语言内核深度解析》——笔记及拓展(2)

文章是我前几天读了朱有鹏,张先凤老师的《嵌入式Linux与物联网软件开发:C语言内核深度解析》写的,拜读之后,虽没有醍醐灌顶,至少解开了我之前的一些疑惑。

《嵌入式Linux与物联网软件开发:C语言内核深度解析》目录

以前学C语言,就是在IDE上编一下代码,编译器会有错误有警告提示,很少思考过变量、指针、结构体、函数之间,及所编代码和当前所运行系统的关系(系统内存有多大,运行速度怎样,怎样优化算法)。等我真正学了嵌入式,才开始思考上面的问题,才开始去了解软件与硬件的关系。

本文章是边复习边记笔记,跟书籍目录不一样,以后可能会补充及修改。

有纰漏请指出,转载请说明。

学习交流请发邮件 1280253714@qq.com

第三章 指针才是C语言的精髓

  • 指针作用

  1. 将不连续的内存空间连接到一起,如数据结构链表(在数据结构part提到)

  1. 操作内存

/*
int型指针ptr,指向num变量的首字节地址
    &取地址        
    *间接运算    取消引用
    &与*互为逆过程
*/
int main(void){
    int num=5;
    int *ptr=#
    printf("num's add=%p \n",&num);
    printf("num's val=%d \n",num);
    printf("ptr's add=%p \n",&ptr);
    printf("ptr's val=%p \n",ptr);
    printf("ptr pointing value=%d \n",*ptr);
    return 0;
}
  • 高级语言Java和python等指针去哪了

使用Java时可能会发生空指针异常(null),说明Java也使用指针,只不过由底层封装好了,不能直接操作指针,所以对于频繁使用指针的底层开发来说就有点力不从心了。

  • 指针使用三部曲

//定义
int *p=NULL;

//关联
int a=10;
p=&a;

//引用
int b=*p; //读
*p=30;    //写
  • 野指针

指针指向一个不确定的地址

void main(){
    int *p;    //没有初始化p,就向p指向的地址赋值
    *p=10;
}

指向一个确定的地址空间,但引用的结果不可预知(读写权限未知)

void main(){
    int *p=0x12345678;    
    *p=10;
}
hello作为字符串常量,存在于内存的常量区中,该段只允许读操作
此处想将‘e’修改为‘w’,导致段错误

访问空间时,内存越界

  • NULL是什么

/* Define NULL pointer value and the offset() macro */
#ifdef _cplusplus
#define NULL 0
else
#define NULL ((void *)0)    //在c中,NULL被定义为(void*类型的0)
#endif
  • const与指针

int const *p等价于const int *p

//p所指向的空间是常量,不能被修改,但是p本身可以被修改
int a=10;
int b=20;
int const *p=&a;    //p指向a
*p=100;             //编译时报错,p指向空间的内容不能被修改
p=&b;               //正确,p本身可以被修改

int *const p

//p所指向的空间是可以被修改,但是p本身不能被修改
int a=10;
int b=20;
int *const p=&a;    //p指向a
*p=100;             //正确
p=&b;               //错误,p的指向不能改变

int const *const p

//p所指向的空间是可以被修改,但是p本身不能被修改
int a=10;
int b=20;
int const *const p=&a;    //p指向a
*p=100;             //编译时报错,p指向空间的内容不能被修改
p=&b;               //错误,p的指向不能改变
  • 指针变量数据类型强制转换

/*指针变量数据类型的含义:对于32位嵌入式系统,
指针变量存放的数据都是32位的,即指向变量的地址*/
int a=10;
int *p=&a;
/* 此处(int *)中,int表示p指向的地址空间是int型,大小为4bytes
当需要存取数据时,按照int型进行存取(注意与float同为4bytes,但解析方法不同)*/

指向空间强制类型转换

int a;
float b=12.3;
int *pa=&a;
float *pb=&b;
*pa=(int)*pb;  //等价于 a=(int)b;

指针本身强制类型转换

int main(void){
    double a=12.3;
    double *pa=&a;
    int *pb=NULL;
    pb=(int *)pa;  //等价于 pb=(int *)&a;
    printf("%d \n",*pb);
    return 0;
}
/*此处pa和pb指向的地址一样,但当使用*pb去读取变量a时,会以int类型读取a的值*/
  • 指针与数组

使用指针访问数组
#include <stdio.h>

int main(void){
    int buf[4]={1,2,3,4};
    int i=0;
    int *p=buf;
    for(i=0; i<sizeof(buf)/sizeof(buf[0]); i++){
        printf("%d \n",buf[i]);        //利用下标访问
        printf("%d \n",*(buf+i));      //利用指针常量访问
        printf("%d \n",*(p+i));        //利用指针变量访问
    }
    return 0;
}

buf与&buf值相同,但含义不同

int main(void){
    int buf[4]={1,2,3,4};
    printf("%p \n",buf);       //数组名或数组第一个元素的首地址
    printf("%d \n",buf[0]);    //第一个元素,rw   
    printf("%p \n",&buf);      //数组首地址
    printf("%p \n",&buf[0]);   //等价于buf数组首地址
    return 0;
}
//buf与&buf值相同,但含义不同
int main(void){
    int buf[4]={1,2,3,4};
    printf("%p \n",buf);
    printf("%p \n",buf+1);    //加一个元素空间,4bytes
    printf("%p \n",&buf+1);    //加一个数组空间,16bytes
    return 0;
}

指针、数组与sizeof

我曾在STM32编程中遇到这样的问题,把10个元素的数组传进函数里,函数只收到4个元素,是怎么回事呢?

因为对于数组来说,传参传的是数组的首元素首字节地址(4bytes),目的是为了提高效率。因为如果数组有1000个元素,把整个数组入栈太费计算机资源了。

#include <stdio.h>

void SendArray(int *arr){
    int i=0;
    printf("sizeof(arr)=\t %d \n",sizeof(arr));
    for( i=0; i<sizeof(arr)/sizeof(arr[0]); i++){
        printf("arr[%d]=%d \n",i,arr[i]);
    }
}

int main(void){
    int i=0;
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    printf("sizeof(a)=\t %d \n",sizeof(a));
    for( i=0; i<sizeof(a)/sizeof(a[0]); i++){
        printf("a[i]=%d \n",a[i]);
    }

    printf("\n\n");
    SendArray(a);
    return 0;
}
字符串是什么

高级语言如Java,有独立的字符串类型,即String

C语言通过字符指针来间接实现,即

char *p="hello"; //字符串

char str[]="hello"; //字符数组

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

int main(void){
    char str[]="hello"; //字符数组
    char *p=str;

    printf("%d \n",sizeof(str[0]));    //1   
    printf("%d \n",str[6]);            //-52

    printf("%d \n",sizeof(str));        //6
    printf("%d \n",strlen(str));        //5
    
    printf("%d \n",sizeof(*p));        //1    *p代表str[0]的地址
    printf("%d \n",strlen(p));         //5    p指向的字符串‘hello’的大小
    printf("%d \n",sizeof(p));         //4    p指针变量的大小
    return 0;
}

sizeof(str)结果是6,因为“hello”还包含‘\0’,实际上sizeof的值等于数组占用的内存字节数

使用'\0'作为字符串结束标志,这是‘魔数’,即选一个特殊的数字,这个数字表示特殊的含义。

在MATLAB的serialport()输入参数中,其中Terminator就是可以指定串口输出的终止符,

LF是LF (NL line feed, new line),即换行符'\n'

CR是CR (carriage return),回车键

  • 指针与函数传参、返回值

以形参和返回值
int fun(int a,int b){
    return a+b;
}
int main(void){
    fun(10,20);
    return 0;
}
全局变量
int a,b,c;
void fun(){
    c=a+b;
}
int main(void){
    a=10;
    b=20;
    fun();
    return 0;
}
指针
void fun(int a, int b, int *p1, int *p2){
    *p1=a*a;
    *p2=b*b;
}
int main(void){
    int c=0;
    int d=0;
    fun(10,20,&c,&d);
    return 0;
}

函数真正的返回值,很多时候都是0或-1等表示函数执行成功或失败

  • 课后 使用指针交换两变量的值

#include <stdio.h>

void swap(int *pa, int *pb)
{
    int t = *pa;
    *pa = *pb;
    *pb= t;
}
 int main(void)
 {
     int a = 5;
     int b = 6;
     swap(&a, &b);
     printf("a=%d, b=%d \n", a,b);
     
     return 0;
 }

第四章 C语言复杂表达式与指针高级应用

  • 指针数组与数组指针

这一part原书讲得很好

从语文角度看,放在前面的是修饰词,放在后面的是主语

从成分上看,中括号[]的优先级比星*高,谁靠得近,谁就是核心

int *p[5] 指针数组

int (*p)[5] 数组指针

  • typedef

轻松理解typedef
typedef int size;    
size i;    //int i;

typedef char Line[10];
Line t;    //char t[10];

typedef int (*fun_ptr)(int,int);
fun_ptr fp; //int (*fp)(int,int)
define和typedef的区别:

#define只是简单的宏替换,在预编译时被处理

#typedef在编译时被处理,在stdint.h中有 typedef unsigned char uint8_t;

//区别一
#define dpchar char *
typedef char * tpchar
dpchar p1,p2;    //char *p1,p2;
tpchar p1,p2;    //char *p1,*p2;
//区别二
#define dInt int
typedef int tInt;
unsigned dInt p1,p2;    //正确,等价于 unsigned int p1,p2;
unsigned tInt p1,p2;    //不行
//区别三
//typedef可以组建新类型,但define不行
typedef char[200] charBuf;
charBuf buf;    //char buf[200]
typedef与struct(这么记就OK)
#include <stdio.h>

int main(void){
    typedef struct {
        char id[8];
        char name[8];
        int age;
    }Student;
    Student stu={"2019","ckj",18};
    return 0;
}
typedef的意义
  1. 简化类型,让程序更易理解和书写

  1. 创造平台无关类型,便于移植

在STM32中,int占用4字节,32比特,数据范围为-2147483648~2147483647[-2^31~2^31-1]

在之前的51单片机中,int占用2字节,16比特,数据范围为-32768~32767[-2^15~2^15-1]

对于之前在51单片机运行的程序,移植到STM32中,只需

把typedef unsigned int uint16_t;

改为typedef unsigned int uint32_t;

  • 函数指针

函数的实质是一段代码,这段代码在内存中连续分布。函数指针(函数名)的值是函数的地址

#include <stdio.h>
void fun(void){
    printf("I am fun");
}
int main(void){
    void (*pFun)(void);
    pFun=fun;
    pFun();
    return 0;
}
// char *strcpy(char* dest, const char *src);
#include <stdio.h>
#include <string.h>

int main(void){
    char a[5]={0};
    char* (*pFun)(char *, const char *);
    pFun=strcpy;
    pFun(a,"abc");
    printf("%s",a);    //abc
    return 0;
}
  • 二维数组

#include <stdio.h>

int main(void){
    int a[2][3]={1,2,3,4,5,6};
    int *p=a[0];    //a[i][j]等同*(p+i)+j

    printf("%p \n",a);           //008FFC8C
    printf("%p \n",&a);          //008FFC8C
    printf("%p \n",&a[0]);       //008FFC8C
    printf("%p \n",&a[0][0]);    //008FFC8C

    printf("%d \n",*p);           //a[0][0]
    printf("%d \n",*(p+1));       //a[0][1]
    printf("%d \n",(*p)+1);       //a[0][1]
    printf("%d \n",(*p+1)+1);     //a[1][1]
    printf("%d \n",(*p+1)+3);     //a[1][3]
    return 0;
}
#include <stdio.h>

int main(void){
    int a[2][3]={1,2,3,4,5,6};
    int (*p)[3]=a;    //a[i][j]等同*(*(p+i)+j)

    printf("%d \n",**p);            //a[0][0]
    printf("%d \n",*(*p+1));        //a[0][1]
    printf("%d \n",**(p+1));        //a[1][0]
    printf("%d \n",*((*p+1)+1));    //a[2][1]
    printf("%d \n",*((*p+1)+3));    //a[2][3]
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值