Linux和C语言(Day09)

news2025/1/10 16:31:39

一、学习内容

  1. 指针

    1. 指针的概念

      1. 什么是内存
        1. 内存是计算必不可少的硬件设备,一般说到内存常会说TA多大——M、G、T

        2. 内存单位:bit【位,最小单位】 Byte【字节,基本单位】 KB MB GB TB

      2. CPU如何从内存取数据
        1. 通过内存地址去取 将内存条想象成一幢超级摩天大楼,里面有很多很多房间[bit/Byte],如何区分这些房间,编号。 编号又称之为地址,也称之为指针 地址、编号、指针【常量】 用于存储地址的变量,称为指针变量 指针变量 【变量】

      3. 约定俗称的
        1. 说指针 指的是指针变量

        2. 说地址 指的是内存编号

    2. 定义指针的格式

      1. 语法:存储类型 数据类型 *指针名

      2. 分析:数据类型:基本 构造 指针 空 eg: int *p float *p char *p double *p int(*p)[3] strcut *p int **p void *P

    3. 指针的初始化

      1. 直接初始化
        1. int a=10 int *p =&a

      2. 间接初始化
        1. int a=10; int *p; //int数据类型 *指针的标志 p是指针名 p =&a; //正确赋值 *p=&a; //错误赋值

      3. 用已经赋值好的指针给另一个指针赋值
        1. int a=10; int *p=&a; int *q=p //q=p

      4. 不知道指针指向哪里先置空,防止野指针
        1. int *p=NULL; 判断指针为空 if(p==NULL)//if(!p)

      5. 大小端判断
        1. 小端:数据低位存储地址低位

        2. 大端:数据低位存储地址高位

        3. 代码:

           int main(){
               int a=0x12345678; 
              char *p=(char *)&a; 
              if(*p == 0x78){
                   printf("小端存储\n");
               }else if(*p == 0x12){ 
                  printf("大端存储\n");
               } 
              return 0; 
          }

    4. 指针的字节大小

      1. 指针变量字节大小,在64位系统中是8字节,32位系统中是4字节

    5. 指针的相关操作

      1. & :取地址符 【内存地址,房间编号】

      2. * :解址/取值 【推开房间门看数据】

      3. & 和 * 互为逆运算符 *&p &*p 其实都是p

    6. 指针的运算

      1. 算术运算: + - ++ --

      2. 关系运算: > < >= <= == !=

      3. 赋值运算: = +=  -=

  2. 指针和一维数组

    • 地址的等价关系

      • int a[5], *p=a; p ==a ==&a[0] p+i == a+i == &a[i] == &p[i]

    • 元素的等价关系

      • *p == *a ==a[0] *(p+i) == *(a+i)==a[i] ==p[i]

  3. 指针和二位数组

    • 通过数组名a和a+1对比发现,通过数组名a移动控制的一整行

    • 你还能定义 int *p=a;吗?不报错但是操作单位是一行

    • 一行包含多个连续存储的相同类型的数据——这个容器不就是数组,将指针与数组结合进行结合操作

    • 首先是一个指针,但是这个指针操作数组

  4. 数组指针

    • 本质是一个指针:指向一个地址,也就是行指针

    • 语法:int (*p)[3]

    • 作用:一般用于二维数组传参的时候

    • 数组指针 指向 二位数组 地址的等价关系

      • &p[i][j] == p[i]+j == *(p+i)+j

    • 元素的等价关系

      • p[i][j] == *(p[j]+j) == *(*(p+i)+j)

  5. 指针数组

    • 本质是一个数组,数组的类型是指针,也就是存储的是地址

    • 整型数组:int a[5]={1,2,3,4,5}//类型是整数,存的都是整数

    • 指针数组:int *a[5]={&,&,&,&,&}

  6. 二级指针

    • 本质是一个指针,数据类型也是指针,指针的指针,地址的地址

    • 语法:int **p //二级指针 int ***p //多级指针

  7. 通用指针

    • 语法:void *p;

    • 作用:可以在使用的时候,强转为你想要的任意类型的指针,一般用于传参

  8. 脑图

二、作业

1.用变量a给出下面的定义(3C科技、宇视科技,神思电子,中安云科,北京凝思软件)

a) 一个整型数:

b) 一个指向整型数的指针:

c) 一个指向指针的的指针,它指向的指针是指向一个整型数:

d) 一个有10个整型数的数组:

e) 一个指向有 10个整型数数组的指针:

解析:

一个整型数

定义一个整型数,直接声明一个 `int` 类型的变量:
int a;

这是最简单的整型变量声明,`a` 是一个整型数。

一个指向整型数的指针

定义一个指向整型数的指针,需要使用 `*` 来声明指针

int *a;

 这里的 `a` 是一个指针,它指向一个 `int` 类型的变量。使用 `*` 表示该变量是指针类型。

 一个指向指针的指针,它指向的指针是指向一个整型数

要声明一个指向指针的指针,使用 `**`:

int **a;

 `a` 是一个二级指针,它指向一个一级指针,该一级指针指向一个 `int` 类型的变量。可以通过 `*a` 来访问一级指针,通过 `**a` 来访问整型数。

一个有10个整型数的数组:

声明一个包含 10 个 `int` 类型元素的数组:

int a[10];

 `a` 是一个包含 10 个整型数的数组,数组中的每个元素都是 `int` 类型。

一个指向有10个整型数数组的指针:

声明一个指向包含 10 个 `int` 的数组的指针,定义方式如下:

int (*a)[10];

 `a` 是一个指针,指向一个包含 10 个 `int` 元素的数组。注意这里的括号 `( *a )[10]`,它表示 `a` 是指向数组的指针,而不是一个包含指针的数组。

2.在 int a = 3, int *p = &a;中,*p 的值是( ) (华辰泰尔)

A.变量 a 的地址值;

B.无意义;

C.变量 p 的地址值;

D.3

解析:

在题目 `int a = 3, int *p = &a;` 中,`p` 是一个指针,它保存了变量 `a` 的地址,而 `*p` 表示指针 `p` 所指向的内容(也就是变量 `a` 的值)。

 选项 A

这个选项是错误的。`*p` 是指针 `p` 所指向的内容,而不是 `p` 本身的值。`p` 存储的是 `a` 的地址,而 `*p` 是通过 `p` 访问的 `a` 的值,即 `3`。

选项 B: 

这个选项也是错误的。`*p` 是有意义的,因为它表示的是指针 `p` 所指向的变量 `a` 的值。在这个例子中,`*p` 的值就是 `a` 的值,即 `3`。

选项 C

 这个选项是错误的。`*p` 并不是 `p` 自身的地址值,而是 `p` 所指向的变量的值,即 `a` 的值。要访问 `p` 的地址,应该使用 `&p`。

选项 D

 这个选项是正确的。`p` 是指向变量 `a` 的指针,`*p` 代表的是指针 `p` 所指向的变量 `a` 的值。因为 `a = 3`,所以 `*p = 3`。

解答:

D

3.下列定义中,( )是定义了一个指向数组的指针p。(矩阵软件)

A.int(*p)[7]

B. int *p[7]

C. (int *)p[7]

D. int *p[]

解析:

 A

这个定义表示 `p` 是一个指针,指向一个包含 7 个 `int` 类型元素的数组。`(*p)` 表示 `p` 是一个指针。 `[7]` 表示 `p` 指向的数组有 7 个 `int` 元素。

 B

 这个定义表示 `p` 是一个包含 7 个元素的数组,其中每个元素都是指向 `int` 类型的指针。`p[7]` 表示 `p` 是一个指针数组,而不是指向数组的指针。

C

 这里使用了类型转换 `(int *)`,并且 `p[7]` 表示 `p` 是一个包含 7 个元素的数组。这意味着 `p` 是一个指针数组,而不是指向数组的指针,类型转换只是告诉编译器将数组的每个元素转换为 `int *` 类型。

 D

 这个定义是不完整的,它表示 `p` 是一个不定长的指针数组,但没有明确指定数组的大小。

解答:

A

4.有以下说明语句,则正确的赋值语句是()。(山大华天)

int a[5][5]; int *p, **q;

A. p = a;

B. q = *a;

C. q = a;

D. p = *a;

解析:


int a[5][5];  // 定义了一个5x5的二维数组
int *p;       // 定义了一个整型指针 p
int **q;      // 定义了一个指向整型指针的指针 q

选项 A

 `a` 是一个二维数组,表示的是一个指向 `int[5]` 的指针(即 `int (*)[5]`),而 `p` 是一个 `int*` 类型的指针。因为 `a` 是指向数组的指针,而 `p` 是指向单个整型数的指针,所以类型不匹配。
  

选项 B

`a` 是一个二维数组,`*a` 表示数组的第一行(`int[5]`),它可以退化为指向第一个元素的指针(即 `int*` 类型)。而 `q` 是 `int**` 类型的指针,因此 `*a` 是 `int*`,`q` 应该指向一个 `int*`,两者类型匹配。但由于**p的意思是指针的指针,跨步大小为8字节,不合理。
 

选项 C

`a` 是一个二维数组,表示的是一个指向数组的指针(`int (*)[5]`),而 `q` 是一个指向指针的指针(`int**`)。两者的类型不匹配,无法直接赋值。
  

选项 D

 `*a` 表示数组 `a` 的第一行,即 `a[0]`,这是一个 `int[5]` 数组,但在使用时可以退化为 `int*`。因此 `p = *a` 是合法的,因为 `p` 是 `int*`,而 `*a` 退化为 `int*`。

解答:

D

5.设 char *s1, *s2; 分别指向两个字符串,可以判断字符串 s1 和 s2 是否相等的表达式为( ) (山大华天)

A. s1 = s2

B. s1 == s2

C. strcpy(s1, s2) == 0;

D. strcmp(s1, s2) == 0;

解析:

选项 A

 这是一个赋值语句,将 `s2` 的地址赋值给 `s1`,使得 `s1` 和 `s2` 都指向同一个字符串。这并不是在比较两个字符串,而是修改了 `s1` 的指针指向,所以它不能用于判断字符串是否相等。

