Linux和C语言(Day09)

一、学习内容

  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)来检测程序中的内存问题,尤其是检查指针的有效性和内存泄漏情况。

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中,`cal`是一个内建的命令行工具,用于显示日历。虽然它的核心是用C语言实现的,但作为系统的一部分,直接查看源码并不常见,因为这些通常由操作系统内核维护,且不公开。然而,如果你想了解如何编写一个简单的C语言日历程序,我可以给你提供一个基础的示例。 这是一个简单的C程序,使用了`ncurses`库(一个用于终端界面的库)来模拟一个基本的日历: ```c #include <stdio.h> #include <ncurses.h> #define MONTHS 12 #define DAY_OF_WEEK 7 void draw_month(int year, int month) { initscr(); // 初始化ncurses环境 cbreak(); // 使输入非阻塞 noecho(); // 隐藏用户输入 curs_set(0); // 关闭光标 int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int current_day = 1; int row = 1; for (int i = 0; i < DAY_OF_WEEK; ++i) { mvprintw(row++, 0, "%2d ", i + 1); } int days_left = DAY_OF_WEEK - 1; for (int day = 1; day <= days_in_month[month-1]; ++day) { if (current_day == 1 && month == 2 && is_leap_year(year)) { days_in_month[month-1]++; } if (days_left > 0) { mvprintw(row++, 0, "%2d ", current_day++); --days_left; } else { for (int j = 0; j < DAY_OF_WEEK - 1; ++j) { mvprintw(row++, 1 + j * 3, " "); } mvprintw(row++, 1, "%2d ", current_day++); days_left = DAY_OF_WEEK - 1; } } getch(); // 等待用户按键 endwin(); } bool is_leap_year(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } int main() { int year, month; printf("Enter year and month: "); scanf("%d %d", &year, &month); draw_month(year, month); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值