嵌入式学习——1——C基础——6 指针

(1)指针

指针:指针就是地址,表示每个字节在内存中的标号

指针变量:就是一个容器,用来存储指针

(加快访问速度,不会发生数据丢失)

一、指针的定义格式

格式:存储类型 数据类型 *指针变量名

int *p;     //p的类型是int*,int表示指针指向地址对应值的类型,指针变量加减运算的偏移量;

char*p1;

void *p2;        //通用类型指针,使用时需要类型强转,多用来传参和返回

二、指针的字节大小

64位操作系统---------------------8字节

32位操作系统---------------------4字节

三、指针的定义和初始化

1、指针的类型和变量的类型保持一致

        int a = 100;

        int *p = &a;   

        char b = 'A';

        char *q;

        q = &b;   

2、指针的类型和变量的类型不一致

        int a = 0x12345678;

        char *p = (char *)&a;

        short *q = &a;

3、定义指针未初始化,称为野指针

        野指针:非法访问内存空间

        缺点:    段错误、计算机混乱、死机

4、当定义指针不清楚指向谁,指向NULL

        int *p = NULL; 

                NULL:表示0号地址,不可使用,只是用来指针指向

        int *p = 0;

5、可以使用已经初始化的指针对另一个指针初始化

        int a = 100;

        int *p = &a;

        int *q = p;

6、不可以让指针初始化为值

        int a = 100;

        int *p = a;        //error

        int *p = 100;        //error

        int *p = 0x7f7f8s8ddg;        //error

四、解引用和取地址

*        解引用(取地址对应的值)

&        取地址

1、值:a--->*p--->*&a

2、地址:&a--->p--->&*p

3、*和&属于逆运算

五、指针的运算

算数运算:+- ++ --,

关系运算:> >= <

赋值运算:= += -=

1、算数运算

int a = 100;        int *p = &a;
运算符例子解释

                +、-

        (+号为例)

&a+n 向高地址偏移n倍的数据类型(int)字节大小
*p+n先取p指针指向地址对应的值,然后对值+n
*(p+n)向高地址方向偏移n倍的数据类型字节大小,在该地址的值
p+n向高地址方向偏移n倍的数据类型字节大小

        ++、--

        (++为例)

(&a)++\++(&a)        error        &a是常量
p++后缀运算,先取,后向高地址方向偏移一个数据类型字节大小
++p前缀运算,先向高地址偏移一个数据类型字节大小,后取
*++p先++p,向高地址偏移一个数据类型字节大小,再取值
*p++先p++,++和p结合,++是后缀,先取值,在对p自增,向高地址方向偏移一个数据类型字节大小
++*p先*p,先取值,对值自增
*(p++)

先p++,++和p结合,++是后缀,先取值,在对p自增,向高地址方向偏移一个数据类型字节大小

*(++p)

先++p,前缀运算,向高地址方向偏移一个数据类型字节大小,在取值

(*p)++

先*p,先取值,在后缀自增,先运算,后自增

2、赋值运算

=

int a=100;int *p=&a;

指针变量p指向变量a的地址,注意需要保证=左右两边类型一致

+=

p+=1--->p=p+1

p向高地址方向偏移一个数据类型字节大小

-=

p-=1--->p=p-1

p向低地址方向偏移一个数据类型字节大小

3、关系运算

>

p>q

条件成立则返回1,否则返回0

>=

p>=q

条件成立则返回1,否则返回0

<

p<q

条件成立则返回1,否则返回0

<=

p<=q

条件成立则返回1,否则返回0

==

p==q

条件成立则返回1,否则返回0

!=

p!=q

条件成立则返回1,否则返回0

练习:在主函数中定义字符串并初始化,实现字符串逆置

char str[]="hello"
    str str+1
    "h   e    l    l    o     \0"
    0x10 0x11 0x12 0x13 0x14  0x15
         start     end
     0   1    2    3     4
     i                    j
    char *start=str,*end=str+strlen(str)-1;
    while(start<end)
    {
       t=*start;*strat=end....
         start++;end--;   
    }


while(i<j)
{
   t=str[i];str[i]=str[j];str[j]=t;
   i++;j--; 
}

六、一维数组和指针

格式: 数据类型 *指针变量=数组名

            int arr[] = {1,2,3}

            int *p = arr;

地址:&arr[i] <==> &arr[0]+i <==> arr+i <==> &p[i] <==> &p[0]+i <==> p+i <==> p++

值:arr[i] <==> *(&arr[0]+i) <==> *(arr+i) <==> p[i] <==> *(&p[0]+i) <==> *(p+i) <==> *p++

冒泡排序

