C语言08--指针数组结合

news2024/11/16 1:41:25

 前言:

        这次的指针数组结合乃作者呕心沥血之作,大家翻翻进度条就知道了,内容十分干货,各位读者若能全部耐心解析读懂了,想必也能理解掌握C语言中的数组指针这两把利剑了。

指针数组结合:

指针数组

概念:他是一个数组,该数组中每一个元素的类型是指针。

语法:

int * arr[ 元素数量 ] ; // 整型指针数组
char * arr[ 元素数量 ] ; // 字符指针数组
float * arr[ 元素数量 ] ; // 浮点指针数组 
int * arr[ 元素数量 ] (int) ; // 函数指针数组

示例:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int a = 123 , b = 456 , c = 789 , d = 111 , e = 222 ;

    // 指针数组
    int *  arr1 [ 5 ] = {&a , &b , &c , &d , &e } ; // 整型指针数组

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

    // arr1[0][1] --》 *(arr1[0]+1)  -- > *(*(arr1+0)+1)
    printf("arr[0][1]:%d\n" , arr1[0][1] ) ;//得到数组首元素即*(&a+1),加一个&a大小,即指向&b,解引用得到456
    printf("**arr:%d\n" , **arr1 ) ;  // a = 123,arr1为数组首元素地址,解引用得到&a,再次解引用得到a的值123
    printf("**arr1+1:%d\n" , **arr1+1 ) ;  // a=124,同上,a的值加一得到124
    printf("**(arr1+1):%d\n" , **(arr1+1) ) ;  // b=456,arr1为数组首元素地址,+1偏移一个数组首元素地址大小,即指向数组第二个元素地址,解引用两次得到b的值
    printf("*(*arr1+1):%d\n" , *(*arr1+1) ) ;  // b =456,arr1为数组首元素地址,解引用得到&a,&a可以看作一个指针,+1偏移一个int地址大小,解引用得到b的值
    printf("*(*arr1+1):%d\n" , **(&arr1+1) ) ;  // 【越界】,&arr1为整个数组的地址,+1加一整个数组地址大小,指向数组之外,越界
    printf("**(*(&arr1+1)-1):%d\n" , **(*(&arr1+1)-1) ) ;  // e = 222,&arr1为整个数组的地址,+1加一整个数组的地址大小,解引用得到数组,指向数组
                                                            //末尾元素地址,-1减去一个数组元素地址大小,指向数组最后一个元素地址,两次解引用得到222
    

    char * arr2 [ 5 ] ; // 字符指针数组
    float *arr3 [ 5 ] ; // 浮点指针数组 
    // int *  arr4 [ 5 ] (int) ; // 函数指针数组

    
    return 0;
}

数组指针数组:

概念:他是一个数组,该数组中每一个元素都是指针,而这些指针指向的类型是数组类型。

语法:

type (* arr [5]) [N] ;

示例:

#include <stdio.h>


int main(int argc, char const *argv[])
{

    // 整型数组
    int arr1[2] ={1,2};
    int arr2[2] ={10,20};
    int arr3[2] ={100,200};


    // 整型数组指针
    int (*p1 )[2] = &arr1 ;
    int (*p2 )[2] = &arr2 ;
    int (*p3 )[2] = &arr3 ;

    // 整型数组指针数组
    int (* arr[3]) [2] = { p1 , p2 , p3} ;

    // arr[i] --> p1 
    // p1 = &arr1 
    // (*arr[i]) --> (*p1) --> (*&arr1) --> arr1
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 2; j++)
        {
            printf("(*arr[%d])[%d]:%d\n", i , j , *((**(arr+i))+j) );
            //(*arr[i])[j]  --> *((*arr[i])+j) --> *((**(arr+i))+j))
            //arr为数组首元素地址,+i加i个数组首元素地址大小,指向数组的第i+1个元素,第一次解引用得到数组的元素,即指针,再次解引用得到
            //指针指向的值,即一维数组,这时候得到一维数组arr1即首元素地址,+加上j个数组首元素地址大小,偏移j个元素,解引用得到一维数组的元素
            //这里会逐个打印出一维数组的全部元素
        }
        
    }
    
    

    return 0;
}

指针数组数组:

概念:他是一个数组,该数组中每一个元素都是数组,而这些数组中每一个元素都是指针。

语法:

 type * arr[3] [2] ;

数组指针:

概念:他是一个指针,该指针指向的是一个数组的类型

语法:

int (*ptr) [5]  ; // 一个指针,该指针指向拥有5个整型数据的数组 【整型数组指针】
char (*ptr) [5]  ; //  一个指向用于5个字符型的数组  【字符数组指针、 字符串数组指针】
#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr [5] = {1,20,3,4,5};

    

    int (*ptr) [5] ; 
    ptr = &arr ;

    int * p = arr ;
    // p == arr ;

    // ptr = &arr ;
    // **ptr = **&arr = *arr = 1
    printf("**ptr:%d\n", **ptr); // 1,ptr解引用得到arr,arr为数组首元素地址,解引用得到数组首元素,即1

    printf("**ptr+1:%d\n", **ptr+1); // 2,同上,1+1=2

    // *(*ptr+1) == *(*&arr+1) = *(arr+1)
    printf("*(*ptr+1):%d\n", *(*ptr+1)); // 20,ptr解引用得到数组arr,arr为数组首元素地址,+1加一个数组首元素地址大小,解引用得到数组第二个元素

    printf("*(*(ptr+0)+1):%d\n", *(*(ptr+0)+1)); // 20,ptr解引用得到arr,arr为数组首元素地址,+1加一个数组首元素地址大小,解引用得到数组第二个元素
    printf("ptr[0][1]:%d\n", ptr[0][1] ); // 20,相当于*(*(ptr+0)+1),同上
    

    // 【拓展】 预习函数的形参中出现数组
    // 一下两种写法是等价的,第一种写法是用于强调(告诉使用者)
    // 函数pipe 需要的地址必须有两个以上整型的合法内存空间
    // int pipe(int pipefd[2]);  
    // int pipe(int * pipefd);


    char msg1[] = "Hello";
    char msg2[] = "World";
    char msg3[] = "WangDaNiang";
    char msg4[] = "ShiDaSao";
    char msg5[] = "!";

    char * arr1[5] = { msg1, msg2, msg3 ,msg4 , msg5};
    char *(* ptr1) [5] = &arr1;
    
    // 操作练习通过ptr1来实现一下操作:
    //  1. 数组以上所有的字符串 "Hello" , "World" , "!" , "!" , "!"
    for (int i = 0; i < 5 ; i++)
    {
        // ptr1 = &arr1 
        //(*ptr1) = arr1
        printf("arr1[%d]:%s\n" , i , (*ptr1)[i] );
        //&arr1为整个数组的地址,ptr解引用得到数组arr1,arr1为数组首元素地址,arr1[i]得到数组的元素
    }
    
    //  2.1  尝试修改某一个字符
    //  arr1[2][5]  ->  (*ptr1)[2][5]
    //  ptr1 = &arr1 
    (*ptr1)[2][5] = 'A';
    //&arr1为整个数组的地址,ptr解引用得到数组arr1,arr1[2][5]得到数组第三个元素的第六个元素
    printf("arr1[2]:%s\n" , arr1[2] ); 


    //  2.2 尝试把所有的小写字符转换为大写
    for (int i = 0; i < 5 ; i++)
    {
        for (int j = 0; j < 32 ; j++)
        {
            if ((*ptr1)[i][j] != '\0' && 
                    (*ptr1)[i][j] >= 'a' && 
                        (*ptr1)[i][j] <= 'z'  )
            {
                (*ptr1)[i][j] -= 32 ;
            }
            
        }
        
    }

    for (int i = 0; i < 5 ; i++)
    {
        // ptr1 = &arr1 
        //(*ptr1) = arr1
        printf("arr1[%d]:%s\n" , i , (*ptr1)[i] );
        //&arr1为整个数组的地址,解引用得到数组arr1,然后arr1[i]访问数组每个元素,打印出改变大小写后的每个字符串
    }
    

    // char * p1 = &"Hello" ;
    
    return 0;
}