选项 B

 这个表达式比较的是 `s1` 和 `s2` 这两个指针的地址值是否相等,而不是它们指向的字符串的内容是否相等。如果两个指针指向的是不同的地址,即使它们的内容相同,比较结果也会是 `false`。因此,不能用这个表达式来判断字符串内容是否相等。

 选项 C

`strcpy` 是用来将字符串 `s2` 复制到 `s1` 中的函数,而不是比较字符串的函数。它不返回比较的结果,返回值也不是用于比较字符串是否相等。因此,这个表达式是错误的。
  

选项 D

 `strcmp` 是标准的字符串比较函数。它会逐字符比较 `s1` 和 `s2`,如果它们相等,则返回 `0`。所以 `strcmp(s1, s2) == 0` 正确判断了两个字符串的内容是否相等。

解答:

D

6.求 n 的值。n=______。(山东丁一)

int a[20];

char *p1 = (char *)a;

char *p2 = (char *)(a+5);

int n = p2-p1;

解析:

aint 类型数组,因此 a+5 表示指向数组中第 5 个 int 元素的指针。将 a+5 转换为 char*,那么 p2p1 之间的差值表示它们之间的字节数。

a+5 指向第 5 个整型元素(索引为 5,即 a[5]),而每个 int 类型在大多数系统中占 4 个字节。因此,从 p1p2 的字节差为:5 * sizeof(int) = 5 * 4 = 20 个字节。

解答:

20

7.若有说明int(*p)[3],以下叙述正确的是( ) (杭州快越科技)

A. p是指针数组

B. (*p)[3]和 *p[3]等价

C. p是指向一维数组中任何一个元素的指针

D. p是指向含有3个整形元素的一维数组的指针

解析:


int (*p)[3];定义了一个指针 `p`,它指向一个包含 3 个 `int` 元素的一维数组。

选项 A

`p` 是一个指针,指向一个包含 3 个 `int` 的数组,而不是指针数组。指针数组是一个数组,其中每个元素都是指针。这个选项描述的与 `int (*p)[3]` 的定义不符。

选项 B 

`(*p)[3]` 表示 `p` 是一个指向数组的指针,其中 `(*p)` 是指向包含 3 个 `int` 元素的数组,`(*p)[3]` 表示访问这个数组中的第 3 个元素。

`*p[3]` 表示 `p` 是一个指针数组,其中每个元素都是 `int*`,`*p[3]` 访问指针数组中第 3 个指针指向的值。
  
  因此,`(*p)[3]` 和 `*p[3]` 是不同的,`(*p)[3]` 是访问数组的第 3 个元素,而 `*p[3]` 是访问指针数组中第 3 个指针指向的内容。

选项 C

 `p` 是指向一维数组的指针,而不是指向数组中单个元素的指针。`p` 指向一个包含 3 个 `int` 元素的数组,不能直接用来访问数组中的单个元素。

选项 D

这个描述准确地反映了 `int (*p)[3]` 的含义。`p` 是一个指针,指向一个包含 3 个 `int` 元素的一维数组。

解答:

D

8.设数组a[5]=(10,20,30,40,50],已知指针p指向a[1],则表达式*++p的值是 ( ) (杭州快越科技)

A. 31              B. 30          C. 21          D. 20

解析:

++p: 这个操作会使指针 p 向前移动一个位置,使其指向下一个数组元素。因为 p 最初指向 a[1]++pp 将指向 a[2],即 30*++p: 这个操作会解引用 ++p 后的位置。即 *++p 的值是 p 移动后指向的那个元素的值,即 30

初始 p 指向 20(即 a[1])。++p 后,p 移动到 a[2]*++p 解引用 p 的当前值,即 30

解答:

9.有以下程序段,执行后,mul的值为( ) (杭州快越科技)

int a[] = {1, 3, 5, 7, 9};

int mul, *data, x;

mul=1;

data=&a[1];

for(x=0; x<3; x++)

{

    mul *= *(data+x);

}

printf("%d\n", mul);

A. 945            B. 315              C. 105              D. 15

解析:

data = &a[1]: data 是一个指向 a[1] 的指针,即 data 指向数组中的值 3*(data + x): 这里 *(data + x)data[x],它表示数组从 data 开始的第 x 个元素

第一次循环 (x = 0):

*(data + 0) 等于 *data,即 a[1] 的值 3

mul = mul * 3 = 1 * 3 = 3

第二次循环 (x = 1):

*(data + 1) 等于 a[2] 的值 5

mul = mul * 5 = 3 * 5 = 15

第三次循环 (x = 2):

*(data + 2) 等于 a[3] 的值 7

mul = mul * 7 = 15 * 7 = 105

解答:

C

10.在32位计算机系统上,以下代码的输出结果是什么?(泰华智慧)

int *a;

char *b;

char c[20];

printf("%d, %d, %d", sizeof(a),  sizeof(b), sizeof(c));

解析:

sizeof(a)

