黑马程序员-C语言回顾-指针

来源:互联网 发布:西恩潘演技知乎 编辑:程序博客网 时间:2024/05/11 18:36

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

C语言回顾-指针

一、地址的概念

计算机硬件系统内部存储器中,拥有大量的存储单元(容量1字节)。

为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的地址。

每个存储单元都有一个唯一的地址。

内存地址:内存中存储单元的编号。

 

在地址所标识的存储单元中存放数据。

注意:内存单元的地址和内存段元中存放的数据是两个完全不同的概念。

变量地址:系统费诶给变量的内存单元的起始地址。

内存单元的地址是不会变的,但是里面存的数据是可以改变的。

 

面试题:

一个东西从变量出生到消失一直伴随着它,而且不会改变。

答案:变量的地址。


二、为什么要使用指针

1、指针的基本概念

内存单元的编号也叫地址。

既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。

内存单元的指针和内存单元的内容是两个不同的概念。


2、使用指针的好处

1)为函数提供修改调用变量的灵活手段;

2)让函数有多个返回值;

3)可以改善某些子程序的效率

在数据传输时,如果数据块较大(比如说素具缓冲区或比较大的结构),这是就可以使用指针传递地址而不是实际数据,即提高传输速度,又节省了大量内存。

4)为动态数据结构(二叉树、链表)提供支持。


3、变量的存取方式

存取方式有两种:直接存取和间接存取。

1)直接存取:

通过变量名获取变量的值。

变量的赋值和取值。

2)间接存取:

通过指针(地址)间接操作完成。

通过地址获取固定个数的存储单元。


三、指针变量概念及定义方法

1、指针变量

存放指针的变量称为指针变量。

一个指针变量的值就是某个内存单元的地址或某个内存单元的指针。

指针是一个地址,是一个常量。

指针变量是存放一个地址,是一个变量。


2、定义一个指针变量

对指针变量的定义包括三个内容:

1)指针类型说明,即定义变量为一个指针变量。

2)指针变量名。

3)变量值(指针)。


一般形式:

  类型说明符 *变量名;

其中,* 表示这是一个指针变量。

变量名即为定义的指针变量名。

类型说明符表示本指针变量所指向的变量的数据类型。

例如:

  int *p 

一个指向整型变量的指针。


注意:

1)在定义指针时,“*”号表示定义的变量是指针变量,变量的值只能存放地址。

2)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。

3)指针也可以被声明为全局、静态局部和局部的。


四、指针变量的初始化和引用

1、指针变量的初始化方法


两种方式:定义的同时进行初始化和先定义后初始化

1)定义的同时进行初始化

  int a = 5;

  int *p = &a;

2)先定义,后初始化

 尽量避免这种情况。

3)注意:

指针没有初始化里面是一个垃圾值,此时是一个野指针,如果操作一个野指针

(1)可能会导致程序崩溃

(2)访问你不该访问的数据

所以指针必须初始化后才可以访问其所指向区域。


2、使用“*”获取指针对应存储区域的内容


两个和地址有关的运算符:

&:取地址运算符;

*:指针运算符(“间接访问”运算符)。

C语言提供了地址运算符&来表示变量的地址。


一般形式:

&变量名;

*指针变量名  :获取指针变量指向的存储空间的内容。


“*”的两种用法

1)用于定义一个指针变量

2)获取指针变量指向的存储空间的内容


指针变量如果不没有初始化会有什么后果?


思考&实现

用函数实现两个变量值的交换

//方案一:void swap1(int a,int b){    //此处实质上是形参a和b值得交换,    //跟实参没有关系    int temp;    temp = a;    a = b;    b = temp;}不能解决问题//方案二:void swap2(int *p,int *p1){    //形参变量的地址进行的交换    int *temp;    temp = p;    p = p1;    p1 = temp;}//不能解决问题//方案三:void swap3(int *p,int *p1){        //通过指针直接修改变量的值了    int temp;    temp = *p;    *p = *p1;    *p1 = temp;    }//解决问题



五、指针常见的应用场景

1、在被调函数中可以修改主调函数中的变量的值

void changValue(int *p){

  *p = 100;

}

2、函数返回多个值