指针数组指针:

概念:他是一个指针,该指针所指向的内存是一个数组的类型,该数组存储的指针类型的元素。

语法:

int * (*ptr) [5] ;

数组指针指针:

概念:他是一个指针,该指针指向的是另外一个指针,该指针指向的是数组的地址。

语法:

int (*(*ptr)) [5] ;
    • 为什么数组名不可以直接赋值
int arr[3];
arr = 123 ;  // 该操作在试图改变arr的地址 , 有明显的逻辑问题(语法错误)
arr[0] = 123 ;
*(arr+1) = 456 ;
    • 数组名为什么不能++
int arr [3];
arr ++ ;  // arr++  -》 arr = arr + 1;    加完之后等式两边不等,左边为数组首元素地址,右边为数组第二个元素地址

指针数组详解

深入 理解char *,char ** ,char a[ ] ,char *a[] 的区别

1 数组的本质

数组是相同类型的多个元素的集合,在内存中分布在地址连续的单元中,所以可以通过其偏移量下标访问不同单元的元素。

2 指针

数据在内存中的地址也称为指针,如果一个变量存储了一份数据的地址,我们就称它为指针变量。

在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量

指针是一种变量,只不过它的内存单元中保存的是一个标识其他位置的地址。由于地址也是整数,在32位平台下,指针默认为32位,64位中默认为64位

面试题中如果没有指明系统的位数,32位和64系统的不同尺寸都要考虑到

3 指针的指向

指向的直接意思就是指针变量所保存的其他的地址单元中所存放的数据类型。

int * p ;

//p 变量保存的地址所在内存单元中的数据类型为整型

float *q;

// ........................................浮点型

不论指向的数据类型为那种,指针变量其本身永远为整型,因为它保存的是地址,大小为系统字长。

32位中指针大小为4字节,64位指针大小为8字节 

4 字符数组

字面意思是数组,数组中的元素是字符。

char str[10];

定义了一个有十个元素的数组,元素类型为字符。

C语言中定义一个变量时可以初始化。

char str[10] = {"hello world"};

当编译器遇到这句时,会把str数组中从第一个元素把hello world\0 逐个填入。。

C语言中规定数组代表数组所在内存位置的首地址,也是 str[0]的地址,即str =&str[0];

而printf("%s",str); 为什么用首地址就可以输出字符串。。

因为还有一个关键,在C语言中字符串常量的本质表示其实是一个地址,这是许多初学者比较难理解的问题。

举例:

char *s ;

s = "China";

为什么可以把一个字符串赋给一个指针变量。。

这不是类型不一致吗?

这就是上面提到的关键 。

C语言中编译器会给字符串常量分配地址,如果 "China", 存储在内存中的0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 .

s = "China" ,意识是什么,对了,地址。

其实真正的意义是 s ="China" = 0x3000;

把China 看作是字符串,但是编译器把它看作是地址0x3000,即字符串常量的本质表现是代表它的第一个字符的地址。

s = 0x3000

那么 %s ,它的原理其实也是通过字符串首地址输出字符串,printf("%s ",s); 传给它的其实是s所保存的字符串的地址。

比如

#include <stdio.h>
int main()
{
    char *s;
    s = "hello";    //这里"hello"为字符串常量,只能通过s访问字符串常量,不能通过s改变字符串常量
    printf("%p\n",s);
return 0;
}

可以看到 s = 0x00422020 ,这也是"hello"的首地址

所以,printf("%s",0x00422020);也是等效的。。

字符数组:

char str[10] = "hello";

前面已经说了,str = &str[0] , 也等于 "hello"的首地址。。