// 冒泡排序
// int type 0 倒序 ,1 正序
void sort_array(int *p, int n, int type){
    int i = 0;
    int j = 0;                                                        
    int temp = 0;
    while(i < n-1){
        while(j < n-i-1){
            if(type == 1 ? (*(p+j)>*(p+j+1)) : (*(p+j)<*(p+j+1))){
                temp = *(p+j);
                *(p+j)=*(p+j+1);
                *(p+j+1)=temp;
            }
            j++;
        }
        j = 0;
        i++;
    }


    i = 0;
    while(i<n){
        printf("%d ",*(p+i));
        i++;
    }
    printf("\n");

}

七、值传递和地址传递

1、值传递

值传递:传递的是值(看实参)

形参的改变不影响实参

形参和实参分别占有存储空间

单项传递

2、地址传递

地址传递:传递的是地址

形参的改变影响实参

形参和实参共享存储空间

单项传递

八、多级指针

格式:存储类型 数据类型 **变量名称(*个数任意)

一级指针存储变量的地址

二级指针存储一级指针的地址

三级指针存储二级指针的地址

。。。

int a;        
int *p=&a;
int **q=&p;
int ***m=&q;

九、通用类型指针

格式:void*指针变量名

可以指向任意类型的地址,但是使用时需要类型强转,一般多用于传参和返回

int main(int argc, const char *argv[])
{
    int a=100;                        
    void *p=&a;
    printf("*p=%d\n",*(int*)p);
    return 0;
} 

十、二维数组和指针

格式:int arr[2][3] = {11,22,33,44,55,66}

           int (*p)[3]=arr; // 数组指针 p->arr

           p->arr

                &arr[0][0] -->&p[0][0]

                arr[0] -->p[0]

                &arr[0] -->&p[0]

                arr -->p

                &arr 不等价-->&p

十一、数组指针

数组指针:本质上是一个指针,用来指向二维数组变量名,指向一维数组为整体的首址编号

格式: 存储类型  数据类型  (*数组指针变量名)[常量表达式]

        int arr[3][5]

        int (*p)[5] = arr;

        p的类型是:int(*)[5],大小是指针的字节大小

十二、指针数组

指针数组:本质上是一个数组,存储多个类型相同的指针

格式: 存储类型 数据类型 *指针数组变量名[常量表达式]

        int *p[5]; //定义5个int*类型的指针

        p的类型是 int *[5],占5*8字节大小

解析

1.常量表达式: 指针的个数,数组长度,初始化时可以省略不写,默认是实际元素的个数

        

int a = 1, b = 2, c = 3, d = 4; // ----->int arr[4]
int *p1 = &a;
int *p2 = &b;
int *p3 = &c;
int *p4 = &d;

int *p[4] = {&a, &b, &c, &d};

十三、字符数组和指针

格式: char str[10]

            char *p = str;

1.通过指针操作字符串
int main(int argc, const char *argv[])
{
    char str[10]="";
    char *p=str;//p-->str
    gets(p);
    puts(p);
    return 0;
}         
2.指针指向字符数组变量
    char str[]="hello";
    char *p=str;//p指针指向字符数组str的首地址
    
    i.可以通过指针修改元素  eg: *(p+1)='E'
    ii.当不同的数组存储相同的字符串时,地址不同
3.指针指向字符串常量
    char *q="hello";//指针q指向字符串常量的首地址,就是字符'h的地址'
    
    i.不允许通过指针修改元素  eg: *(q+1)='E'   段错误
    ii.不同的指针指向同一个字符串时,地址相同

十四、字符指针数组

字符指针数组:本质上是一个数组,存储多个字符指针

格式: char *指针数组变量名[常量表达式]

1.字符指针数组指向字符数组变量的地址
    char a[]="123";
    char b[]="abc";
    char c[]="ABC";
    char *p1=a;
    char *p2=b;
    char *p3=c;
    char *p[3]={a,b,c};
                0 1 2
                p[0]-->a
                      puts(a)
    for(i=0;i<3;i++)
    {
          puts(p[i]);  
    }

2.字符指针数组存储多个字符串常量的地址
    char *p[3]={"123","abc","ABC"};
     for(i=0;i<3;i++)
    {
          puts(p[i]);  
    }

十五、指针函数

指针函数定义:本质上是一个函数,返回了一个地址,注意不允许返回局部变量的地址

格式:

        数据类型* 函数名(参数列表) {

                函数体;         

                return 地址;

         }

十六、函数指针

函数指针:本质上是一个指针,用来指向函数的的首地址,函数名表示函数的首地址

格式: 存储类型 数据类型 (*函数指针变量名)(参数列表)

void sum1();                void (*p)() = sum1           p的类型是void(*)()

void sum2(int a, float b)   void (*p)(int, float) = sum2 p的类型是void(*)(int float)

float sum3()                float (*p)() = sum3

float sum4(int a, float b)  float (*p)(int, float)= sum4

char* strcpy(char *dest, const char *src);
        char* (*p)(char*,const char*)=strcpy

函数指针主要使用在回调函数:函数名作实参,函数指针作形参接收函数的首地址

十七、函数指针数组

函数指针数组:本质上是一个数组,存储多个类型相同的函数指针

