数据类型(基本内置类型)——指针和指针孩子(指针变量(指针类型)、数组(构造类型)、字符串、函数指针、空指针等)

一、指针(一种方法)

1、指针是变量:系统为指针分配内存空间;指针有自己的地址;指针能够存值,是地址;

2、指针的类型:指针本身具有的类型:把指针生命语句里的指针名字去掉,剩下的部分就是这个指针类型

int *ptr;                    //指针类型int *
char *ptr;                   //指针类型char *
int **ptr;                   //指针类型int **
int (*ptr)[3];               //指针类型int (*)[3]
int *(*ptr)[4];              //指针类型int * (*)[4]
3、指针所指向的类型:通过指针来访问指针所指向内存区时,指针所指向的类型决定了编译器将把内存区里的内容当作什么看待

int *ptr;                    //指针所指向的类型是int
char *ptr;                   //指针所指向的类型是char
int **ptr;                   //指针所指向的类型是int *
int (*ptr)[3];               //指针所指向的类型是int ()[3]
int *(*ptr)[4];              //指针所指向的类型是int *()[4]
4、指针的值:

指针所指向的内存区域地址。指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。

所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。

指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。

5、指针本身所占据的内存区

用函数sizeof(指针类型)测出来。在32位的平台里,指针本身占据4个字节长度。


*特别:

1、指针数组和数组指针

int *p[7];
指针数组:定义了一个数组,数组中的元素是指针,包含了7个指针变量

int (*p)[4];
数组指针:指向含4个数据元素的二维数组的指针


2、函数指针和指针函数

函数指针:指向这个函数的指针

int (*fun)(int *,int *);
指针函数:返回值是指针的函数

char* fun(int*,int*);

3、空指针:不指向任何对象,这种指针被称为空指针。

1)空指针的值:NULL


2)NULL解读的两句话:

概括点的:是一个纯粹的0,它可能被强制转换成“void*”或“char*”类型。故,有时用0代替NULL。

详细点的:NULL被定义为0或者“(void *)0”,这两种值几乎是相同的。当程序中需要一个指针时(尽管编译程序并不是总能指示什么时候需要一个指针),一个纯粹的0或者一个void指针能自动被转换成所需要的任何类型的指针。


3)空指针的使用

A、用空指针终止对递归数据结构的间接引用,例如,在链表最后一个元素中,指向下一个元素的指针被赋值为NULL,当碰到空指针时,就可以终止对链表的引用了。

B、用空指针进行函数调用失败时的返回值

if (setlocale(cat,loc_p) == NULL){

}
C、用空指针作警戒值。

#include <stdio.h>
#include <assert.h>
int main (int argc,char ** argv)
{
    int i;
    printf ("program name = \"%s\"\n",argv[0]);
    for (i = 1; argv[i] != NULL; ++i)
    {
        printf ("argv[%d] = \"%s\"\n",i,argv[f]);
        assert (i == argc);
     }
     return 0;
}


4、指针void:万能指针

void指针指向某个对象,但该对象不属于任何类型。

在C或C++,任何时候都可以用其他类型的指针代替void指针,或者用void指针来代替其他类型的指针,宁切不需要进行强制转换。

当进行纯粹的内存操作时,或者传递一个指向未定类型的指针时,可以使用void指针,void指针也经常用作函数指针。


memcpy()函数将内存中的数据从一个位置复制到另一个位置

使用了void指针,以说明该函数只进行纯粹的内存复制,包括NULL字符(零字节)在内任何内容都将被复制

void *memcpy(void *addr1,void *addr2,size_t n);

#include "thiingie.h"
struct thingie *p_src , *p_dest;
memcpy(p_dest,p_src,sizeof(struct thingie) *numThingies);

memcpy()函数将把从p_src指向的位置开始的“sizeof(struct thingie) * numThingies”和字节的内容复制到从p_dest指向的位置开始的一块内存区域中。

对memcpy()函数来说,p_dest和p_src都仅仅是指向内存中某个地址的指针。



5、野指针:野指针不是NULL指针,是指向“垃圾”内存的指针。一般不会错用NULL指针,因为用if语句很容易判断。但是野指针是很危险的,if语句对它不起作用。

野指针的成因主要有两种。

1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。所以,指针变量在创建时同时应当被初始化,要么将指针设置为NULL,要么让它指向合法内存。

2)指针p被free或者delete之后,没有设置NULL,让人误以为p是个合法的指针。别看free和delete的名字“恶狠狠的”(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。用调试跟踪,发现指针p被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p成了“野指针”。如果此时不把p设置为NULL,会让人误以为p是个合法的指针。

char *p = (char *) malloc (100);
strcpy(p,"hello");
free(p);                            //p所指的内存被释放,但是p所指的地址仍然不变
。。。
if (p != NULL)                      //没有起到防错作用
{
    strcpy(p,"world");              //出错
}


二、数组

1、数组名就是数组在内存存放的数组首元素地址

2、数组与指针的比较

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命周期内保持不变,只有数组的内容可以改变。

指针可以随时指向任意类型的内存块,它的特征是“可变”,所以常见指针来操作动态内存。指针远比数组灵活但也更危险。

1)修改内容

#include<iostream.h>
void main ()
{
    char a[] = 'hello';
    a[0] = 'x';
    printf("%s\n",a);
    char *p = "world";              //注意p指向常量字符串
    p[0] = 'x';                     //编译器不能发现该错误
    printf("%s\n",p);
}

字符数组a的容量是6个字符,其内容为hello,a的内容可以改变,如a[0]='x'。

指针p指向常量的字符串“world"(位于静态存储区,内容为world),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句p[0]='x'有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。


2)内容复制比较

//数组
char a[] = 'hello';
char b[10];
strcpy(b,a);                        //不能用b = a
if (strcmp(b,a) == 0)               //不能用if ( b == a)
...

//指针
int len = strlen(a);
char *p = (char *)malloc(sizeof(char)*(len+1));
strcpy(p,a);                        //不要用p = a
if (strcmp(p,a) == 0)               //不要用if (p == a)

不能对数组名进行直接复制与比较。若想把数组a的内容复制给数组b,不能用语句“b =a ",否则产生编译错误,应该用标准库函数strcpy进行复制。同理比较b和a的内容是否相同,不能用if(b==a)来判断,应该用标准库函数strcmp进行比较。

语句p=a并不能把a的内容复制指针p,而是把a的地址赋给了p。要想复制a的内容,可以先用库函数malloc为p申请一块容量为strlen(a)+1个字符的内容,再用strcpy进行字符串复制。同理,语句if(p==a)比较的不是内容而是地址,应该用库函数strcmp来比较。


3)计算内存容量

//示例a
char a[] = 'hello world';
char *p = a;
printf("%d\n",sizeof(a));            //12字节
printf("%d\n",sizeof(p));            //4字节

//示例b
void Func(char a[100])
{
   printf("%d",sizeof(a));           //4字节而不是100字节
}

用运算符sizeof可以计算出数组的容量(字节数)。sizeof(a)的值是12(注意别忘了’‘)。指针p指向a,但是sizeof(p)的值却是4,这是因为sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p所指的内存容量。C/C++没办法知道指针所指的内存容量,除非在申请内存是记住它。

注意:当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。


三、字符串:C语言将字符串看作一个字符数组(以NUL字符结束的),字符串和字符指针是等价的



猜你喜欢

转载自blog.csdn.net/qq_27397357/article/details/53739336