所以printf("%s",str); 本质也是printf("%s", 地址");

C语言中操作字符串是通过它在内存中的存储单元的首地址进行的,这是字符串的终极本质。

5. char * 与 char a[ ];

char *s;

char a[ ] ;

前面说到 a代表字符串的首地址,而s这个指针也保存字符串的地址(其实首地址),即第一个字符的地址,这个地址单元中的数据是一个字符,

这也与 s 所指向的 char 一致。

因此可以 s = a;

但是不能 a = s;

C语言中数组名(即首元素地址)可以赋值给指针表示地址, 但是却不能将指针赋给给数组名,它是一个常量类型,所以不能修改。

当然也可以这样:

char a [ ] = "hello";
char *s =a;    //这里a为字符串数组,a为数组首元素地址,即h的地址,赋给指针s,指针s和a都可以访问且修改字符串
for(int i= 0; i < strlen(a) ; i++)
{    printf("%c", s[i]);
    //或 printf("%c",*s++);//这里优先级++比*高,会先运算++符号,但是++是先赋值再加一,s解引用得到数组首元素,之后s++指针指向下一个元素
}    

字符指针可以用 间接操作符 *取其内容,也可以用数组的下标形式 [ ],数组名也可以用*操作,因为它本身表示一个地址 。

比如 printf("%c",*a); 将会打印出 'h'

char * 与 char a[ ] 的本质区别:

当定义 char a[10 ] 时,编译器会给数组分配十个单元,每个单元的数据类型为字符。。

而定义 char *s 时, 这是个指针变量,只占四个字节,32位,用来保存一个地址。。

sizeof(a) = 10 ;//sizeof计算时,表示整个数组大小,char类型占一个字节,数组总共10个元素.所以数组大小为10个字节

sizeof(s) = ? //32位中指针为4字节,64位中为8字节

当然是4了,编译器分配4个字节32位的空间,这个空间中将要保存地址。

printf("%p",s);

这个表示 s 的单元中所保存的地址。

printf("%p",&s);

这个表示变量本身所在内存单元地址。

用一句话来概括,就是 char *s 只是一个保存字符串首地址的指针变量, char a[ ]是许多连续的内存单元,单元中的元素为char ,之所以用 char *能达到

char a [ ]的效果,还是字符串的本质,地址,即给你一个字符串地址,便可以随心所欲的操所他。。但是,char* 和 char a[ ] 的本质属性是不一样的。。

6 char ** 与char * a[ ] ;

先看 char *a [ ] ;

由于[ ] 的优先级高于* 所以a先和 [ ]结合,他还是一个数组,数组中的元素才是char *,前面讲到char * 是一个变量,保存的地址。。

所以 char *a[ ] ={"China","French","America","German"};

同过这句可以看到, 数组中的元素是字符串,那么sizeof(a)是多少呢,有人会想到是五个单词的占内存中的全部字节数 6+7+8+7 = 28;

但是其实sizeof(a) = 16;//sizeof()表示整个数字大小,数组中存放的元素是指针,指针在32位系统中是4字节,所以这里数组为16字节

为什么,前面已经说到, 字符串常量的本质是地址,a 数组中的元素为char *指针,指针变量占四个字节,那么四个元素就是16个字节了

看一下实例:

#include <stdio.h>
int main()
{
char *a [ ] ={"China","French","America","German"};
printf("%p %p %p %p\n",a[0],a[1],a[2],a[3]);
return 0;
}

可以看到数组中的四个元素保存了四个内存地址,这四个地址中就代表了四个字符串的首地址,而不是字符串本身。

因此sizeof(a)当然是16了。

注意这四个地址是不连续的,它是编译器为"China","French","America","German"分配的内存空间的地址, 所以,四个地址没有关联,有关联的是&a[0].&a[1].&a[2]&a[3]的地址.

#include <stdio.h>
int main()
{
    char *a [ ] ={"China","French","America","German"};
    printf("%p %p %p%p\n",a[0],a[1],a[2],a[3]); //数组元素中保存的地址
    printf("%p %p %p%p\n",&a[0],&a[1],&a[2],&a[3]);//数组元素单元本身的地址
return 0;
}

可以看到 0012FF38 0012FF3C 0012FF400012FF44,这四个是元素单元所在的地址,每个地址相差四个字节,这是由于每个元素是一个指针变量占四个字节。

char **s;

char **为二级指针, s保存一级指针 char *的地址

举例:

char *a [ ] ={"China","French","America","German"};
char **s = a;  //a表示数组首元素地址,数组首元素又是"China"匿名字符串常量,匿名字符串常量

为什么能把 a赋给s,因为数组名a代表数组元素内存单元的首地址,即 a = &a[0] =0012FF38;

而 0x12FF38即 a[0]中保存的又是 00422FB8 ,这个地址,00422FB8为字符串"China"的首地址。

即 *s = 00422FB8 = "China";

这样便可以通过s 操作 a 中的数据

printf("%s",*s); //8s相当于*a,a为数组首元素的地址,*a取得数组首元素,数组首元素为"China"的地址,所以打印得到China

printf("%s",a[0]); //a[0],数组a的首元素为"China"的地址,打印得到China

printf("%s",*a); //a数组首元素地址,*解引用得到数组首元素,数组首元素为"China"字符串常量,"China"字符串常量实际上为

//匿名数组,字符型本身是它的数组名,所以这里"China"表示该匿名数组的首元素'C'的地址,打印得到"China"

都是一样的。

但还是要注意,不能a = s,前面已经说到,a 是一个常量。

#include <stdio.h>

int main(int argc, char const *argv[])
{
    char *a[] = {"China", "French", "America", "German"};
    char **s = a;

    //程序打印hina,a为数组首元素地址,*a得到数组首元素,数组首元素为"China"匿名数组,
    //"China"的名字即是字符串本身,所以"China"为数组名,代表"China"首元素地址,所以*a+1
    //为"China"首元素地址加上一个首元素地址大小,指向"China"的第二个元素h的地址
    printf("%c\n", *a + 1); 
    

    //&"China"表示一整个数组的地址,+1表示加上一整个数组的地址大小,指向"China"匿名数组之外,
    //再解引用得到未知的值,其实这里&"China"是错误的,&只能取变量的地址,而"China"为字符串常量
    //这里只是为了更好理解才这样写
    printf("%s\n",*(&"China"+1));  //未知

    //a表示数组首元素地址,+1表示加一个数组首元素地址大小,指向数组的第二个元素,解引用得到数组第二个元素
    printf("%s\n",*(a+1));  //程序打印French

    return 0;
}
 

再看一个易错的点:

char **s = "hello world";

//s为二级指针,它的类型是char ** ,而"hello world"字符串常量的类型是char *,两边类型不匹配

这样是错误的,

因为 s 的类型是 char ** 而 "hello world "的类型是 char*

虽然都是地址,但是指向的类型不一样,因此,不能这样用。,从其本质来分析,"helloworld",代表一个地址,比如0x003001,这个地址中的内容是 'h',为 char 型,而 s 也保存一个地址 ,这个地址中的内容(*s)(*s解引用二级指针中保存的地址即一级指针的地址) 是char* ,是一个指针类型, 所以两者类型是不一样的。

如果是这样呢?

char **s;
*s = "hello world";
//s为二级指针,指针必须初始化,不然就会变成野指针,这里二级指针即s没有初始化,为野指针.

貌似是合理的,编译也没有问题,但是 printf("%s",*s),就会崩溃

why??

咱来慢慢推敲一下。。

printf("%s",*s); 时,首先得有s 保存的地址,再在这个地址中找到 char * 的地址,即*s;

举例:

s = 0x1000;

在0x1000所在的内存单元中保存了"hello world"的地址 0x003001, *s = 0x003001;

这样printf("%s",*s);

这样会先找到 0x1000,然后找到0x003001; 

如果直接 char **s;

*s = "hello world";

s 变量中保存的是一个无效随机不可用的地址, 谁也不知道它指向哪里,*s 操作会崩溃。

所以用 char **s 时,要给它分配一个内存地址。

char **s ;

s = (char **) malloc(sizeof(char**));

*s = "hello world";

这样 s 给分配了了一个可用的地址,比如 s = 0x412f;

然后在 0x412f所在的内存中的位置,保存 "hello world"的值。。

再如:

#include <stdio.h>
void buf( char **s)
{
    *s = "message";
}
int main()
{
    char *s ;
    buf(&s);
    printf("%s\n",s);
}
//char *s定义了一个char类型的一级指针,取地址后&s为char ** 二级指针类型,然后传入buf函数,解引用得到一级指针,"message"为字符串常量实际上是匿名数组,
//存放在数据段中的常量区,所以函数buf在栈空间中被释放后,"message"字符串常量内存空间并不会被释放,所以后面主函数中一样可以访问字符串常量,但是只能访问
//不能通过指针对字符串常量进行修改删除等操作,字符串本身就是数字名,这里数组名即代表字符串常量的首元素地址,为char *类型,所以赋给函数中char *的*s一级
//指针,将主函数中的s指向数据段中的字符串常量"message"的地址,打印得到字符串

二级指针的简单用法,说白了,二级指针保存的是一级指针的地址,它的类型是指针变量,而一级指针保存的是指向数据所在的内存单元的地址,虽然都是地址,但是类型是不一样的。

注:字符串常量:

字符串常量是指在程序中直接使用双引号括起来的字符串。例如:"hello"、"world"等都是字符串常量。字符串常量在C语言中是不可变的,也就是说,一旦定义了字符串常量,就不能再修改它的值。

在C语言中,你可以使用双引号括起来的任何字符序列来定义字符串常量。

字符串常量的本质表现是代表它的第一个字符的地址,这是因为在C语言中,字符串常量实际上是一个匿名数组,被存储为字符数组,并以空字符 '\0' 结尾,并且字符串即是匿名数组的数组名,而数组名一般情况下又代表数组首元素地址。因此,当你声明一个字符串常量时,实际上是在数据段中创建一个字符数组,并将该字符串中每个字符的地址存储在匿名数组中。

在C语言中,字符串常量是不可变的,因此任何尝试修改字符串常量的操作都是不允许的。这也是为什么在前面的例子中,尝试使用指向字符串常量的指针来进行修改会导致崩溃的原因。

当你写下 char str[] = "helloworld"; 时,实际上发生了:

  1. 创建了一个字符数组 str,并分配了足够的内存来存储字符串 "hello world"。
  2. 在内存的数据段创建了一个匿名数组,匿名数组存放字符串常量"hello world" 
  3. 将字符串 "hello world" 中的每个字符复制到了字符数组 str 中,并在末尾添加了一个空
  4. 字符 '\0' 来表示字符串的结束。
  5. 编译器会自动在字符数组的末尾添加一个空字符 '\0' 来表示字符串的结束。这个过程是自动进行的,你不需要手动添加空字符

  

因此,你可以对字符数组 str 进行任意操作,因为它是可变的。这包括修改、反转、拼接等操作,如果将数组的地址赋给一个指针,通过指针也可以对字符串进行任意操作。

但是,如果直接将字符串常量赋给指针,(char *p = "hello world",此时只能通过指针访问字符串常量,而通过指针直接对字符串常量"hello world" 进行操作,就会导致编译错误或者运行时错误

{"China","French","America","German"}这几个都是字符串常量,系统在内存中为他们分配了不同的地址,程序通过char *a[ ]来存放他们的地址,所以这几个字符串的地址不是数组空间的地址,数组空间地址是连续的,但是存放的字符串常量地址不是连续的,所以这几个地址没有关系.

//"hello"为字符串常量实际上是匿名数组,"hello"字符串本身即数组名,而在这里数组名表示数组首元素地址, 所以指针msg存放了hello的首元素h的地址
    char *msg = "hello";
    printf("%c\n",*msg);  // 打印出'h',
    printf("%d\n",*msg);  // 打印出'h'的ASCII码值
    printf("%p\n",*msg);  // 打印出'h'的地址值,这是错误的用法
    //%p是用来打印指针的地址的,而*msg是字符,不是指针
   
    printf("%d\n",msg);   // 打印出msg的值,即存储的地址
    printf("%p\n",msg);   // 打印出msg的值,即存储的地址
    printf("%s\n",msg);   // 打印出整个字符串"hello"    

  1. printf("%c\n",*msg); - 这会打印出'h',因为*msg解引用了指针msg,得到了字符串的第一个字符'h'。
  2. printf("%d\n",*msg); - 这会打印出104,因为'h'的ASCII码值为104。
  3. printf("%p\n",*msg); - 这是错误的用法,%p是用来打印指针的地址的,而*msg是字符'h',不是指针,所以这里的用法是不正确的。
  4. printf("%d\n",msg); - 这会打印出msg的值,即存储的地址。
  5. printf("%p\n",msg); - 这也会打印出msg的值,即存储的地址。
  6. printf("%s\n",msg); - 这会打印出整个字符串"hello"。

请注意,对于指针变量msg,*msg表示的是指针所指向的值,而msg表示的是指针本身的值,即存储的地址。

*msg已经解引用msg的首字符的地址的值,即'h',所以打印的时候都是打印关于'h'的 

/**总结:

 *数组名字除了sizeof和定义和&情况下代表整个数组,其他情况下代表数组首元素的地址,注意是首元素的地址.

 * 首元素地址和首元素不一样概念,首元素为s[0],首元素地址为&s[0]

 *而&s[0]和&s又不一样,虽然他们地址都是一样的,都是数字首元素的地址,但是意义不同,&s[0]为数组首元素地址,&s为整个数组的地址

 *+1时各自不同,&s[0]为数组首元素地址加上一个数组首元素地址大小,加完指向数组的第二个元素的地址

 *&s为数组的地址加上一整个数组的地址大小,即数组地址加上数组类型字节大小和数组元素个数的乘积大小

 *

 *二维数组解引用数组首元素地址比较不一样,第一次解引用*s得到数组首元素,而数组首元素类型为数组类型,即此时*s同时也是得到了

 * 二维数组首元素的首元素的地址,地址和二维数组首元素地址一样,但是意义不一样.

 * 第二次解引用得到数组首元素的首元素

 *

 * 数组和指针+1时都需要注意原先为什么类型,多少字节大小,+1就是加上原来类型字节大小

 *比如&s+1为整个数组地址即数组首元素地址加上一整个数组地址大小,大小为数组元素类型字节大小和数组元素个数大小乘积,加完指向数组末尾

 * s+1为数组首元素地址加上一个首元素地址大小,加完指向数组第二个元素的地址

 *还有一个在给数组指针赋值地址时,要给数组加上&   如 int arr[3] = {1,2,3};  int *p[3] = &arr; //这里指针p为int [3] 类型,arr为数组首元素地址,为int *类型,和指针p类型不匹配

,所以加上&,表示取一整个数组的地址,一整个数组的地址类型为int [3],两边类型匹配

 *因为不加取址符,表示把数组首元素地址赋给指针,但是数组首元素地址为int类型,指针为数组类型(int [3]),加上取址符为整个数组类型的地址。

 * 这时候*p相当于*(&arr) =arr,虽然*p和p地址数值都还是数组首元素地址,但是他们意义不同,*p为数组首元素类型,p为整个数组类型

*/

指针数组结合理解题:

  • 理解题1:

#include <stdio.h>
int main(void)
{
    int (*p)[5] = NULL;  //数组指针,
    int arr[5] = {1,2,4,5,7};
    p = &arr;//这里给指针赋值,要加上&,arr只是代表数组首元素地址,数组首元素地址是int类型,而指针是int [5]类型,加上&表示取一整个数组的地址,类型为int [5]
    printf("arr    = %p\n",arr);      //数组首元素地址,arr为数组首元素地址
    printf("&arr   = %p\n",&arr);     //整个数组的地址,即首元素地址 
    printf("p      = %p\n",p);         //整个数组的地址,即首元素地址
    printf("*p     = %p\n",*p);       //p为数组地址,*p得到数组元素地址,即首元素地址
    printf("p+1    = %p\n",p+1 );      //p为一整个数组地址,+1表示加一整个数组地址大小,跳到了数组之外的地址,大小大概为首元素地址加20
    printf("(*p)+1 = %p\n",(*p)+1);   //p为一整个数组的地址,解引用得到数组首元素地址,+1表示加上一个数组首元素地址大小,指向数组第二个元素地址
   
    return 0;
}
/***输出结果:
    * arr    = 0xffffcbd0
   &arr   = 0xffffcbd0
   p      = 0xffffcbd0
   *p     = 0xffffcbd0
   p+1    = 0xffffcbe4
   (*p)+1 = 0xffffcbd4
*/
/**总结:
 * 数组的特殊性,首元素地址代表整个数组的地址,数组地址即首元素地址,
 * 但是在&arr中虽然数值和首元素地址一样,类型不一样,意义不同,+1也各不相同
 * arr+1为加一个数组首元素地址大小,即跳到数组第二个元素地址.
 * &arr+1为加一整个数组地址大小,由数组类型及元素个数乘积决定。
 * 数组首元素地址是首元素地址,&arr是代表整个数组的地址,只是刚好一样
*/

  • 理解题2:

#include <stdio.h>
int main(void)
{
    int a[5] = {1,2,3,4,5};
    int b[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
//打印结果:
// &a     = 0xffffcbd0
// &a + 1 = 0xffffcbe4
// a      = 0xffffcbd0
// a + 1  = 0xffffcbd4
// &b     = 0xffffcba0
// &b + 1 = 0xffffcbd0
// b      = 0xffffcba0
// b + 1  = 0xffffcbb0
// *b     = 0xffffcba0
// *b + 1 = 0xffffcba4
// **b     = 1
// **b + 1 = 2
    printf("&a    = %p\n",&a);       //整个数组的地址,数值和数组首元素地址一样
    printf("&a+ 1 = %p\n",&a+1);     //整个数组的地址加上一整个数组地址大小,数值为首元素地址加20个字节
    printf("a     = %p\n",a);        //数组首元素地址  
    printf("a+ 1  = %p\n",a+1);      //数组首元素地址加一个首元素地址大小,即数组第二个元素的地址
    printf("\n");
    printf("&b    = %p\n",&b);       //整个数组的地址,数值和数组首元素地址一样
    printf("&b+ 1 = %p\n",&b+1);     //整个数组的地址加上一整个数组地址大小,数值为首元素地址加48字节
    printf("\n");
    printf("b     = %p\n",b);      //数组首元素的地址
    printf("b+ 1  = %p\n",b+1);    //数组首元素的地址加上一个数组首元素地址大小,即数组第二个元素,数组首元素地址加16字节
    printf("\n");
    printf("*b    = %p\n",*b);       //数组首元素地址解引用,得到第一个元素,即{1,2,3,4},而{1,2,3,4}的首元素地址和b数组首元素地址一样
    printf("*b+ 1 = %p\n",*b+1);     //由上一句可得,*b+1为加一个{1,2,3,4}里的首元素地址大小,即指向2的地址,数值为首元素地址加4字节
    printf("\n");
    printf("**b    = %d\n",**b);      //*b得到数组第一个元素{1,2,3,4},解引用第一个元素的首元素地址,得到1
    printf("**b+ 1 = %d\n",**b+1);   //由上一句,1+1=2
    return 0;
}
/**总结:
 * 数组名字除了sizeof和定义和&情况下代表整个数组,其他情况下代表数组首元素的地址,注意是首元素的地址.
 * 首元素地址和首元素不一样概念,首元素为s[0],首元素地址为&s[0]
 * 而&s[0]和&s又不一样,虽然他们地址都是一样的,都是数字首元素的地址,但是意义不同,&s[0]为数组首元素地址,&s为整个数组的地址
 * +1时各自不同,&s[0]为数组首元素地址加上一个数组首元素地址大小,加完指向数组的第二个元素的地址
 * &s为数组的地址加上一整个数组的地址大小,即数组地址加上数组类型字节大小和数组元素个数的乘积大小
 * 
 * 二维数组解引用数组首元素地址比较不一样,第一次解引用*s得到数组首元素,而数组首元素类型为数组类型,即此时*s同时也是得到了
 * 二维数组首元素的首元素的地址,地址和二维数组首元素地址一样,但是意义不一样.
 * 第二次解引用得到数组首元素的首元素
 * 
 * 数组和指针+1时都需要注意原先为什么类型,多少字节大小,+1就是加上原来类型字节大小
 * 比如&s+1为整个数组地址即数组首元素地址加上一整个数组地址大小,大小为数组元素类型字节大小和数组元素个数大小乘积,加完指向数组末尾
 * s+1为数组首元素地址加上一个首元素地址大小,加完指向数组第二个元素的地址
*/

  • 理解题3:

#include <stdio.h>
int main(void)
{
    int arr[] = { 1, 3, 5, 7, 9};
    int len= sizeof(arr)/ sizeof(int);  //求数组长度
    int i;
//输出结果:
// *(arr+0) = 1
// *(arr+1) = 3
// *(arr+2) = 5
// *(arr+3) = 7
// *(arr+4) = 9 
    for(i=0; i<len; i++) 
    {
        //arr为数组首元素地址,+i为加上i个数组首元素地址大小
        //解引用表示数组第i+1个元素
        printf("*(arr+%d) = %d\n",i, *(arr+i) ); 
    }
        
    printf("\n");
    
    return 0;
} 

  • 理解题4:

#include <stdio.h>
int main(void)
{
    int arr[] = { 1, 3, 5, 7, 9};
    int i, *p = arr;
    int len = sizeof(arr) / sizeof(int);
    
    for(i=0; i<len; i++)
    {
        //p为数组首元素的地址,加i为数组首元素地址加上i个数组首元素地址,
        //相当于数组的第i+1个数组元素地址,解引用得到数组元素
        printf("*(p+%d) = %d\n",i, *(p+i));  
    }
    printf("\n");
    
    return 0;
}

理解题5:

#include <stdio.h>
int main(void)
{
    int arr[] = { 1, 3, 5, 7, 9};
    int *p = &arr[2]; 
    
    //*p为数组第三个元素的地址,*p为数组第三个元素3
    //(p+—i)为数组第三个元素地址加减i个第三个元素地址大小,即偏移i个元素
    //解引用得到数组元素
    printf("%d, %d, %d, %d, %d\n", *(p-2), *(p-1), *p, *(p+1), *(p+2) );
    
    
    return 0;
}

  • 理解题6:

#include <stdio.h>
int main(void)
{
    int arr1[] = { 12, 35, 54, 72, 99};
    int arr[] = { 1, 3, 5, 7, 9};
    int i, *p = arr, len = sizeof(arr) / sizeof(int);
    
    printf("arr = %p\n", arr);    //arr为数组首元素地址
    printf("p = %p\n", p);    //p为数组arr首元素地址
    for(i=0; i<len; i++)
    {
        //p存放数组首元素地址,这里运算符优先级 ++ > *,
        //所以会先计算p++,但是因为p++先赋值后运算,
        //所以p先解引用得到1,这里先输出1
        //接着p++,p为数组首元素地址,+1代表加一个数组首元素地址
        //p指向数组第二个元素地址
        printf("%d\n", *p++ );    
        //i为0,p在这里为数组第二个元素地址,表示第0次指向第二个元素地址
        printf("%d ---> %p\n",i, p);
        //p解引用得到3,接着*p打印得到3,然后*p自加一,得到4,*p索引到指针指向的目标,并把数组第二个元素变为4
        printf("%d\n", (*p)++ );  
        printf("======================\n");
    }
    printf("======================\n");
    for(i=0; i<len; i++)
    {
        
        printf("arr[%d] = %d\n",i,arr[i] );
    }
    printf("\n");
    return 0;
}
//打印结果:
// arr[0] = 1
// arr[1] = 4
// arr[2] = 6
// arr[3] = 8
// arr[4] = 10

  • 理解题7:


#include <stdio.h>
int main(void)
{
    int a = 1, b = 2, c = 3;
    int *arr[3]= {&a, &b, &c};//整型指针数组
    int **parr = arr;
    
    //arr[0]为数组首元素,*解引用得到*(&a)即1,其他同理
    printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);
    //parr为数组首元素地址,parr+i为数组首元素地址加上i个数组首元素地址大小
    //即数组首元素地址偏移到第i+1个元素地址,*解引用得到&a,再解引用得到*(&a) = 1
    printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2));
    return 0;
}

  • 理解题8:

#include <stdio.h>
int main(void)
{
    char *lines[5]=      //字符指针数组
    {
    "COSC1283/1984",
    "Programming",
    "Techniques",
    "is",
    "greatfun"
    };
  
  
    char *str1 = lines[1];         
    char *str2 = *(lines + 3); 
    char c1    = *(*(lines + 4) + 6);  
    char c2    = (*lines + 5)[5];  
  
    char c3    = *lines[0] + 2;
  
  //打印结果:
// str1 = Programming
// str2 = is
// c1   = f
// c2   = 9
// c3   = E
  //lines[1]是字符串数组的元素之一,lines[1]元素是"Programming"的首地址,
  //str1指向该首地址,所以打印"Programming"字符串
    printf("str1 = %s\n", str1);   
  
  //lines+3,代表lines首元素的地址加上3个首元素地址大小,即指向数组第四个元素
  //即"is"的地址,然后解引用得到"is",打印得到is
    printf("str2 = %s\n", str2);   
  //lines+4偏移到数组第五个元素的地址,解引用得到"great fun",然后
  //+6偏移6个"great fun"首元素地址大小,指向"great fun"第七个元素的地址,
  //解引用得到f  
    printf("c1   = %c\n", c1);     
  //lines数组首元素地址解引用得到"COSC1283/1984"的首地址,加五即
  // 加上五个"COSC1283/1984"的首地址大小,偏移到"COSC1283/1984"的元素2
  // 然后(&2)[5]等于*(&2+5),地址再偏移加上五个&2大小,即偏移到了9的地址,
  //然后解引用得到9
    printf("c2   = %c\n", c2);
  //lines[0]即"COSC1283/1984"即"COSC1283/1984"的首元素地址,然后解引用得到C
  //'C'+2得到E    
    printf("c3   = %c\n", c3);    
  
    return 0;
}

  • 理解题9:

#include <stdio.h>
int main(void)
{
    int i;
    int num;
    int (*p)[5]= NULL;  //数组指针 
    int arr[5] =  {5,2,4,5,7};
    p = &arr;
    printf("=====================================\n");
    num = sizeof(arr)/sizeof(arr[0]);
//打印结果:
// *p[0] = 5
// *p[1] = 0
// *p[2] = -13232
// *p[3] = 1
// *p[4] = 0
    for(i=0;i<num;i++)
    {
        //*p[i]相当于**(p+i),p为数组的地址,加i表示加i个数组的地址
        //加一表示加一整个数组地址大小,然后p指向数组末尾地址
        printf("*p[%d] = %d\n",i,*p[i]);
    }
    printf("=====================================\n");
    
//打印结果:
// *(p+0) = 0xffffcbc0
// *(p+1) = 0xffffcbd4
// *(p+2) = 0xffffcbe8
// *(p+3) = 0xffffcbfc
// *(p+4) = 0xffffcc10  
    for(i=0;i<num;i++)
    {
        //p为数组的地址,加i表示加i个数组的地址
        //加一表示加一整个数组地址大小,然后p指向数组末尾地址,
        //输出地址每个地址相差数组元素字节大小和数组元素个数的乘积
        //即相差20字节
        printf("*(p+%d) = %p\n",i,*(p+i));  
    } 
    printf("=====================================\n");
    
//打印结果: 
// p[0] = 0xffffcbc0
// p[1] = 0xffffcbd4
// p[2] = 0xffffcbe8
// p[3] = 0xffffcbfc
// p[4] = 0xffffcc10
    for(i=0;i<num;i++)
    {
        //p[i]相当于*(p+i)
        //p为数组的地址,加i表示加i个数组的地址
        //加一表示加一整个数组地址大小,然后p指向数组末尾地址,
        //输出地址每个地址相差数组元素字节大小和数组元素个数的乘积
        //即相差20字节
        printf("p[%d] = %p\n",i,p[i]);     
    }
    printf("=====================================\n");
    
//打印结果: 
// (*p)[0] = 5
// (*p)[1] = 2
// (*p)[2] = 4
// (*p)[3] = 5
// (*p)[4] = 7
    for(i=0;i<num;i++)
    {
        //*p相当于*(&arr)相当于arr表示数组首元素地址,
        //然后arr[i]输出数组元素
        printf("(*p)[%d] = %d\n",i,(*p)[i]); 
    }
    
    return 0;
}
//总结:先判断是指针还是数组,具体看标识符和哪个优先级高的符号结合
//接着判断元素类型,数组的元素类型是什么,指针指向的类型是什么
//数组:判断数组名字含义,在sizeof运算符,&arr,定义时数组名字代表数组元素首地址
//注意:因为数组的特殊性,所以arr和&arr地址相同,但是他们意义不同,arr表示数组首元素地址
//&arr表示整个数组的地址,各自加一时也不同,arr为加一个数组首元素地址大小,&arr为加一个数组地址大小
//数组索引可以arr[0]直接索引,也可通过指针索引,arr[0]相当于:*(arr+i).
//注意数组的偏移量和第几个元素不一样,数组偏移量是从0开始偏移,第几个元素是从1开始计数
//将数组地址赋给指针时,要注意是赋值arr还是&arr,特别是二维数组更加不一样
//指针:指针是一个存放地址的变量,存放地址的类型要和指针的类型一致,特别是二级指针,数组指针等等
//指针+1运算时,要注意指针是什么类型的,是加多大的元素地址大小