a 是一个 int* 类型的指针。在 32 位系统上,指针的大小通常是 4 字节。因此,sizeof(a) 的结果是 4

sizeof(b)

b 是一个 char* 类型的指针。在 32 位系统上,char* 的大小也是 4 字节。因此,sizeof(b) 的结果是 4

sizeof(c)

c 是一个包含 20 个 char 元素的数组。char 的大小在所有系统上通常是 1 字节。

因此,sizeof(c) 的结果是 20(数组的总字节数)。

解答:

4  4  20

11.有以下定义:(华三外协,紫光云数,山东信通电子,新华三,石峰)

int a[]={1, 2, 3, 4, 5, 6, 7, 8 ,9 ,10}, *p = a

下列哪个表达式的值为3:( )

A. p += 2, *(p++)             B. p += 2, *++p

C. p += 3, *p++                D. p += 2, ++*p

解析:

选项 A

`p += 2` 将 `p` 指向 `a[2]`,即 `3`。*(p++)` 是先解引用 `p`(指向 `3`),然后 `p` 自增到 `a[3]`,即 `4`。 结果是 `*(p++)` 的值是 `3

 选项 B

`p += 2` 将 `p` 指向 `a[2]`,即 `3`。 `*++p` 是先将 `p` 自增到 `a[3]`(即 `4`),然后解引用 `p` 的值。结果是 `*++p` 的值是 `4

选项 C

`p += 3` 将 `p` 指向 `a[3]`,即 `4`。*p++` 是先解引用 `p`(值是 `4`),然后 `p` 自增到 `a[4]`(值是 `5`)。结果是 `*p++` 的值是 `4`

选项 D

*`p += 2` 将 `p` 指向 `a[2]`,即 `3`。 `++*p` 是先将 `*p` 自增,即 `3` 自增到 `4`,然后 `p` 仍然指向 `4`。结果是 `++*p` 的值是 `4`。

解答:

A

12.若已定义:int a[6], *p = a, 不能表示 a[1] 地址的表达式是:(云尖软件开发,紫光云数,智洋创新,新华三,石峰)

A. p+1           B. a+1               C. a++        D. ++p

解析:

 选项 A

p` 是一个指针,`p + 1` 会使指针向前移动一个 `int` 的位置,因此 `p + 1` 指向 `a[1]` 的地址。

选项 B

* `a` 是数组名,它表示数组的起始地址(即 `&a[0]`)。`a + 1` 是数组名加上一个元素的偏移量,所以 `a + 1` 指向 `a[1]` 的地址。

选项 C

 `a` 是数组名,不能对数组名进行自增操作。`a++` 是非法的,因为 `a` 不是一个指针,而是一个数组名,不能被自增或自减。

选项 D

 `++p` 会使 `p` 指针自增一个 `int` 的位置,因此 `++p` 指向 `a[1]` 的地址。

解答:

C

13.有定义: int x, *p;能使指针变量P指向变量x的语句是:________ (智洋)

A. *p=&x;           B. p=&x;           C. *p=x;            D. p=*&x;

解析:

选项 A

`*p` 是指针 `p` 指向的值。`&x` 是 `x` 的地址。因此,`*p = &x` 实际上是将 `x` 的地址赋给 `*p`,这会导致类型不匹配的错误,因为 `*p` 应该是一个 `int` 类型,而 `&x` 是 `int*` 类型。

选项 B

&x` 是变量 `x` 的地址,`p = &x` 将 `x` 的地址赋值给指针 `p`,使 `p` 指向变量 `x`。

选项 C

