Linux笔记 --- 指针+数组

指针这种数据类型的使用极其频繁并且也很复杂所以在此单开一个笔记记录指针的特殊使用方法

二级指针

所谓二级指针就是指向指针的指针

cpp 复制代码
int a = 1;      
int *p1 = &a;   //指向a
int **p2 = &p1; //指向p1

printf("a:%d\n",a);
printf("%d\n",*p1);
printf("%d\n",**p2);//三种方式打印出来的都是a的值

上面例子里对p2进行了两次解引用,第一次解引用解出p1的值即a地址,第二次解引用就是解出a的数据

数组指针

指向数组的指针,此处所谓指向数组的指针不是只指向数组中的某个元素,而是指向整个数组,我们可以直接使用指针来间接访问数组的内容,实例:

cs 复制代码
int a[5] = {1,2,3,4,5};      
int (*p1)[5] = &a;   //指向a

int i=0;
for (i=0;i<5;i++)
{
    printf("%d\n",(*p1)[i]);
}

在此例中指针p1指向整个数组a,我们可以访问指针代替访问数组

函数指针

指向一个函数,也就是指向一段代码的指针,在使用中,函数名 其实就是函数的起始地址,那我们就可以指向函数名

cpp 复制代码
void swap(int *a,int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;

}

int main(int argc, char const *argv[])
{
    void (*p)(int *a,int *b);//定义一个函数指针
    p = swap;
    
    int a = 1;
    int b = 2;

    p(&a,&b);

    printf("a:%d\n",a);
    printf("b:%d\n",b);

    return 0;
}

注意定义函数指针的类型一定要与函数的返回类型相同

二维数组

实际上并不存在真正的二维数组,所有数组都是一维的,区别就在于其中的元素,所谓二维数组就是**由两个具有三个整形元素组成的数组的数组,**只是这样叫太过繁琐因此取了个简称

cpp 复制代码
int (a[2]) [3];

上面例子就定义了一个二维数组,在括号中表明此数组是具有两个元素的名为a的数组,外部表明其中的元素的类型是int【3】的小数组

对于数组a来说首元素为a[0],首元素的首元素为a[0][0],

cpp 复制代码
    int arr[2][3]={{1,2,3},{4,5,6}};
    int (*pa)[2][3] = &arr;
    int (*pb)[3] = &arr[0];
    int *pc = &arr[0][0];

    printf("pa:%p\n",pa);
    printf("pa+1:%p\n",pa+1);

    printf("pb:%p\n",pb);
    printf("pb+1:%p\n",pb+1);

    printf("pc:%p\n",pc);
    printf("pc+1:%p\n",pc+1);



    //运行结果
    pa:0x7ffff50c7a10
    pa+1:0x7ffff50c7a28
    pb:0x7ffff50c7a10
    pb+1:0x7ffff50c7a1c
    pc:0x7ffff50c7a10
    pc+1:0x7ffff50c7a14

定义三个指针分别指向整个数组,数组中第一个元素,第一个元素中的第一个元素,再分别打印地址和指针加1,由此我们就能看出差别

零长数组

在Linux中我们需要在不同客户机间发送信息,这些信息中包括部分定长数据和不定长数据,比如用户自定义的文本和文件,那么我们在定义这个数据包时就可以用上零长数组

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>

struct package
{
   int  msg_len;
   char msg[0];
};

void test_package(struct package *p)
{
    printf("total len:%ld\n",sizeof(struct package)+p->msg_len);
    printf("msg:%s\n",p->msg);
}


int main(int argc, char const *argv[])
{
    //char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,
    // 并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,
    // 或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
    char buffer[100];
    while (1)
    {
        fgets(buffer,100,stdin);

        struct package *p = malloc(sizeof(struct package)+strlen(buffer)+1);
        p->msg_len =strlen(buffer)+1 ;
        strncpy(p->msg , buffer , p->msg_len);
        test_package(p);
    }
    
    return 0;
}

在此例子中零长数组就担任了表示可变字长数据的作用,搭配利用malloc函数进行动态的内存分配实现不同字长数据发送

const指针

使用const修饰的指针只能通过该指针访问这块地址的数据,但是不能通过这个指针对此地址的数据进行更改,虽然不能通过指针修改数据了但是不代表指针本身的指向不可以改变,被const修饰的指针也可以改变指向。

cpp 复制代码
int const *p;
int * const p;

而第二种方法中const是直接修饰的p变量,因此此指针可以改变地址中的数据但是不能改变指向地址

相关推荐
摇滚侠3 分钟前
Spring Boot 3零基础教程,WEB 开发 通过配置类代码方式修改静态资源配置 笔记32
java·spring boot·笔记
聪明的笨猪猪2 小时前
Java JVM “内存(1)”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
_dindong2 小时前
Linux网络编程:Socket编程TCP
linux·服务器·网络·笔记·学习·tcp/ip
摇滚侠3 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 属性优先级 行内写法 变量选择 笔记42
java·spring boot·笔记
摇滚侠3 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 总结 热部署 常用配置 笔记44
java·spring boot·笔记
rechol3 小时前
汇编与底层编程笔记
汇编·arm开发·笔记
lzj_pxxw4 小时前
嵌入式开发技巧:舍弃标志位,用宏定义函数实现程序单次运行
笔记·stm32·单片机·嵌入式硬件·学习
润 下5 小时前
C语言——回调函数的典型示例(分析详解)
c语言·开发语言·人工智能·经验分享·笔记·程序人生
朝新_5 小时前
【EE初阶 - 网络原理】传输层协议
java·开发语言·网络·笔记·javaee
koo3645 小时前
李宏毅机器学习笔记27
人工智能·笔记·机器学习