结语:

        写到这里,大概18550多字,不知道读者是否有耐心读到这里,但是我相信,看到这里的并且认真研究过的,相信你对指针数组的理解已经十分透彻明白了,恭喜你,掌握了C语言的数组指针这两把利剑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2106857.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

异步编程学习

UniTask UniTask 访问UniTask的GitHub的主页可以直接下载unity Package进行导入&#xff0c;或者通过 Package Manager导入&#xff0c;导入完成之后根据文档加一下 宏定义“UNITASK_DOTWEEN_SUPPORT” 这样就可以正常的控制DoTween了 2.UniTask 的简单使用 // UniTask 是可以作…

lambda表达式用法——C#学习笔记

“Lambda 表达式”是一个匿名函数&#xff0c;它可以包含表达式和语句&#xff0c;并且可用于创建委托或表达式目录树类型。 实例如下&#xff1a; 代码如下&#xff1a; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.…

景联文科技:专业视频标注服务助力计算机视觉应用升级

视频标注是指对视频内容进行分析&#xff0c;并在视频中的特定对象、行为或事件上添加标签的过程。 视频标注包括&#xff1a; 1. 对象检测与跟踪 •对象检测&#xff1a;在每一帧中识别并定位特定的对象&#xff0c;如人、车、动物等。 •对象跟踪&#xff1a;跟踪这些对象…