格式: 存储类型 数据类型 (*函数指针数组变量名[常量表达式])(参数列表)

float Sum(int a,float b)    float (*p1)(int,float)=Sum
float Sub(int a,float b)     float (*p2)(int,float)=Sub
float Mul(int a,float b)     float (*p3)(int,float)=Mul
float Div(int a,float b)     float (*p4)(int,float)=Div

float (*p)(int, float) = {Sum, Sub, Mul, Div}

    Sum(a,b)
    (p[i])(a, b)

  p占4*8,p的类型 float (*[4])(int,float)

(2)存储类型

一、auto

自动类型变量

1.局部变量省略存储类型默认是auto

2.auto不允许修饰全局变量

3.auto类型的变量内存默认在栈区

4.auto修饰局部变量,会初始化为0

二、static

static作用:静态变量,延迟局部变量的生命周期

1、static修饰局部变量:延迟局部变量的生命周期至整个文件

2、static修饰函数:把函数的生命周期延长至本文件结束,不可以跨文件使用

3、static修饰指针:不允许使用static修饰的指针指向auto类型变量的地址

                                计算机先给static类型的变量分配内存,后分配auto

三、volatile

防止内存优化,保持内存的可见性

四、const

1.const作用:修饰的值不可改变

2.const修饰的全局变量内存在静态区的只读段

3.const修饰的局部变量内存在栈区

4.const和指针结合

const char *p *在const的右边,const修饰的值,值不变,地址可变

char const *p *在const的右边,const修饰的值,值不变,地址可变

char * const p *在const的左边,const修饰的地址,地址不变,值可变

const char * const p 第一个const修饰是值,第二个const修饰的值地址,值和地址均不变

char const * const p 第一个const修饰是值,第二个const修饰的值地址,值和地址均不变

五、extern

引用外部变量

1、全局变量和函数省略存储类型默认是extern

2、extern类型的变量默认内存在静态区

3、不可以引用static修饰的变量

六、register

寄存器变量

        内存---->高速缓存---->寄存器

1、寄存器类型的变量不可以取地址操作

(3)宏

宏:宏是常量,只做替换,不做计算,不做正确性的检查

1.宏:不属于C语句

2.发生在预处理阶段

3.一旦定义不可以修改

一、宏

格式:#define 宏名 宏体

解析:

1.#define: 表示是一个预处理

2.宏名:满足命名规范,建议大写

3.宏体:默认是字符串

4.宏默认是全局变量

二、宏自定义函数

        i.使用({})

格式: #define 宏函数名(参数) ({表达式1;表达式2;....表达式n})

1.宏函数名:建议大写

2.参数: 只写名字,没有数据类型

3.({}) 当只有一个表达式时,可以省略不写,默认返回最后一个表示的结果

        ii.do..while(0)

格式: #define 宏函数名(参数) do{表达式1;表达式2;....表达式n}while(0)

1.宏函数名:建议大写

2.参数: 只写名字,没有数据类型

3.{}do..while(0)当只有一个表达式时,可以省略不写,默认是一个无返回值

三、宏系统函数

==============判断宏是否为真====
    #if 宏            #if 0
        C语句;
    #endif
执行过程:判断宏为真,则执行C语句,为假跳过,多用于注释
    #if 宏
        C语句1
    #else
        C语句2
    #endif
执行过程:如果宏为真,则执行C语句1,否则执行C语句2
    #if 宏1
        C语句1
    #elif 宏2
        C语句2
    .....
    #else
        C语句n
    #endif
执行过程:如果宏1为真,则执行C语句1,结束,
    如果宏1为假,则判断宏2,如果宏2为真,则执行C语句2,结束
    重复以上过程,如果条件都为假,则执行C语句n
========================判断宏已经定义=====
    #ifdef 宏
        C语句
    #endif
过程:判断宏已经定义,则执行C语句,未定义则跳过
=======================判断宏未定义=======
    #ifndef 宏
        C语句
    #endif
过程:判断宏未定义则执行C语句,否则跳过
======================判断多个宏定义=====
#if defined(宏1) && defined(宏2)    //可以判断多个宏
    C语句
#endif
=======================判断多个宏未定义=====
#if !defined(宏1) && !defined(宏2)    //判断多个宏未定义
    C语句
#endif
======================取消宏====
#undef 宏

=======================# 转换字符串
#define M(x) #x
#define N(x) x
int main(int argc, const char *arg
{
//  printf("%s\n",M(hello));        //"hello"
    printf("%s\n",N(hello));          //hello


    return 0;
}

========================## 拼接
#define A(x,y) x##y                    
int main(int argc, const char *argv[])
{


    int num2=100;
    printf("%d\n",A(num,2)); //num2
    return 0;
}

四、多文件编译

头文件:预处理命令:宏、头文件、全局变量、函数声明

主函数文件:主函数

自定义文件:自定义函数

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值