指针的使用

1.指针与别名陷阱

若是有一块内存区域,指向这块内存区域的指针有多个,其中每个指针都是其余指针的别名。数组

别名操做在优化程序是会形成不少麻烦,以下面的例子:函数

void f1(int *x, int *y)
{
    *x += 2 * *y;
}
void f2()
{
    *x += *y;
    *x += *y;
}

上面的两段代码,第一段访问两次寄存器,进行一次乘法计算,第二段代码进行4次访问寄存器,两次乘法。这两段代码的效率明显不同,优化的时候确定选择效率高的代码,可是两段代码的执行结果都是同样的吗?优化

当x和y都指向同一块内存时,结果并非同样的:spa

当x和y都指向变量a,且a = 3;指针

执行第一段代码后a的值为9,执行第二段代码后a的值为12 。缘由就是x和y都指向同一块内存区域,每次操做都会改变这块内存的值。code

2.数组的指针

在C语言中,一个指针变量一样能够指向一个数组:对象

int (*p)[10];

表示p是一个指针,指向一个数组对象,该数组是一个拥有10个整数的数组。内存

所以当p+1时,p移动的字节数应该是等于p所指向的数组对象的字节数。如上面的p+1,应该移动sizeof(int)*10也就是40个字节,以下图所示编译器

下面的程序演示了指向数组的指针:it

#include <stdio.h>

int main(void)
{
  int a[5] = {1, 2, 3, 4, 5};  //数组初始化
  int (*p)[5];  //数组指针
  int *ptr;
  p = &a;
  ptr = (int *)(p + 1);  //将数组指针转换为整型的指针
  printf("the result is : %d\n", *(ptr-1));  //输出数组的最后一个元素
  
  return 0;
}

运行结果为:

the result is : 5

p为数组指针,当p+1后,p指向了数组后面的位置,并转换为整型指针,当整型指针前移4个字节时,就指向了数组的最后一个元素。

3.指针的指针

在C语言中,指针能够指向指针,也就是说,指针变量的值能够是另一个指针变量的地址。

定义一个指向指针的指针变量:

int **p;

以下程序:

#include <stdio.h>

int main()
{
  int a;
  int *p;
  int **q;
  a = 100;
  p = &a;  //p指向变量a
  q = &p;  //q指向指针p
  printf("var a : %d\n", a);  //输出变量的值
  printf("pointer p : 0x%x\n", *p);  //输出指针的值
  printf("pointer pointer q : 0x%x\n", *q); //输出指向指针的指针的值
  return 0;
}

运行结果

4.指针与参数传递

使用传递变啦很指针的方法来改变参数自己的值。下面的例子使用了指针做为餐胡传递,交换实现两个变量的值。

#include <stdio.h>

void swap(int *a, int *b)
{
  int t;
  t = *a;
  *a = *b;
  *b = t;
}

int main(void)
{
  int a, b;
  a = 1;
  b = 2;
  printf("a, b : %d, %d\n", a, b);
  swap(&a, &b);
  printf("a, b : %d, %d\n", a, b);
  return 0;
}

运行结果:

函数将两个指针做为参数赋值到栈帧上,可是并无改变指针自己的值,而是经过指针修改所指向的内容,这时,修改是能够被调用这函数看见的。

同理,若是须要修改指针自己,则需传递的参数应该是指针的指针。

#include <stdio.h>
#include <stdlib.h>

void alter(int **p)
{
  int *q;
  q = (int *)malloc(sizeof(int));//分配一块存储整型变量的内存
  *q = 100;  //将该变量的值设置为100
  *p = q; //是指针的指针所指向的内容指向这块新的内存
}

int main(void)
{
  int a;
  int *p;
  a = 10;
  p = &a;
  printf("p : 0x%x, *p %d\n", p, *p);
  alter(&p);  //更改指针变量自己的值
  printf("p : 0x%x, *p %d\n", p, *p);
  return 0;
}

运行结果:

5.指针类型的意义

指针的本质就是一个无符号的整型,表明一个内存单元的单元号,在定义一个指针变量的同时,每每会声明该指针变所指向的数据的类型,以下:

int *p;

表示该指针变量p指向的数据是一个整型,其做用在于告诉编译器须要从该地址处向后看多少个字节,把这些字节当作一个对象来看。

下面的程序演示指针所指向的数据类型的意义:

#include <stdio.h>

typedef struct{
  int array[2];
  char ch;
}Test;

int main(void)
{
  Test var = {0x12345678, 0x12345678, 0x30};  //初始化结构体
  char *p;
  Test *q;
  //将指针p转换为指向字符型变量,向后看一个字节
  p = (char *)&var;
  printf("1 byte : 0x%x\n", *p);
  //将指针p转换为指向短整型变量,向后看两个字节
  printf("2 byte : 0x%x\n", *(short *)p);
  //将指针转换为指向整型变量,向后看4个字节
  printf("4 byte : 0x%x\n", *(int *)p);
  //将指针p转换为指向长整型,向后看8个字节
  printf("8 byte : 0x%lx\n", *(long *)p);

  //将指针p转换为Test结构类型变量
  q = (Test *)p;
  printf("whole bytes : 0x%x, 0x%x, %c\n", q->array[0], q->array[1], q->ch);
  
  return 0;
}

运行结果为:

上面程序中的结构变量var的存储结构为:

4.void*型指针

void*指针表示一个任意类型的指针,能够指向任意类型的内存单元。

C语言中定义一个指向任意类型对象的指针:

void *p;

该指针p表示指向任意类型,可是若是须要引用该指针所指向的数据时,结汇发生编译错误。

下面的程序演示了引用一个任意类型指针所指向的数据:

#include <stdio.h>

int main(void)
{
  int a = 100;
  void *p;
  p = &a;
  printf("%d\n", *p);  //引用p所指向的数据
}

编译该程序,发现出错了:

指针类型的意义在于编译器能够知道从该指针所表示的地址开始,向后将多少字节当作一个总体对象来看。可是任意类型指针并不能告诉编译器该对象的大小是多少,所以编译器不知道向后看多少个字节,因此就会出现编译错误。

    既然没法引用任意类型指针所指向的数据,void*类型彷佛没有存在的意义了,其实否则。有时编译器不清楚用户要吧指针所指向的内容作声明用途,这时候void*类型的指针就派上用场了,编译器认为这块内存用户作什么都是合法的,所以指向这块内存的指针就是任意类型的。

    最典型的一个例子就是使用malloc()函数分配一块内存后,获得这块内存的首地址。用户分配了一块内存,编译器不知道用户要作什么,所以就返回一个void*类型的指针,这样就能够躲过没必要要的编译器类型检查。以后怎么使用该指针是用户的事情,与malloc()函数无关了。

下面的程序演示使用malloc()函数获得一个void*类型的指针:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  void *p;  //p是一个void*类型的指针
  int *q;
  p = malloc(sizeof(int));  //使用malloc()函数分配4个字节
  if(p == NULL) {
    perror("fail to malloc");
    exit(1);
  }
  q = (int *)p; //要将p转换为指向整型的指针后才可使用其指向的数据
  *q = 100;
  printf("the value is : %d\n", *q);
  
  return 0;
}

运行结果为:

C语言中对指针类型的检查十分严格,参与运算或者比较的两个指针指向的对象类型必须相同才被认为是同一类型的指针。

指针的本质是一个无符号的整数,从理论上讲,指针和一个整数的比较应该是没有问题的,可是c语言编译器不容许二者进行比较,所以在比较一个指针和一个整型数据时,首先要将整型数据转换为该指针类型,而后再进行比较。

#include <stdio.h>
int main(void)
{
  int *p;
  if(p == 1000){  //直接拿整型常量和指针进行比较
    printf("equal\n");
  } else {
    printf("not equal\n");
  }

  if(p == (int *)1000){ //将整型转换为指针变量后比较
    printf("equal\n");
  } else {
    printf("not equal\n");
  }
  return 0;
}

编译时,编译器会警告类型不匹配:

由此,为了不编译器的警告,须要将整型常量转换为指针。

一个典型的例子就是NULL常量,该常量用于表示空指针。其自己并非C语言中的一个关键字,而是一个定义在stdio.h文件中的宏:

#define NULL (void *)0

空指针实际上就是一个常数0,其表明0号内存单元。在全部的系统中,0号内存单元都是不容许进行读写操做的,所以指向该内存单元的指针做为空指针使用。之因此须要将0转换为void*类型的指针,其目的是要避免编译器作无用的类型检查。

相关文章
相关标签/搜索