你知道吗?Python现在这么火爆的真相!

Python之所以如此火爆&#xff0c;主要得益于其多方面的优势和广泛的应用场景。以下是对Python火爆原因的详细归纳&#xff1a; 1. 易学易用 语法简洁&#xff1a;Python的语法设计非常简洁、直观&#xff0c;易于学习和理解。初学者能够快速上手&#xff0c;减少编写代码时的…

解决linux云服务器ping不通另一台linux云服务器的问题

这里以华为云服务器为例 1、找到云主机详情&#xff1a;xxx实例 / 实例概览 / 服务器详情&#xff0c;找到安全组 2、找到云主机当前在使用的安全组&#xff0c;添加入向规则 注意这里要选择 ICMP 协议&#xff0c;因为 ping 的协议是基于 ICMP 协议工作的 3、再次ping即可通 …

多目标应用:四种多目标优化算法(NSGA2、NSPSO、NSDBO、NSCOA)求解柔性作业车间调度问题(FJSP),MATLAB代码

一、柔性作业车间调度问题 柔性作业车间调度问题(Flexible Job Scheduling Problem, FJSP) 的描述如下&#xff1a;n个工件 { J , J 2 , . . , J n } \{J,J_2,..,J_n\} {J,J2​,..,Jn​}要在 m m m 台机器 { M 1 , M 2 , . . , M m } \{M_1,M_2,..,M_m\} {M1​,M2​,..,Mm​} …