*p` 是指针 `p` 指向的内存位置。`x` 是整型值,因此 `*p = x` 将 `x` 的值存储到 `p` 指向的内存位置。这并不会使 `p` 指向 `x`,而是将 `x` 的值存储到 `p` 指向的内存地址。

选项 D

`*&x` 是 `x` 的值,因此 `p = *&x` 实际上是将 `x` 的值赋给 `p`,这会导致类型不匹配的错误,因为 `p` 是 `int*` 类型,而 `x` 是 `int` 类型。

解答:

B

14.若有说明int a=2, *p=&a, *q=p;则以下非法的赋值语句是( )。 (智洋)

A. p=q           B. *p=*q          C. a=*q              D. q=a

解析:

选项 A

 `p` 和 `q` 都是 `int*` 类型的指针。`p = q` 是将 `q` 的值(`a` 的地址)赋给 `p`,这是合法的,因为 `p` 和 `q` 是相同类型的指针。

选项 B

 `*p` 和 `*q` 都是 `int` 类型的值,`*p = *q` 是将 `q` 指向的值(即 `a` 的值)赋给 `p` 指向的位置(也是 `a`),这是合法的。

选项 C

`*q` 是 `a` 的值,所以 `a = *q` 实际上是将 `*q`(即 `a` 的值)赋给 `a` 本身。这是合法的,因为 `a` 和 `*q` 都是 `int` 类型。

选项 D

 `q` 是 `int*` 类型的指针,`a` 是 `int` 类型的值。将一个 `int` 类型的值赋给 `int*` 类型的指针是非法的,因为它们的类型不匹配。

解答:

D

15.请写出输出结果 (晟安信息)

int main()

{

    int a[10] = {0};

    int *p = a;

    int *q = &n[6];

    printf("%d\n", q-p);

    printf("%d\n", (int)q - (int)p);

}

解析:

int *p = a;

p 指向数组 a 的第一个元素,即 a[0]

int *q = &a[6];

q 指向数组 a 的第七个元素,即 a[6]

printf("%d\n", q - p);

q - p 是指针差值,表示 qp 之间的元素个数。q 指向 a[6]p 指向 a[0],因此差值是 66 - 0)。这表示 qp 之后 6 个 int 元素的位置。

printf("%d\n", (int)q - (int)p);

(int)q(int)p 是将指针转换为整数。因为在 C 语言中,指针相减得到的是元素个数,但当将指针强制转换为 int 时,得到的是地址值,计算结果是地址值之差。由于在 32 位系统上,指针通常是 4 字节的,(int)q(int)p 是将这两个地址值相减。两个地址相差 6int 元素,每个 int 通常占 4 字节,因此总的字节差异是 6 * 4 = 24 字节。所以 (int)q - (int)p 的结果是 24

解答:

6   24

16.下面的程序输出的结果是__________ (飞音时代)

#include <stdio.h>

int a[] = {0, 2, 4, 6, 8};

main()

{

    int i;

    int *p = a;

    for(i = 0; i < 4; i++) a[i] = *p++;

    printf("%d\n", a[2]);

}

解析:

初始化

a 是一个包含 {0, 2, 4, 6, 8} 的整数数组。

p 是一个指向数组 a 的指针,初始化为指向 a[0]

循环

在每次迭代中,a[i] 被赋值为 *p 的值:

a[4] 的值保持不变为 8

for(i = 0; i < 4; i++) a[i] = *p++;

*p++ 是先解引用 p 指向的值,然后 p 自增。

在第一次迭代中,*pa[0] 的值 0,然后 p 自增指向 a[1]

在第二次迭代中,*pa[1] 的值 2,然后 p 自增指向 a[2]

在第三次迭代中,*pa[2] 的值 4,然后 p 自增指向 a[3]

在第四次迭代中,*pa[3] 的值 6,然后 p 自增指向 a[4]

第一次迭代:a[0] = 0

第二次迭代:a[1] = 2

第三次迭代:a[2] = 4

第四次迭代:a[3] = 6

打印

printf("%d\n", a[2]); 打印 a[2] 的值。

根据循环赋值,a[2] 的值被设定为 4

解答:

4

17.数组声明为:short a[3][4],引用第3行第1列的元素写作________。(富士安全)

a. **(a+2)           b. *(*a+2)               c. a[3][1]          d. *(a[3]+1)

解析:

选项 A

`a + 2` 是指向数组 `a` 第三行(即 `a[2]`)的指针。`*(a + 2)` 解引用 `a[2]`,即获取第 3 行的首元素 `a[2][0]`,这个表达式是正确的。

选项 B

`*a` 是指向数组 `a[0]` 的指针,即第 1 行的首元素 `a[0][0]`。`(*a + 2)` 是指向第 1 行的第三个元素 `a[0][2]`,这个表达式与题目要求的第 3 行第 1 列无关,因此是错误的。

选项 C

数组的最大有效行索引为 2(因为 `a` 只有 3 行,行索引从 0 开始),`a[3]` 超出数组范围,会导致错误访问,因此这个表达式是错误的。

选项 D

`a[3]` 超出了数组范围,和选项 C 一样,这个表达式也是错误的。

解答:

A

18.指针变量p1和p2类型相同,要使p1,p2指向同一个变量,正确的是________。(富士安全)

a. p2=*&p1               b. p2=**p1             c. p2=&p1              d. p2=*p1

解析:

选项 A 

`*&p1` 等价于 `p1`,因为 `&p1` 取得的是指针 `p1` 的地址,而 `*` 解引用后仍然是 `p1` 的值(即 `p1` 指向的地址)。这实际上是将 `p2` 赋值为 `p1` 的值,所以 `p2` 将指向 `p1` 所指向的变量。

选项 B

 `**p1` 是对 `p1` 指向的地址进行两次解引用,这意味着 `p1` 必须是一个指向指针的指针(即 `int **p1`),但题目并没有提到 `p1` 是指向指针的指针,因此这不适用。
  - **该选项是错误的**。

选项 C

`&p1` 是 `p1` 的地址,将 `p2` 赋值为 `&p1`,意味着 `p2` 将指向 `p1` 本身,而不是 `p1` 指向的变量。

选项 D

`*p1` 是解引用 `p1`,得到 `p1` 指向的变量的值,而不是该变量的地址。`p2` 是一个指针,因此不能直接将一个变量的值赋给指针。

解答:

A

19.下列哪个引用是不正确的? (中维世纪)

int a[10]={0. 1, 2, 3, 4, 5, 6, 7, 8, 9}, *p=a;

A. a[p-a];     B. *(&a)    C. p;    D. *(*(a+i));

解析:

A.

`p - a` 计算指针 `p` 与数组首地址 `a` 的差值(即索引值),因为 `p = a`,因此 `p - a = 0`。表达式 `a[p - a]` 实际上等价于 `a[0]`,即第一个元素,结果是 `0`。

B.

`&a` 是数组 `a` 的地址(但它是整个数组的地址),而 `*(&a)` 解引用该地址,结果是数组 `a` 本身。虽然有些不常见,但从技术上来说,解引用数组的地址并不会造成语法错误。

C.

`p` 是指向数组 `a` 的指针。这个引用是指针 `p` 本身,它指向 `a[0]`,是合法的。

D.

这个表达式是不正确的。`a` 是一个一维数组,而不是二维数组。`*(a + i)` 解引用得到的是数组的第 `i` 个元素,但再进行一次解引用 `*(...)` 是不合法的,因为数组的元素是 `int` 类型,而 `int` 不能再被解引用。

解答:

D

20.下面程序的结果是多少? (中科四平)

p1=(unsigned char *)0x801000;

p2=(unsigned long *)0x810000;

请问:

p1+5=

p2+5=

解析:

p1 = (unsigned char *)0x801000

p1 是一个 unsigned char* 类型的指针,指向内存地址 0x801000。在指针运算中,p1 + 5 会将 p1 向前移动 5 个字节,因为 unsigned char 类型的大小是 1 字节。

计算:p1 + 5 = 0x801000 + 5 = 0x801005

p2 = (unsigned long *)0x810000

p2 是一个 unsigned long* 类型的指针,指向内存地址 0x810000。在 32 位系统上,unsigned long 占用 8个字节,因此 p2 + 5 会将 p2 向前移动 5 个 unsigned long 类型的大小,即 5 × 8= 40 字节。

计算:p2 + 5 = 0x810000 + 5 × 8 = 0x810000 + 40 = 0x810028

解答:

0x801005    0x810028

21.请写出以下程序输出内容。(小端环境) (信雅达)

void main()

{

    int a[4] = {1, 2, 3, 4};

    int *p1 = (int *)(&a +1);

    int *p2 = (int *)((char *)a + 1);

    printf("0x%x, 0x%x", *(p1-1), *p2);

}

解析:


void main() {
    int a[4] = {1, 2, 3, 4};       // 定义一个整型数组
    int *p1 = (int *)(&a + 1);     // p1 指向数组 a 之后的地址
    int *p2 = (int *)((char *)a + 1); // p2 指向数组 a 转换为 char* 类型后的第一个字节的地址
    printf("0x%x, 0x%x", *(p1-1), *p2); // 输出 p1 指向的前一个元素,和 p2 所指向的值
}
1. `int *p1 = (int *)(&a + 1);

