C语言--指针

1,*和&的使用

&:取操作符,取出操作对象在内存单元中的地址;*:访问操作符,访问操作对象所指向的变量

注意点:&的操作对象必须是变量或数组元素,不能是常量或表达式,因为常量和表达式在内存中没有占用固定的单元;&也不能作用于寄存器变量

           *的操作对象必须是指针变量或指针表达式

2,指针变量和指针所指变量

指针变量:用于存储某个内存地址的变量

指针所指变量:指针变量中保存的内存地址所对应的变量

注意:指针变量在使用进行间接访问前必须初始化,同时一个变量只能指向同一类型的变量

3,指针数组和数组指针

int *p1[10];      

int (*p2)[10];

根据运算符的优先级进行判断:

(1)'[]的优先级' >'*的优先级',∴ p1和[]先结合是数组;int *p1[10]; 是一个指针数组,该数组里面有5个元素,每个元素类型是(int *)

(2)'()的优先级' >'[]的优先级',∴p1和*先结合是指针;int (*p2)[10];是一个数组指针,该指针指向一个匿名数组,数组里有10个int型元素

补充:其实,指针变量的类型就是把相应数据的说明中的标示符去除后剩下的部分,如int (*p2)[10]; 去除p2后int (*)[10],int (*p2)[10]可以写成int (*)[10]p2,前面是指针类型,后面是指针变量,但是看着别扭,所以把p2提到前面,效果是一样的

4,数组的首地址和数组首元素的首地址(a 和 &a的区别)

int a[5] = {1,2,3,4,5};

int (*p1)[5] = &a;

int (*p2)[5] = a; //会警告类型不一致!但是当变量作为右值时,编译器只计算变量的值,此时和&a变量的值相同,所以不提示错误,运行结果会发现结果一样!