linux基础IO——动静态库——实现与应用学习、原理深度解析

前言&#xff1a;本节内容是基础IO部分的动静态库。 本节内容&#xff0c; 我们将站在实现者的角度上自己实现一下动静态库&#xff0c; 并且会站在使用者的角度上使用我们自己实现的库。过程中牵扯到许多新的知识&#xff0c; 最后我们会重谈一下我们的进程。 理解一下有了动静…

【Python基础】想学好Python,就必须要知道的Python知识。一篇文章带你了解Python,学好Python!!!

Python知识涵盖面非常广泛&#xff0c;从基础语法到高级特性&#xff0c;再到丰富的库和框架&#xff0c;都是Python学习的重要组成部分。 一、基础语法 变量与数据类型&#xff1a; Python是动态类型语言&#xff0c;变量无需声明类型&#xff0c;直接赋值即可。常见的数据类…

织梦dedecms后台文章列表显示空白或有页码显示不了文章的解决方案

cms系统在用的过程才会发现更多的问题&#xff0c;dedecms也不例外。 问题描述&#xff1a; 可以正常登录&#xff0c;列表正常显示&#xff0c;文章页码也是正常&#xff0c;就是无法显示文章的标题。 分析原因&#xff1a; 1、是否改动源码 2、数据库是否完整 在数据库是…

非标独立设计选型--二十一--滚子链选型计算

链传动 相比于带传动 1、噪音大、震动大---平稳性不加、精度不够 运行速度不要太快…… 2、负载能力强&#xff0c;抗造---大负载、线性传动---抗冲击 【工况1】负载较大&#xff08;几百kg---几吨&#xff09;、运行速度较缓慢的场合 3、预紧力不需要像同步带那样大…

OpenAI 的发展启示录

OpenAI 的发展启示录 前言OpenAI 的发展启示录 前言 在当今科技迅猛发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度改变着我们的生活和工作方式。OpenAI 作为人工智能领域的先驱者&#xff0c;其发展路径和成就备受关注。它的每一次突破和创新&…

信也持续构建集群容器化改造之路

1. 前言 随着应用构建需求增加以及新构建场景引入&#xff0c;公司对构建系统的扩展性、稳定性要求日益提高。多语言构建&#xff08;如Golang、Java、Python、Node.js 等&#xff09;所依赖的环境&#xff0c;部署在同一台物理机上时&#xff0c;使构建机环境维护困难&#xf…

Elasticsearch Mapping 详解

1 概述 映射的基本概念 Mapping 也称之为映射&#xff0c;定义了 ES 的索引结构、字段类型、分词器等属性&#xff0c;是索引必不可少的组成部分。 ES 中的 mapping 有点类似与DB中“表结构”的概念&#xff0c;在 MySQL 中&#xff0c;表结构里包含了字段名称&#xff0c;字…

【gtokentool】元宇宙nft区块链是什么

元宇宙 元宇宙的定义 元宇宙&#xff08;Metaverse&#xff09;这个词起源于Neal Stephenson在1992年出版的小说《雪崩》&#xff0c;Metaverse由Meta&#xff08;意即“超越”、“元”&#xff09;和verse&#xff08;意即“宇宙universe”&#xff09;两个词构成。元宇宙是整…

安卓13带有系统签名的应用不能正常使用webview 调用webview失败 系统应用app apk

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 android版本高一些的平台,经常会遇到一些权限安全问题,像客户的应用如果带有系统签名,会导致不能正常使用webview问题。 2.问题分析 我们log信息,可以发现下面的提示: Fo…

权威解读|2024固定网国内数据传送业务办理指南

一、固定网国内数据传送业务是什么&#xff1f; 固定网国内数据传送业务&#xff0c;是指互联网数据传送业务以外的&#xff0c;在固定网中以有线方式提供的国内端到端数据传送业务。主要包括基于IP承载网、ATM网、X.25分组交换网、DDN网、帧中继网络的数据传送业务等。 根据…

佰朔资本:股票的买卖点有哪些?如何判断?

1、根据均线找买卖点 当股价跌破5日均线时&#xff0c;投资者可以将其作为卖点&#xff1b;股价向下跌触碰5日均线之后&#xff0c;出现反弹向上运转的痕迹&#xff0c;投资者可以将其作为买点。 2、根据MACD方针和KDJ方针找买卖点 当MACD方针或许KDJ 方针出现高位死叉时&am…

AI绘画神器 Fooocus 2.0 来啦!出图质量升级+新增4大实用功能!低配电脑必用的AI绘画工具教程

大家好&#xff0c;我是灵魂画师向阳 之前为大家推荐过一款融合了 midjourney 和 stable diffusion 优点的 AI 绘画工具 Fooocus&#xff0c;它用的是开源的 SDXL 1.0 模型&#xff0c;可以免费生成高质量的图像&#xff0c;同时操作界面又如 Midjourney 般简单&#xff0c;受…

三菱机器人手柄维修示教器维修手操器面板等

机器人手柄维修&#xff08;示教器维修&#xff09;故障现象包括&#xff1a;黑屏、指示灯无任何显示&#xff0c; 上电蓝屏、白屏&#xff0c;通电几分钟后屏幕变为蓝屏&#xff0c;主板故障&#xff0c;通讯时有时无&#xff0c; 触摸失灵&#xff0c;有时白屏&#xff0c;触…

【苍穹外卖】Day 6 HttpClient、wx小程序

1 HttpClient HttpClient 是 Apache Jakarta Common 下的子项目&#xff0c;可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新的版本和建议 HttpClient 是一个用于发送 HTTP 请求并接收响应的类或库&#xff0c;在…