一、学习内容
-
指针
-
指针的概念
-
什么是内存
-
内存是计算必不可少的硬件设备,一般说到内存常会说TA多大——M、G、T
-
内存单位:bit【位,最小单位】 Byte【字节,基本单位】 KB MB GB TB
-
-
CPU如何从内存取数据
-
通过内存地址去取 将内存条想象成一幢超级摩天大楼,里面有很多很多房间[bit/Byte],如何区分这些房间,编号。 编号又称之为地址,也称之为指针 地址、编号、指针【常量】 用于存储地址的变量,称为指针变量 指针变量 【变量】
-
-
约定俗称的
-
说指针 指的是指针变量
-
说地址 指的是内存编号
-
-
-
定义指针的格式
-
语法:存储类型 数据类型 *指针名
-
分析:数据类型:基本 构造 指针 空 eg: int *p float *p char *p double *p int(*p)[3] strcut *p int **p void *P
-
-
指针的初始化
-
直接初始化
-
int a=10 int *p =&a
-
-
间接初始化
-
int a=10; int *p; //int数据类型 *指针的标志 p是指针名 p =&a; //正确赋值 *p=&a; //错误赋值
-
-
用已经赋值好的指针给另一个指针赋值
-
int a=10; int *p=&a; int *q=p //q=p
-
-
不知道指针指向哪里先置空,防止野指针
-
int *p=NULL; 判断指针为空 if(p==NULL)//if(!p)
-
-
大小端判断
-
小端:数据低位存储地址低位
-
大端:数据低位存储地址高位
-
代码:
int main(){ int a=0x12345678; char *p=(char *)&a; if(*p == 0x78){ printf("小端存储\n"); }else if(*p == 0x12){ printf("大端存储\n"); } return 0; }
-
-
-
指针的字节大小
-
指针变量字节大小,在64位系统中是8字节,32位系统中是4字节
-
-
指针的相关操作
-
& :取地址符 【内存地址,房间编号】
-
* :解址/取值 【推开房间门看数据】
-
& 和 * 互为逆运算符 *&p &*p 其实都是p
-
-
指针的运算
-
算术运算: + - ++ --
-
关系运算: > < >= <= == !=
-
赋值运算: = += -=
-
-
-
指针和一维数组
-
地址的等价关系
-
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]
-
-
-
指针和二位数组
-
通过数组名a和a+1对比发现,通过数组名a移动控制的一整行
-
你还能定义 int *p=a;吗?不报错但是操作单位是一行
-
一行包含多个连续存储的相同类型的数据——这个容器不就是数组,将指针与数组结合进行结合操作
-
首先是一个指针,但是这个指针操作数组
-
-
数组指针
-
本质是一个指针:指向一个地址,也就是行指针
-
语法:int (*p)[3]
-
作用:一般用于二维数组传参的时候
-
数组指针 指向 二位数组 地址的等价关系
-
&p[i][j] == p[i]+j == *(p+i)+j
-
-
元素的等价关系
-
p[i][j] == *(p[j]+j) == *(*(p+i)+j)
-
-
-
指针数组
-
本质是一个数组,数组的类型是指针,也就是存储的是地址
-
整型数组:int a[5]={1,2,3,4,5}//类型是整数,存的都是整数
-
指针数组:int *a[5]={&,&,&,&,&}
-
-
二级指针
-
本质是一个指针,数据类型也是指针,指针的指针,地址的地址
-
语法:int **p //二级指针 int ***p //多级指针
-
-
通用指针
-
语法:void *p;
-
作用:可以在使用的时候,强转为你想要的任意类型的指针,一般用于传参
-
-
脑图
二、作业
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;
解析:
a
是 int
类型数组,因此 a+5
表示指向数组中第 5 个 int
元素的指针。将 a+5
转换为 char*
,那么 p2
和 p1
之间的差值表示它们之间的字节数。
a+5
指向第 5 个整型元素(索引为 5,即 a[5]
),而每个 int
类型在大多数系统中占 4 个字节。因此,从 p1
到 p2
的字节差为: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]
,++p
后 p
将指向 a[2]
,即 30
。*++p
: 这个操作会解引用 ++p
后的位置。即 *++p
的值是 p
移动后指向的那个元素的值,即 30
。
初始 p
指向 20
(即 a[1]
)。++p
后,p
移动到 a[2]
。*++p
解引用 p
的当前值,即 30
。
解答:
B
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
是指针差值,表示q
和p
之间的元素个数。q
指向a[6]
,p
指向a[0]
,因此差值是6
(6 - 0
)。这表示q
在p
之后 6 个int
元素的位置。
printf("%d\n", (int)q - (int)p);
(int)q
和(int)p
是将指针转换为整数。因为在 C 语言中,指针相减得到的是元素个数,但当将指针强制转换为int
时,得到的是地址值,计算结果是地址值之差。由于在 32 位系统上,指针通常是 4 字节的,(int)q
和(int)p
是将这两个地址值相减。两个地址相差6
个int
元素,每个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
自增。在第一次迭代中,
*p
是a[0]
的值0
,然后p
自增指向a[1]
。在第二次迭代中,
*p
是a[1]
的值2
,然后p
自增指向a[2]
。在第三次迭代中,
*p
是a[2]
的值4
,然后p
自增指向a[3]
。在第四次迭代中,
*p
是a[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)来检测程序中的内存问题,尤其是检查指针的有效性和内存泄漏情况。