void caculator(int x,int y,int *add,int *jian,int *cheng,float *chu){    //在函数的内部访问了主调函数中的变量值    *add = x+y;    *jian = x-y;    *cheng = x*y;    *chu = x/(float)y;    }int main(int argc, const char * argv[]) {    int add=0;    int jian=0;    int cheng=0;    float chu=0.0f;        caculator(12, 4, &add, &jian, &cheng, &chu);        printf("add = %d\n",add);    printf("jian = %d\n",jian);    printf("cheng = %d\n",cheng);    printf("chu = %.2f\n",chu);        return 0;}运行结果:add = 16jian = 8cheng = 48chu = 3.00Program ended with exit code: 0



六、二级指针介绍

1、二级指针

定义:如果一个指针变量存放的优势另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。也称为“二级指针”。

指针变量直接指向变量,称为“一级指针”。

如果通过指向指针的指针变量来访问变量则构成“二级指针”。

int a  =7;int *p = &a;int **p1 = &p;//指针变量p1指向另一个指针变量p,指针p指向变量a,所以p1是二级指针,p是一级指针。// p = &a;//p1 = &p;//*p = a;// *p1 = p;// **p1 = a;




2、指针为什么要区分类型

在同一种编译器环境下,一个指针变量所占用的内存空间是固定的。

在64位编译器下,指针变量占用8个字节。

虽然所有指针都占有8个字节,但是不同类型的变量却占用不同的字节数。

定义什么类型的指针就应该指向什么类型的变量。为的是能从内存中准确读取指针所指向的变量。


七、数组指针


1、数组指针的概念


一个变量有地址,一个数组包换若干元素,每个元素都有相应地地址。

指针变量可以指向数组元素(把某一元素的地址放到一个指针变量中)。

数组元素的指针就是数组元素的地址。

可以用一个指针变量指向一个数组元素。

int a[10] = {1,3,5,7,9,11,13,15,17,19};int *p;p = &a[0];//等价于p = a ;



注意:数组名a不代表整个数组,只代表数组首元素的地址。

“p=a;”的作用是“把a数组的首地址赋给指针变量p”,而不是“把数组a各个元素的值赋给p”。


2、数组指针初始化和使用方法


1)使用指针引用数组元素

指针指向数组元素时,允许以下运算:

加一个整数,p+1

减一个整数,p-1

自加运算,p++或++p

自减运算,p——或——p

两个指针相减,p1-p2(只有p1和p2都指向同一个数组中的元素时才有意义)

如果指针变量p已经指向数组中的一个元素,则p+1指向同一个数组的下一个元素,p-1指向同一数组的中的上一个元素。


注意:

(1)如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,他们指向a数组序号为i的元素。

(2)p是一个指针变量,它存放的值是可以改变的。


2)引用一个数组元素的两种方法

(1)下标法,如a[i]形式

(2)指针法,如*(a+i)或*(p+i)

注意:a是常量,所以a++是错误的,但是p是变量,所以p++是正确的。


思考&实现

用指针将数组a中的n个整数按相反顺序存放。


void nixuArray(int a[],int len){    //定义数组指针    int *p = a;        //定义下标    int i = 0,j = len-1;     int temp;    while (i<j) {        //交换a[i]  和 a[j];                temp = *(p+i);        *(p+i) = *(p+j);        *(p+j) = temp;                //修改下标        i++,j--;            }}



八、一维指针数组


1)概念:

    一个数组的元素值为指针则是指针数组。 

指针数组是一组有序的指针的集合。

指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。

一般形式:

  类型说明符 *数组名[数组长度]

例如:

  int *p[3]

表示p是一个指针数组,它有三个元素,每个元素都是一个指针,指向整型变量。


2)指针数组的使用

  int a =3,b=4,c=5;

int *pa[3] = {&a,&b,&c};


九、指针变量之间的运算

只有指向同一数组的两个指针变量之间能进行运算,否则运算毫无意义。


1、两指针变量减法运算

两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。

实际上是两个指针值(地址)相减之差再除以该数组元素的长度(字节数)。 

1)直接用两个地址相减。

意义不大。

2)常见用法:

两个指针都指向同一个数组。

(1)判断两个指针变量指向的元素是否连续。

(2)判断两个指针变量之间相隔几个元素。


注意:两个指针变量之间没有加法运算,只有减法运算。

如果两个指针变量指向同一个元素,那么他们相减的结果是0 。

如果连个指针变量指向的元素相邻,他们相减的结果的绝对值是1 。


2、两个指针变脸之间的关系运算

用来判断地址的大小


十、用数组名来访问二维数组

关于行指针和列指针

a代表第0行首地址     

a+1代表第1行首地址    

a+2代表第2行首地址

a+i代表行号为i的行首地址(按行变化) 


a[i]+j      代表a[i][j]的地址

*(a[i]+j)  代表元素a[i][j]

*(*(a+i)+j)与*(a[i]+j)等价

 

难理解的地方:

*(a+1)  = 0x7fff5fbff710

  a+1   = 0x7fff5fbff710

在行指针中,a,a+1,a+2等是行指针。

二维数组是特殊的一维数组   上面的数组可以看做一个特殊的一维数组      a[3]={a[0],a[1],a[2]};

那么a指向a[0]     

 a+1---------->a[1]       

a+2---------->a[2]   

所以*(a+1) 是取a[1]中的值,是一个地址。

如果看成一个一维数组,数组名a是第一个元素的地址,a+1是第二个元素的地址,

所以:a+1也是一个地址 所以:

*(a+1)  = 0x7fff5fbff710

  a+1   = 0x7fff5fbff710

 

 