使用printf("%d%d",&a+1,a+1);运行结果不同如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
    int a[5]={1,2,3,4,5};
    int (*p1)[5];
    int (*p2)[5];
    p1 = &a;
    p2 = a;
    printf("p1 = %d
", p1);
    printf("p2 = %d
", p2);

    printf("&a = %d
", &a);
    printf("a = %d
", a);

    printf("&a+1 = %d
", &a+1);
    printf("a+1 = %d
", a+1);

    printf("p1+1 = %d
", p1+1);
    printf("p2+1 = %d
", p2+1);

    return 0;
}
运行结果:

p1 = 2359028
p2 = 2359028
&a = 2359028
a = 2359028
&a+1 = 2359048
a+1 = 2359032
p1+1 = 2359048
p2+1 = 2359048

 

另一个例子:

int main()
{
   int a[4]={1,2,3,5};
   int *ptr1=(int *)(&a+1);
   int *ptr2=(int *)((int)a+1);
   int *ptr3=(int *)(&a);
   printf("%d
",a);
   printf("%d
",a+1);
   printf("%d
",(int)a+1);
   printf("%d
",&a+1);
   printf("%d,%d
",*(ptr1-1),*ptr2);
   printf("%d,%d
",*(ptr1),*(ptr3+1));
   printf("%d,%d
",(int *)(&a),ptr3);
printf("%d",ptr1[-1]);
return 0; }
解析:
int *ptr1=(int *)(&a+1):将&a+1强制转化为(Int*)类型,然后赋值给ptr1,此时ptr1指向数组a的下一个整形数据;
ptr1[-1]:相当于*(ptr1 -1),即ptr1向后退四个字节地址处所指的数组元素的值,此题中为5;
int *ptr2=(int *)((int)a+1):就是将地址a转化为整型值,然后整数之间的加减运算+1,就是a[0]第二个字节的地址,然后把这个地址强制转换为(int *)后赋值给ptr2,
所以,*ptr2的值为a[0]第二个字节后的4个字节的内容,如下图所示:



 
 

 5,空指针、野指针和通用指针(void指针/泛指针)

空指针:(NULL)不指向任何对象,和其他有效地址的值均不同,是<stddef.h>里面定义的宏,可理解为一个纯粹的0;

          使用其可以防止指向任何未知的内存区域;

         主要用法:(1)用空指针终止对递归数据结构的间接引用。如:链表、哈希表等递归数据结构,while(p!=NUll)

        (2)用空指针作函数调用失败时的返回值。许多C库函数的返回值是一个指针,在函数调用成功时,函数返回一个指向某一对象的指针;反之,则返回一个空指针      (3)用空指针作警戒值 :警戒值是标志事物结尾的一个特定值。例如,main()函数的预定义参数argv是一个指针数组,它的最后一个元素(argv[argc])永远是一个空指针

注意:指针的值不能是整型值,但空指针是个例外,即空指针的值可以是一个纯粹的零(空指针的值并不必须是一个纯粹的零,但这个值是唯一有用的值。在编译时产生的任意一个表达式,只要它是零,就可以作为空指针的值。在程序运行时,最好不要出现一个为零的整型变量);

绝对不能间接引用一个空指针,否则,你的程序可能会得到毫无意义的结果,或者得到一个全部是零的值,或者会突然停止运行。 

野指针:不是NULL指针,是指向被释放的或者访问受限的垃圾内存的指针

野指针的成因主要有三种:
一、指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。别看free和delete的名字(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。释放后的指针应立即将指针置为NULL,防止产生“野指针”。
例:
free( p );
if ( p != NULL )
p = NULL;
三、指针操作超越了变量的作用范围。比如不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

避免方法

1)指针变量一定要初始化为NULL,因为任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的。
2)当指针p指向的内存空间释放时,没有设置指针p的值为NULL。delete和free只是把内存空间释放了,但是并没有将指针p的值赋为NULL。通常判断一个指针是否合法,都是使用if语句测试该指针是否为NULL。
int main(){
int *p =(int *) malloc(sizeof(int )*6);
free(p);
p = NULL;
if(p!=NULL){
*p = 7;
printf("%d",*p);
}

}
 
所以动态分配内存后,如果使用完这个动态分配的内存空间后,必须习惯性地使用delete操作符取释放它。
void 指针:可指向任意类型数据的存储地址,当进行纯粹的内存操作时,或者传递一个指向未定类型的指针时,可以使用void指针。void指针也常常用作函数指针。 例
如:memcpy()函数使用了void指针,以说明该函数只进行纯粹的内存拷贝,包括NULL字符(零字节)在内的任何内容都将被拷贝。
常用于编写通用函数!!
注意:任何类型的指针均可以直接赋值给void指针,但是void指针赋值给其他类型时,必须进行强制类型转换,同时void指针无法进行加减运算
 
6,指向函数的指针和指针函数
指针函数:类型 *函数名(参数表); float *func(int x, int y); 返回一个指向float量的指针
指向函数的指针:类型(*指针变量)(参数表); float(*p)(int x, int y);
注意:(1)指向函数的指针使用前必须定义并初始化使其指向某个函数;另外指向函数的指针变量可作为函数参数,将一个函数传递给另一个函数,因为在C语言中是不可以在一个函数中定义另一个函数的
       (2)我们知道函数返回值不可以返回数组和函数类型,但可以返回一个指针,所以,当被调函数需要返回数组或函数时,可以“变通”为返回一个指向此类对象的指针即可!
      (3)C编译中对函数名的处理和数组名的处理一样,将函数名转换为指向该函数在内存中入口地址的指针,然后利用函数名对指向函数的指针变量进行赋值,就使得指针变量指向函数。
实例代码1:
char *month_name(int n){
    char *name[]={"illegal","January","February","March"};
    if(n<1)
        return name[0];
    else
        return name[n];
}
int main(){
    int n;
    char *p;
    scanf("%d",&n);
    p=month_name(n);
    printf("month = %s",p);
    return 0;
}

实例代码2:

double square(double x){
    return x*x;
}
int main(){
    double (*p)(double x);
    p = square;
    printf("%f,%f,%f",square(1.6),p(1.6),(*p)(1.6));
    return 0;
}
                   
 
原文地址:https://www.cnblogs.com/graceting/p/4105667.html