本节探讨c语言中最重要的指针,c语言之所以如此灵活,关键就在于指针的运用,这是一个难点,也是学c语言必须要掌握的内容。
1、什么是指针?
指针如其名字所示的一样,他是一种有方向性的数据,能够指向别的数据类型。首先要明白的一点是,指针也是一种数据。那么在c语言中的指针是怎么运作的呢?在c语言中,我们通过*来创建指针数据,我们通过一个例子来看:
int a = 12; // 创建整形变量 a
int *p = &a; // 创建指针 p 指向 a
printf("a的地址:%p a的大小为:%lu Byte a的内容:%d\n", &a, sizeof(a), a); // 输出 a 的地址、大小及存储内容
printf("p的地址:%p p的大小为:%lu Byte p的内容:%p\n", &p, sizeof(p), p); // 输出 p 的地址、大小及存储内容
printf("p指向的内容为 %d\n", *p); // 输出p指向的内容
在该例子中,我们创建了一个变量 a=12,然后创建了一个指针 p 指向 变量 a,我们看看程序输出情况:
a的地址:0x7fff5fbff8d8 a的大小为:4 Byte a的内容:12
p的地址:0x7fff5fbff8d0 p的大小为:8 Byte p的内容:0x7fff5fbff8d8
p指向的内容为 12
从输出结果可以看出,变量 a 占用内存单元的4个字节,存储的是数值 12,指针 p 占用内存单元的8个字节,存储的是一个地址数据,进一步观察可以发现,p对应内存单元所存储的地址数据和变量a的地址一致,这样我们就可以通过 p 直接访问 a 的内容,换句话说,p 就是 a。验证如下:
*p=13;
printf("a = %d\n", a); // 输出 a 的内容
前面的代码中我们给变量 a 赋值 12,在该代码中,我们没有直接对 a进行操作,我们看看结果又会怎样:
a = 13
可以发现,我们通过指针 p 就可以间接修改 变量 a 的内容。
2、思考
通过上面的例子发现,我们可以通过指针绕过变量本身,直接修改变量的值,换句话说,我们可以通过指针直接操作变量对应的地址单元!这就是指针的本质。那么,为什么要这样做呢?
a、我们知道,数据在内存中的存储都是以基本的内存单元按顺序进行排列的,对变量的操作其本质就是操作变量对应的地址单元,我们每创建一个变量,实际上就是用一个标识符(变量名)标识了一块内存单元,这样当创建大量的变量时就会产生大量的标识符,这是不利于变量的调用的。当对大量的数据或者具有某种结构和顺序的数据进行操作时,指针的优势就凸现出来,因为指针是直接通过地址单元来操作变量的,而我们对地址单元的简单运算,就能在大量不同的变量之间跳转,非常的灵活。
b、当然这也产生了一些问题。由于指针是对地址单元的直接操作,而如果我们错误的将指针定位到不该指向的内容时,有时会导致整个程序的崩溃,这一点一定要注意。
3、特殊的指针
a、数组
c语言中数组本身就是一种指针,只不过写法略有不同。
int ar[]={1,2,3};
int *arp=ar;//int *arp={1,2,3};这样是错误的
//输出数组的值
printf("%d\n", arp[0]); // *arp
printf("%d\n", arp[1]); // *arp+1
printf("%d\n", arp[2]); // *arp+2
printf("ar的地址: %p\narp的地址: %p \n", &ar, &arp);
输出结果:
1
2
3
ar的地址: 0x7fff5fbff8c8
arp的地址: 0x7fff5fbff8c0
在上面的例子中,我们将一个指针指向了数组,有没注意到数组名的前面没有加 &地址操作符?这是因为数组名本身就是一个指针!这两个指针占用不同的内存单元,但是他们都能对数组进行操作。
b、字符串
前面有提到,字符串本身就是一种数组,那同样的,他也可以用指针来表示:
char *str = "This is a string!";
printf("%s\n", str);
输出:
This is a string!
c、指针数组
在数组名的前面加 * 就成为了指针数组:
//创建指针常量数组
char *name[]= {
"Jack",
"Tom",
"Helen",
"April",
};
// 输出
for (int i=0; i<4; i++) {
printf(" %s\n",name[i]);
}
输出结果:
Jack
Tom
Helen
April
d、指针函数
函数本身也是一种指针(函数的细节将在别的章节讨论),我们当然也可以通过指针指向函数:
//(*p)为固定写法,表示该指针指向函数
void (*p)(char);
p=func;
// 测试函数和指针指向的地址
printf("func地址: %p\n", &func);
printf("p指向地址: %p\n", p);
// 指针调用函数
(*p)('A'); // 可以简化为p('A')
输出结果:
func地址: 0x100000e80
p指向地址: 0x100000e80
func()函数被调用,传入字符参数 A
可以发现这两个地址完全一致。
4、其他
在计算机领域流传一句话:多一层间接,没有解决不了的问题。指针是c语言构建简洁结构的关键,在以c语言为基础的面向对象设计语言中,这种对指针的运用将显得格外突出和复杂。