列指针:

  在第一行中:

a[0]代表列号为0的元素    a[0]+1代表列号为1的元素 





 

十一、普通指针访问二维数组

普通数组可以访问二维数组,但是不提倡使用。

一维数组中,int *p = a;(a是一维数组),数组名a是一个本身是一个地址。

而在二维数组中,数组名相当于是一个二级指针,用普通指针访问二维数组,想当于用一个一级指针指向了一个二级指针,但是又可以用,是因为数组的元素在内存中是按行线性连续排列的。


#include <stdio.h>int main(int argc, const char * argv[]) {    int a[3][4] = {{1,3,5,7},{2,4,6,8},{9,11,13,15}};    int *p = a;    for (int i = 0; i<12; i++) {        printf("%d\t",*(p+i));    }    return 0;}显示结果:135724689111315Program ended with exit code: 0



十二、二维数组指针定义、初始化

二维数组名是指向行的,例如 int a[3][4],不能直接用int *p = a,因为p和a的对象性质不同,二者不是同一级指针。

C语言可以通过定义行数组指针的方法,使得一个指针变量与二维数组名具有相同的性质。

 

1、二维数组指针的定义

一般形式:

      数据类型  (*指针变量名)[二维数组列数];

例如:

int a[3][4];

int (*p)[4];   //定义一个数组指针,指向含有4个元素的一维数组。

p = a;  //p可以执行p++,但是a不可以,因为a是常量。

 

2、二维数组指针的初始化

int a[3][4];

int (*p)[4] = a;

 

3、二维数组指针的使用

*(*(p+i)+j)  就是获取二维数组的每个元素。

可以替换数组名,因为数组名是一个常量,无法进行++的操作,但是指针可以。

代码:

#include <stdio.h>int main(int argc, const char * argv[]) {    int a[3][4] = {{1,3,5,7},{2,4,6,8},{9,11,13,15}};    int *p = a;    for (int i = 0; i<12; i++) {        printf("%d\t",*(p+i));    }    return 0;}显示结果:135724689111315Program ended with exit code: 0

 

十三、指针数组和二维数组指针变量的区别

两者都可以表示二维数组。

1、定义形式不同

二维数组指针是单个变量。

二维数组指针:  int (*p)[3];

3表示二维数组的列数或者分解为一维数组的长度为3.

 

指针数组      int *p[3];

3表示数组中有3个指针。


十四、字符串指针

字符串指针变量的定义说明与指向字符变量的指针变量说明是相同的。只能按对指针变量的赋值不同来区别。

格式:

char *变量名 = “字符串内容”;

其中“字符串内容”是常量,存在常量区,是只读的。


十五、二维字符数组

char st[3][6] = {"china","japan","korea"};//或者char st[][6] = {"china","japan","korea"};

注意:可以省略行下标,但是不能省略列下标。

一维表示有几个字符串,第二维表示每个字符串的最大长度。


十六、char型指针数组、字符数组和字符串指针的区别

1、char类型的指针数组

char *name[] = {"Monday","Tuesday","Wednesday"};

数组的每个元素存放的是每个字符串在内存中的首地址。


2、字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘\0’作为字符串的结束符。

字符数组室友若干个数组元素组成的,它可用来存放整个字符串。 

3、字符串指针

<pre name="code" class="objc"><pre name="code" class="objc">char *ps = "C Language";//或者char *pa;*pa = "C Language";




字符数组:
char ps[] = "C Language";//下面是错误的char pa[];pa[] = "C Language";
因为数组名是常量,不能复制。

应用:字符串排序
/* 输入5个国家名,并按字母顺序排列后输出。 char *name[]={"CHINA","AMERICA","AUSTRALIA","FRANCE","JANPAN"}; */#include <stdio.h>#include <string.h>/** *  字符串排序 *  冒泡排序法。 *  @param arr 字符型指针数组 *  @param len 数组长度 */void paiXu(char *arr[],int len){    char *temp;    for (int i  = 0; i<len-1; i++) {        for (int j = 0; j<len-1-i; j++) {            if (strcmp(arr[j], arr[j+1])>0) {                temp = arr[j];                arr[j] = arr[j+1];                arr[j+1] = temp;            }        }    }}void printArr(char *arr[],int len){    for (int i  = 0; i<len; i++) {        printf("%s\n",arr[i]);    }    }int main(int argc, const char * argv[]) {    char *name[]={"CHINA","AMERICA","AUSTRALIA","FRANCE","JAPAN"};    printf("排序前:\n");    printArr(name, 5);    paiXu(name, 5);    printf("排序后的结果:\n");    printArr(name, 5);    return 0;}

运行结果:
排序前:CHINAAMERICAAUSTRALIAFRANCEJAPAN排序后的结果:AMERICAAUSTRALIACHINAFRANCEJAPANProgram ended with exit code: 0




0 0
原创粉丝点击