`&a` 是整个数组 `a` 的地址,即数组首地址。`&a + 1` 计算的是数组 `a` 之后的地址,跳过整个数组大小 (在小端环境下 `int` 类型为 4 字节,`a` 是 4 个元素的数组,占用 16 字节)。`(int *)(&a + 1)` 将该地址转换为 `int*`,此时 `p1` 指向数组末尾之后的地址。 `*(p1-1)`:`p1` 指向的是 `a[4]` 之后的位置,`p1-1` 指向 `a[3]`,即数组的最后一个元素 `a[3]`,其值为 `4`。因此,`*(p1 - 1)` 的值为 `4`。

 2. `int *p2 = (int *)((char *)a + 1);

`a` 是数组的首地址,`(char *)a` 将 `a` 的类型转换为 `char *`,此时 `a` 以字节为单位操作。
- `(char *)a + 1` 是在数组的第 1 个字节后面,即偏移了 1 个字节,指向 `a[0]` 中第二个字节的地址。`(int *)((char *)a + 1)` 再将该地址转换为 `int *`。由于 `p2` 指向的是 `a[0]` 的第二个字节(1 的字节形式在小端存储为 `0x01 00 00 00`),因此 `*p2` 读取从 `0x00 00 00 02` 开始的 4 字节数据,即值为 `0x02000000`。

解答:

0x4   0x2000000

22.用 C 语言编程,向内存0xff9527地址上存入一个整型数0x123 (宇视科技)

解答:
 

#include <stdio.h>

int main() {
    // 将地址 0xff9527 强制转换为指向 int 的指针
    int *p = (int *)0xff9527;

    // 向该内存地址存入整型数 0x123
    *p = 0x123;

    // 输出确认存入操作
    printf(" 0x%X\n", (unsigned int)p);

    return 0;
}

三、总结

学习内容概述

指针的基本概念

学习了指针的定义、作用及其在C语言中的内存操作。理解了指针与变量、内存地址之间的关系

指针与数组

学会了如何通过指针操作一维和多维数组,以及如何通过指针进行数组元素的访问。

指针与函数

理解了指针作为函数参数时的作用,可以实现数据的间接修改。

通用指针(`void *`)

学习了通用指针的使用方式,它可以指向任何数据类型,但需要在使用时进行类型转换。

指针数组和数组指针

了解了指针数组(即数组中的元素为指针)与数组指针(指向数组的指针)之间的区别。

指针的运算

包括指针的加减运算,如何通过指针访问不同位置的内存,以及指针与一维数组、二维数组之间的关系。

 学习难点

指针与数组的关系

指针可以作为数组的替代来访问元素,但两者在内存中的表现不同。理解指针与数组名在使用上的等价性和差异,是学习中的重点和难点。

多级指针的使用

特别是指向指针的指针(如`int **p`)的理解较为困难,涉及到更复杂的内存层级,容易导致混淆。

指针运算的细节

在学习中,需要掌握指针的运算规则,特别是在进行加减操作时,指针的移动是以其所指向的数据类型大小为单位的(如`int`类型每次移动4个字节)。

内存分配和释放

在指针操作中,动态内存的分配和释放是一个重要且容易出错的部分,错误的内存管理可能导致内存泄漏或指针悬空。

注意事项

指针初始化

在使用指针之前,一定要初始化指针,否则会出现访问非法内存地址的情况。可以用`NULL`对指针进行初始化,以便在程序中更容易检查指针是否有效。

指针越界问题

在指针操作中,特别是数组和指针结合使用时,容易出现越界访问的情况,需要确保访问的索引在有效范围内。

指针的类型转换

通用指针`void *`可以指向任何类型的数据,但在实际操作时必须进行类型转换,并确保转换后的类型与实际存储的数据类型一致。

未来学习的重点

函数指针接下来可以学习如何使用指针指向函数,实现更加灵活的函数调用,比如回调函数的实现等。

指针与数据结构结合

可以深入学习指针在链表、树等动态数据结构中的应用,理解如何通过指针实现更复杂的结构和算法。

指针与动态内存管理

在当前理解的基础上,进一步学习堆区的内存分配与管理,特别是在大型项目中如何高效地管理内存。

内存调试工具的使用

在开发中,可以使用内存调试工具(如Valgrind)来检测程序中的内存问题,尤其是检查指针的有效性和内存泄漏情况。

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

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

相关文章

全球主流单片机制造商

欧美地区 1、FreescaleNXP&#xff08;飞思卡尔恩智浦&#xff09;&#xff1a;荷兰&#xff0c;主要提供16位、32位MCU。应用范围&#xff1a;汽车电子、LED和普通照明、医疗保健、多媒体融合、家电和电动工具、楼宇自动化技术电机控制、电源和功率转换器、能源和智能电网、自…

数据资产入表(二)|数据要素资产化与数据资产入表路线框架,一文说清“入表”实现步骤

前言 小兵的数据资产入表系列文章第一篇为大家介绍了国内外关于“数据要素市场化”的探索现状以及推进“数据资产入表”的相关政策。想必大家读完第一篇后&#xff0c;已经认识到数据要素市场化是大势所趋&#xff0c;但是数据的特殊性使得其市场化之路比其他生产要素要更加复杂…

Vue3+Django5+REST Framework开发电脑管理系统

前端&#xff1a;Vue3TypeScript 后端&#xff1a;Django5REST Framework 功能介绍 用户管理角色管理菜单管理配件管理仓库管理类型管理电脑管理入库管理出库管理库存管理收发明细管理 界面预览 源码地址&#xff1a;managesystem: 电脑管理系统

亚信安全出席第五届国际反病毒大会 探究AI现代网络勒索治理

近日&#xff0c;第二届网络空间安全&#xff08;天津&#xff09;论坛正式开幕。本届论坛由天津市政府主办&#xff0c;国家计算机病毒应急处理中心、天津市公安局、天津市滨海新区政府承办&#xff0c;国家网络与信息安全信息通报中心协办&#xff0c;围绕“共建网络安全 共治…

MySQL表操作及约束

修改表 重命名&#xff1a; mysql> alter table user1 rename to user; 新增一列 mysql> alter table user add image_path varchar(128) comment 路径 after birthday; mysql> alter table user add image_path varchar(128) comment 路径 after birthday; Query…

火山引擎VeDI核心产品DataTester再进化,A/B大模型应用评测功能上线

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 近日&#xff0c;火山引擎A/B测试产品DataTester上线了A/B大模型应用评测功能。此次升级不仅强化了模型上线前的基础能力评测&#xff0c;还新增了针对线上使用场景…

基本数据结构:“树”的简单介绍

树是一种重要的非线性数据结构&#xff0c;它模拟了树这种自然结构&#xff0c;由结点&#xff08;Node&#xff09;和边&#xff08;Edge&#xff09;组成&#xff0c;常用于表示分层关系&#xff08;如文件系统、组织结构等&#xff09;。以下是树的一些基本概念和特性&#…

对浏览器事件循环机制的理解

目录 事件循环宏观理解事件循环异步任务细分宏任务与微任务后的理解结合流程图与代码理解览器事件循环 首先来介绍一些谷歌浏览器的进程与线程。 谷歌浏览器包含五个进程&#xff1a;浏览器主进程、渲染进程、GPU进程、网络进程、插件进程。 其中最重要的渲染进程包含五个线程…

java多线程笔记2

一.线程和进程的区别 为什么要进行并发编程:CPU多核心 通过多进程可以实现并发编程,但是进程太重量了,因此引入了多线程. 线程是轻量级的进程,创建和销毁的开销更小,进程是包含线程的,同一进程的若干线程公用同一份系统资源(内存,硬盘等). 二.Thread类重要的属性 三.线程等…

Chainlit集成LlamaIndex并使用通义千问模型实现AI知识库检索网页对话应用

前言 之前使用Chainlit集成Langchain并使用通义千问大语言模型的API接口&#xff0c;实现一个基于文档文档的网页对话应用。 可以点击我的上一篇文章《Chainlit集成Langchain并使用通义千问和智谱AI实现AI知识库检索网页对话应用》 查看。 本次将Langchain框架更改为LlamaInde…

初识php库管理工具composer的体验【爽】使用phpword模板功能替换里面的字符串文本

需求&#xff1a; 做了一个租赁的项目&#xff0c;里面要求签署个人授权协议&#xff0c;里面要填写姓名&#xff0c;手机号&#xff0c;身份证号&#xff0c;签署日期等参数&#xff0c;格式如下图 格式&#xff1a; 如上图&#xff0c;word中的字符串模板变量使用${varname…

Linux的历史,版本,Linux的环境安装、简单学习4个基本的Linux指令、创建普通用户等的介绍

文章目录 前言一、Linux的历史二、版本三、Linux的环境安装1. 腾讯云服务器的申请2. xshell的安装与使用 四、 简单学习4个基本的Linux指令1. ls2. pwd3. mkdir4. cd 五、创建普通用户总结 前言 Linux的历史&#xff0c;版本&#xff0c;Linux的环境安装、简单学习4个基本的Li…

公司电脑屏幕监控软件大战摸鱼怪!五个好用的实时屏幕监控软件,第一款Get了吗?

在繁忙的办公室里&#xff0c;总有一些“摸鱼怪”在不经意间悄然出现&#xff0c;他们或许在浏览无关网页&#xff0c;或许在偷偷聊天&#xff0c;甚至可能在上班时间玩起了游戏。 一、企业面临的问题详情 小李是公司的一名程序员&#xff0c;每天的工作任务繁重且紧急。 然而…

seafaring靶场漏洞测试攻略

打开网页 一.sql注入漏洞 1.输入框测试回显点 -1 union select 1,2,3# 2.查看数据库名 -1 union select 1,2,database()# 3.查看表名 -1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schematest# 4.查看admin表中列名 -1 unio…

千兆工业交换机最重要的是什么?

在现代工业环境中&#xff0c;千兆交换机扮演着至关重要的角色&#xff0c;支持着企业的信息流动和数据传输。然而&#xff0c;当我们探讨千兆工业交换机的各个方面时&#xff0c;最为重要的则是其稳定性和可靠性。首先&#xff0c;千兆工业交换机通常被应用在苛刻的工作环境中…

[网络]http/https的简单认识

文章目录 一. 什么是http二. http协议工作过程三. http协议格式1. 抓包工具fiddler2. http请求报文3. http响应报文 一. 什么是http HTTP (全称为 “超⽂本传输协议”) 是⼀种应⽤⾮常⼴泛的 应⽤层协议 HTTP 诞⽣与1991年. ⽬前已经发展为最主流使⽤的⼀种应⽤层协议 HTTP 往…

docker 多服务只暴露一个客户端

业务场景 docker部署多个服务时候,当为了安全考虑 部署了多个服务,数据库,缓存库,文件服务器啥的,如果全都暴露的话可能会增加资源侵入的风险,所以只需要挂载一个客户端端口给外部访问即可,其他服务均在内网,保障资源安全 docker 网络 可以把容器们都放在同一网络下,由于docke…

Redis——常用数据类型List

目录 List列表常用命令lpushlpushxrpushrpushlrangelpoprpoplindexlinsertllenlremltrim key start stoplset 阻塞版本命令blpopbrpop list的编码方式list的应用 List列表 Redis中的list相当于数组&#xff0c;或者 顺序表&#xff0c;一些常用的操作可以通过下面这张图来理解…

JAVA中线程池的详解

1.概念 顾名思义&#xff0c;线程池就是管理一系列线程的资源池&#xff0c;其提供了一种限制和管理线程资源的方式。每个线程池还维护一些基本统计信息&#xff0c;例如已完成任务的数量。 这里借用《Java 并发编程的艺术》书中的部分内容来总结一下使用线程池的好处&#x…

windows vscode ssh 连接远程服务器

1.在 PowerShell 中运行以下命令&#xff0c;查看 OpenSSH 客户端是否已安装 Get-WindowsCapability -Online | Where-Object Name -like OpenSSH.Client*如果有安装的话&#xff0c;如下图 2.如果没有安装&#xff0c;那么用下面的命令进行安装 Get-WindowsCapability -On…