C 语言指针问题思考总结

作者 Marlous 日期 2019-03-07
C 语言指针问题思考总结

一 基本概念

  • 指针变量就是存储地址的变量,声明时为 int *p = &a;,引用变量 a 的值赋给 b 需要先指针解引用 b = *p(此时用星号来引用该存储的地址的变量的值)。/ 指针类型的大小是固定的。

  • %p 为输出地址值的格式控制;& 为取地址符。输出变量地址值:

    1
    2
    3
    4
    int a = 2;
    int *pzz = &a;
    printf("address is %p",pzz);
    printf("address is %p",&a);
  • 函数指针:
    指向函数的指针,存的是函数在内存中的入口地址,函数指针如 int (*compare)(int a, int b)。实参是函数名,形参是函数指针。

  • 指针函数:
    函数返回的是一个指针,如 int *compare(int a, int b)

二 详解

举例,一些定义:
变量 c、指针变量 p;数组 name、指针数组 people。(%p %X 效果相同,前者是输出地址格式控制)。

普通变量、普通指针变量、数组变量、字符串数组变量略,详见 《C 语言疑难知识点总结》。

  1. 指针数组解释:
  • 一个指针数组存放了多个字符串数组的首地址,指针数组名是这个指针数组变量自己的首地址,对其解引用是字符串数组的首地址,因为字符串数组可以直接通过其首地址引用值,所以指针数组变量解引用也是存储的字符串。等同于普通指针变量的用法。

  • 地址(这个位置本身)需要解引用还原成值(位置上的值)。/ 如 *(people+2)[0]),指针变量 people+2(指针数组中的第三个指针变量)存的是地址,加一个星号解引用取值(另一个存值的数组的地址),如果是字符串数组就直接是值了,但如果不是则需要再次用方框解引用取值。

  1. 指针数组小结:
  • 用指针数组存若干个存了值的数组(其首地址):
    想用若干数组(非字符串数组)中某个数组中的某个值时:指针数组(其中一个),解引用后变存值的数组首地址(指针数组变量存的位置上的值)-> 再次解引用变存值的数组中某个位置具体值

  • 用指针数组存若干个存了值的变量地址、字符串数组(其首地址):
    想用变量值、字符串数组,和普通指针变量一样:指针数组(其中一个),解引用后变成变量存的位置上的值(相当于字符串数组)

  1. 小结:
  • 基本变量、指针:
    常规变量名是存的值(位置上的值)、常规指针变量名是存的某变量的地址(某个位置)。

  • 数组、指针数组:
    数组变量名是它自己的首地址、指针数组变量名(相当于若干个指针变量的第一个)是它自己的首地址(这个位置本身)。/ 数组名、指针数组名相当于初始化有自己地址的指针变量。/ 方框和星号 [] * 分别解引用数组、指针变量,来取这个位置上的值。

  • 字符串(数组):
    (位置上的值、这个位置本身)数组的首地址可以引用其值即字符串数组,或方框星号 [] * 解引用来取这个位置上的值。

三 总结

1 概念间关系

总结

2 指针数组总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int main(void) {
char *people[3];
int i;
char name1[10] = {"hello"};
char name2 = 'D';
char name3[10] = {'g','h','i'};
for(i=0;i<3;i++)
{
people[i] = (char *)malloc(10 * sizeof(char)); // 每个元素是一个指针(存储名字的数组首地址)
}
people[0] = name1; // 字符串
people[1] = &name2; // 基本类型变量
people[2] = name3; // 数组

/* 输出方式:指针数组里的指针变量自己的地址、指针数组变量存的值(地址)、指针数组变量引用值 */
printf(" people+2 address is %p\n", people+2 ); // 指针数组变量自己的地址

printf(" people[2] value is %p\n", people[2] ); // 指针数组变量存的值(地址)
printf(" people[2] value is %p\n", *(people+2) );

printf(" * people[1] value is %s\n", people[0] ); // 指针数组变量引用值:字符串数组
printf(" * people[1] value is %s\n", *(people+0) );

printf(" * people[1] value is %c\n", *(people[1]) ); // 指针数组变量引用值:基本类型变量
printf(" * people[1] value is %c\n", *(*(people+1)) );

printf(" * people[2] value is %c\n", people[2][1] ); // 指针数组变量引用值:数组
printf(" * people[2] value is %c\n", (*(people+2))[1] );
}
1
2
3
4
5
6
7
8
9
10
/* 输出 */
people+2 address is 0061FF28 // 指针数组变量自己的地址
people[2] value is 0061FF0B // 指针数组变量存的值(地址)
people[2] value is 0061FF0B // ...
* people[1] value is hello // 指针数组变量引用值:字符串数组
* people[1] value is hello //...
* people[1] value is D // 指针数组变量引用值:基本类型变量
* people[1] value is D //...
* people[2] value is h // 指针数组变量引用值:数组
* people[2] value is h //...

指针数组总结

  • 指针数组、二维数组的统一:
    指针数组是先有指针(一组指针变量),用指针去表示是引用值。/ 二维数组是现有数组,后将数组的一维地址赋给指针,然后用指针去引用值。/ 两者在引用值上相同,但定义初始化不同:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    指针数组:
    char *people[3];
    char name3[10] = {'g','h','i'};
    people[2] = name3;
    printf(" * people[2] value is %c\n", (*(people+2))[1] );

    二维数组:
    char name[3][3] = {{...},{...},{'g','h','i'}};
    char (*p)[4];
    p = &name[0];
    printf(" name[2][1] value is %c\n", (*(p+2))[1] ); // h

3 解释

  • 指针数组存放的是指针数组(若干个指针变量)变量自己的首地址(是数组、指针两个概念的集合)。/ 指针数组名 + n 来表示组中若干个指针变量;注意,数组名相当于首地址指针常量,不能加法移动改变位置。

  • 数组和指针类似,不同之处:数组名存放的是数组变量自己的首地址;指针存放的是其它变量的地址。

  • 基本类型变量存放的是一个数据值。

  • 字符串数组既类似基本变量(可以直接通过数组地址引用值,常用方法)、也是数组。

  • 数组指针:因为数组名本质上也是指针,所以第一次解引用是从数组指针变量还原为数组名,第二次解引用是从数组名还原为具体值。