用一局王者的时间 彻底拿下c语言指针(覆盖c语言各个部分 考试周限定超详细版)

新年快乐~这份超详细的指针详解请收下(不信还有更详细的了捏)

 

 一定要认真看捏~这篇文章已经写了好久啦 现在凌晨三点终于完工了 下面是我对指针的一些理解 希望对你有帮助 临近期末 一起加油哦

目录

1.什么时候用指针?

2.内存地址与指针

1.地址与取地址运算

2.地址与字节的关系

2.什么是指针?

1.指针的类型

2.指针所指向的类型

3.指针的值(指针所指向的内存区或地址)

4.指针的算术运算

3.指针的定义与使用

1.变量的访问形式

2.指针与指针变量

指针变量的定义

指针变量的引用

& 与* p = &a ;

4.指针与函数

1.传递指针给函数

 2.函数返回指针

3.指向函数的指针

5.指针与数组

一维数组与指针  

二维数组与指针

指针和数组的区别

6.指针与结构类型

7.指针与字符串

定义指向字符串的指针变量    

输出字符串

字符数组与字符指针变量的区别  

字符串指针作函数参数

8.指针的类型转换



指针总结

1.什么时候用指针?

1. 需要改变实参的时候, 只能用指针。
2. 传递大型结构并且"只读"其元素的时候。因为大型结构通过值传递, 需要拷贝其每个元素, 这样效率太低
3. 需要遍历数组或频繁引用其元素时,这样效率比使用下标高。
4. 动态分配空间时, 必须使用指针。
5. 传递数组时, 必须使用指针。
6. 函数返回多个值,某些值就必须通过指针带回。
7.函数返回运算的状态,结果通过指针返回。

例如如下代码 要返回两个值(即最大值和最小值),而return只能返回一个,就要用指针方法了。

#include<stdio.h>
void compare(int a[],int len,int *min,int *max){
	*min=*max=a[0];
	for(int i=0;i<len;i++){
		if(a[i]<*min){
			*min=a[i];
		}
		if(a[i]>*max){
			*max=a[i];
		}
	}
}



int main(){
	int a[]={0,1,2,3,4,5,6,7,8,9};
	int min,max;
	compare(a,sizeof(a)/sizeof(a[0]),&min,&max);
	printf("min=%d,max=%d",min,max);
	return 0;
}

2.内存地址与指针

运行程序时程序、数据都存在内部存储器。 内部存储器由很多存储单元组成。 每个存储单元有自己独有的地址,称为内存地址。

每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。

打个比方,计算机的内存可以看成一排排房屋,每个房屋都要有门牌号,这些门牌号就相当于计算机的内存地址,而房屋里面的东西就相当于需要存放的各种各样的数据,所以要想访问这些数据,就得知道它的内存地址。

先来认识一下地址吧。

1.地址与取地址运算

C程序中的变量在内存中占有一个可标识的存储区, 每一个存储区是由若干个字节组成, 每一个字节都有自己的地址, 在程序中所定义的变量,编译系统会根据变量的类型,分配不同长度的内存。 当变量只占用一个字节,该字节的地址就是变量的地址。 当变量占用连续若干字节,第一个字节的地址就是变量的地址。

C语言允许在程序中使用变量的地址 ( 通过地址运算符&可得到)  

如:  float   x ;         变量 x 的地址 ---- &x          

       int    a[10] ;    数组变量 a 的地址 ---- 数组名 a

实例如下,它将输出定义的变量地址:

#include <stdio.h>
 
int main ()
{
    int a = 10;
    int *p;              // 定义指针变量
    p = &a;
 
   printf("a变量的地址: %p\n", p);//取地址用%p
   return 0;
}

当以上代码被执行时,产生的结果是:

说明:(1)p是一个指针,存储着变量a的地址。

(2)指针p的类型必须与变量a的类型保持一致,因为整型指针只能存储整型变量的指针地址。

2.地址与字节的关系

地址就是以字节做单位的。指针类型占4个字节。

具体某种数据类型所占的字节数是编译器根据操作系统的位数决定的,这时就可以用sizeof对其进行测定。

用sizeof操作一下叭~

#include<stdio.h>
int main(){
	int ip;
	float fp;
	double dp;
	char chp;
	//sizeof操作符用于计算变量的字节大小
	printf("Size of int:%ld bytes\n",sizeof(ip));
	printf("Size of float:%ld bytes\n",sizeof(fp));
	printf("Size of double:%ld bytes\n",sizeof(dp));
	printf("Size of char:%ld bytes\n",sizeof(chp));
	return 0; 
}

运行结果如下:

附上一张c类型所占字节数的表格

c类型32位平台(所占字节数)64位平台(所占字节数)
int44
float4

4

short int22
char11
double88
int *44

2.什么是指针?

通常程序猿口中的指针指的就是内存的地址。

指针变量是用来存放内存地址的变量。就像其他变量或常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。下面是指针的正确声明样例:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *chp;    /* 一个字符型的指针 */

所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。

不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同

说到这里 就一起看看指针的类型和指针所指向的类型吧(关于指针你必须知道的...)

1.指针的类型

从语法的角度看,只要 把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型 。这是指针本身所具有的类型。比如说:
(1)int*ptr;
//指针的类型是 int*
(2)char*ptr;
//指针的类型是 char*
(3)int**ptr;
//指针的类型是 int**
(4)int(*ptr)[3];
//指针的类型是 int(*)[3]
(5)int*(*ptr)[4];
//指针的类型是 int*(*)[4]

2.指针所指向的类型

当通过指针来访问指针所指向的内存区时,指针所指向的类型决定了 编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须 把指针声明语句中的指针名字和名字左边的指针声 明符*去掉,剩下的就是指针所指向的类型。 例如:
(1)int*ptr;
//指针所指向的类型是 int
(2)char*ptr;
//指针所指向的的类型是 char
(3)int**ptr;
//指针所指向的的类型是 int*
(4)int(*ptr)[3];
//指针所指向的的类型是 int()[3]
(5)int*(*ptr)[4];
//指针所指向的的类型是 int*()[4]
在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念,要注意区分。

3.指针的值(指针所指向的内存区或地址)

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而 不是一个一般的数值。 在 32 位程序里,所有类型的指针的值都是一个 32 位整数,因为 32 位程序里内存地址全都是 32 位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为 sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是xxxx,就相当于说该指针指 向了以 xxxx为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
吾日三省吾身。以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指 的类型是什么?该指针指向了哪里?

4.指针的算术运算

指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,以单元为单位。例如:(1)
char a[20];
int *ptr=(int *)a; //强制类型转换并不会改变 a 的类型
ptr++;

在上例中,指针 ptr 的类型是 int*,它指向的类型是 int,它被初始化为指向整型变量 a。接下来的第 3 句中,指针 ptr 被加了 1,编译器是这样 处理的:它把指针 ptr 的值加上了 sizeof(int),在 32 位程序中,是被加上 了 4,因为在 32 位程序中,int 占 4 个字节。由于地址是用字节做单位的, 故 ptr 所指向的地址由原来的变量 a 的地址向高地址方向增加了 4 个字节。 由于 char 类型的长度是一个字节,所以,原来 ptr 是指向数组 a 的第 0 号 单元开始的四个字节,此时指向了数组 a 中从第 4 号单元开始的四个字节。

(2) 我们可以用一个指针和一个循环来遍历一个数组,比如说:
int array[99]={0};
int *ptr=array;
for(i=0;i<99;i++)
{
(*ptr)++;
ptr++;}

这个例子将整型数组中各个单元的值加 1。由于每次循环都将指针 ptr 加 1 个单元,所以每次循环都能访问数组的下一个单元。

3.指针的定义与使用

1.变量的访问形式

直接访问 : 通过变量名或地址访问变量的存储区。例如

scanf ( “%d” , &x ) ;   
x = sqrt(x) ;              
printf ( “%d” , x ) ; 

间接访问 : 将一个变量的地址存放在另一个变量中.      如将变量 x 的地址存放在变量p 中, 访问x 时先找到p, 再由p 中存放的地址找到 x。

2.指针与指针变量

指针: 一个变量的指针就是该变量的地址(指针就是地址)。

指针变量: 存放变量地址的变量, 它用来指向另一个变量。

指针变量的定义

1. 格式 :   数据类型     * 指针变量名 ;          

例       int      *p1 ;                    

          char   *p2 ;

2. 说明 : (1) 在变量定义时, * 号表示该变量是指针变量 ( 注意: 指针变量是p1 , p2 , 不是*p1 , *p2 )

(2) 定义指针变量后, 系统为其分配存储空间, 用以存放其他变量的地址, 但在对指针变量赋值前, 它并没有确定的值, 也不指向一个确定的变量。

int   x , *p ;  
      x = 1024;

如上例,指针变量p的值是随机值, 此时p 和 x 没有关系捏。

(3) 使指针变量指向一个确定的变量必须进行赋值 。

int   x , *p ;  
x = 5 ;
p = &x ; //这样就可以啦

指针变量的引用

1. p与*p不同, p是指针变量, p的值是p所指向的变量的地址 ,*p 是p 所指向的变量 , *p的值是p所指向的变量的值。

2.引用指针变量时的 * 与 定义指针变量时的 * 不同。定义变量时的 * 只是表示其后的变量是指针变量。

& 与* p = &a ;

*有不同的含义,可以代表指针,也可以代表取值。(*是取值运算符,&是取地址运算符)

&*p  = &(*p)  = &a

*&a = *(&a) =  *p  = a

4.指针与函数

1.传递指针给函数

C 语言允许传递指针给函数,只需要简单地声明函数参数为指针类型即可。

int fun1(char *,int);
int (*pfun1)(char *,int);
pfun1=fun1;
int a=(*pfun1)("abcdefg",7); //通过函数指针调用函数。

可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。

int fun(char *);
int a;
char str[]="abcdefghijklmn";
a=fun(str);
int fun(char *s)
{
    int num=0;
    for(int i=0;;)
    {
        num+=*s;s++;
    }
    return num;
}

这个例子中的函数 fun 统计一个字符串中各个字符的 ASCII 码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把 str 作为实参传递给形参 s 后,实际是把 str 的值传递给了 s,s 所指向的地址就和 str 所指向的地址一致,但是 str 和 s 各自占用各自的存储空间。在函数体内对 s 进行自加 1 运算,并不意味着同时对 str 进行了自加 1 运算。

再来看个例子 用简单变量作为函数形参,实现两个整数的交换。来看几个代码:

#include<stdio.h>
void Swap(int a,int b);
int main(){
	int x,y;
	scanf("%d%d",&x,&y);
	Swap(x,y);
	printf("交换后:%d %d",x,y);
	return 0;
} 
void Swap(int a,int b){
	int temp;
	temp=a;
	a=b;
	b=temp;
	
}

结果:输入1 2 交换后为1 2  没有变化(可以试一试)。

还是一开始说的问题 return返回不了两个值。尽管主函数传递了两个值给Swap,因为Swap使用简单变量作为形参,所以仅仅在Swap函数内部交换了形参a和b的值,但是这个交换结果不会影响到实参。因此将简单变量作为形参时以值传递的方式调用函数无法改变实参的值

那么如何改变实参的值捏?指针变量提供了解决的方法:采用地址传递的方法。Swap的形参声明是指针变量p1和p2。调用Swap时,p1拥有&x的值,p2拥有&y的值。因此*p1和*p2分别是x和y的别名,函数体内*p1和*p2的每一次出现多少次对x和y的间接引用,而且允许函数既可以读取x和y的值,也可以修改x和y的值。

上代码:

#include<stdio.h>
void Swap(int *p1,int *p2);
int main(){
	int x,y;
	scanf("%d%d",&x,&y);
	Swap(&x,&y);
	printf("交换后:%d %d",x,y);
	return 0;
} 
void Swap(int *p1,int *p2){
	int temp;
	temp=*p1;
	*p1=*p2;
	*p2=temp;
	
}

这次就可以啦~输入1 2 交换后为2 1

注意:指针作为函数形参时,在函数体内一定要修改指针变量所指变量的内容,否则实参的值不会发生改变。来看一看下面的代码有什么问题。

#include<stdio.h>
void Swap(int *p1,int *p2);
int main(){
	int x,y;
	scanf("%d%d",&x,&y);
	Swap(&x,&y);
	printf("交换后:%d %d",x,y);
	return 0;
} 
void Swap(int *p1,int *p2){
	int *temp;
	temp=p1;
	p1=p2;
	p2=temp;
	
}

 有错误 错在哪?

从运行结果看,两个数没有实现交换,尽管Swap函数体内实现了形参所指的地址的互换,但是形参所指的地址,但形参所指的变量值并没有发生互换。执行流程返回主函数时,由于形参p1和p2是动态局部变量,离开定义它们的Swap函数时,分配给它们的内存被释放了。

因此,指针变量作为函数形参时,在被调函数内必须改变形参所指变量的值,才能在函数调用结束后改变实参的值。那么这样修改,可以吗?

#include<stdio.h>
void Swap(int *p1,int *p2);
int main(){
	int x,y;
	scanf("%d%d",&x,&y);
	Swap(&x,&y);
	printf("交换后:%d %d",x,y);
	return 0;
} 
void Swap(int *p1,int *p2){
	int *temp;
	*temp=*p1;
	*p1=*p2;
	*p2=*temp;
	
}

操作一下,输入两个数之后,卡住了,怎么会这样?

说明:这种方法可能会破坏系统的正常 工作状态,因为temp是一个指针变量 但是在函数中并没有给temp一个确定的地址,这样它所指向的内存单元是 不可预见的,而对*temp的赋值可能 带来危害。下次不要这样写啦!

继续改:

#include<stdio.h>
void Swap(int *p1,int *p2);
int main(){
	int x,y;
	scanf("%d%d",&x,&y);
	Swap(&x,&y);
	printf("交换后:%d %d",x,y);
	return 0;
} 
void Swap(int *p1,int *p2){
    int a;
	int *temp=&a;
	*temp=*p1;
	*p1=*p2;
	*p2=*temp;
	
}

这样就可以啦~

 2.函数返回指针

前面我们用到的函数, 有些无返回值, 有些有返回值, 返回值类型多为 int , float , char . 一个函数的返回值也可以是一个指针 类型的数据(即地址)。

定义函数:  

数据类型    * 函数名 ( 形参表列 )  {    函数体 ;   }  

例: int     * fun ( int  a , int  b )  {    函数体 ;   }  

说明:   定义一个返回指针值的函数与以前定义函数格式 基本类似, 只是在函数名前加 *  , 它表明该函数返回一个指针值 , 而这个指针值是指向一个 int 型数据。

#include <stdio.h>
#include <string.h>
#define N 99
char  a[N] ;
char  *p=a ;
char  *alloc( int  n)
{  char  *begin ;
    if ( p+n <= a+N )
          {  begin=p ;    
             p=p+n;
             return(begin);
           }
     else    return(NULL);
}   
int  main(void)
{  char *p1,*p2 ;   int  i ;
    p1=alloc(10);
    strcpy(p1,”123456789”);
    p2=alloc(5);
    strcpy(p2,”abcd”);
    printf("a=%p\n”, buf);
    printf(“p1=%p\n”, p1);
    printf(“p2=%p\n”, p2);
    puts(p1);     puts(p2);
    for( i=0 ; i<15 ; i++)
        printf(“%c”, a[i]);
}

3.指向函数的指针

函数的指针: 函数的入口地址          

在程序执行过程中调用函数时, 计算机会转去执行函数体内的语句, 因此计算机必须知道函数在什么地方。 实际上函数在内存中也要占据一片存储单元, 这片存储单元一个起始地址, 我们称其为函数的入口地址, 即函数的指针, 这个函数的入口地址是用函数名来表示. 因此我们可以定义一个指针变量, 让它的值等于函数的入口地址,   然后可以通过这个指针变量来调用函数, 该指针变量称为指向函数的指针变量 。

对于指向函数的指针变量  

1. 定义格式:  数据类型    ( *指针变量名) ( 形参表列) ;            

int         ( *pt ) ( int   arr[ ] ,  int  n ) ;  

说明: ① 数据类型: 指针变量所指向的函数的返回值类型            

         ② 形参表列: 即指针变量所指向的函数的形参表列          

         ③ 格式中的小括号不能省略 

2. 应用  (1) 让指针变量指向函数       pt = add ;        因为函数名为函数的入口地址,  所以直接将函数名        赋给指针变量即可  

              (2) 使用指针变量调用函数          格式 :  (*指针变量名) ( 实参表列)

#include  <stdio.h>
int add( int   b[ ] , int   n);
int  main(void )
{  int  a[6]={ 1, 3, 5, 7, 9, 11}, total ;
    int  (*pt) ( int  b[ ] , int n ) ;   
    pt = add ; 
    total = (*pt) ( a , 6 ) ;                  
    printf( “total = %d \n” ,total ) ; 
 }
int  add( int   b[ ] , int   n ) 
{  int  i , sum = 0 ;
    for ( i = 0 ; i<n ; i++ )
          sum = sum+b[i];
    return(sum);
}

5.指针与数组

一维数组与指针  

一定要记住:数组名表示的是数组首元素的地址。

不信你看:

#include <stdio.h>
int main()
{
    int arr[4] = { 0,1,2,3 };
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

打印结果是相同的。

1. 一维数组及元素的地址表示      int   a[5] = { 1, 2, 3, 4, 5 } ;            

数组的地址 : a    

  元素地址
*aa[0]&a[0]a
*(a+1)a[1]&a[1]a+1
*(a+2)a[2]&a[2]a+2
*(a+3)a[3]&a[3]a+3
*(a+4)a[4]&a[4]a+4

2.用指针变量引用数组元素

(1) 定义指针变量       int   *p ,  a[5] = { 1, 2, 3, 4, 5 } ;       p = a ;

(2)引用数组元素

下标法地址法指针法
第k个元素a[k]*(a+k)*(p+k)
第k个元素地址&a[k]a+kp+k

注意 : 指针变量也可以加下标   p[k]   等价于  a[k]  

① 分别用三种方法输出数组元素, 其效率不同, 下标法与地址法的效率相同, 指针法的效率较快  

② 用指针变量访问数组元素时要注意下标是否越界

例1 将数组a中全部元素加1, 再输出a

#include <stdio.h>
 int main( void)
 {  int   a[5] = {1, 3, 5 ,7 ,9 }, *p , j ;
    for ( p=a ; p<a+5 ; p++ )
        printf("%3d", *p);//可以用p++ , 但不能用a++ 因为a 代表数组的起始地址 它是地址常量,不能改变.而p 是一个指针变量

    printf("\n") ;
    for ( j=0 ; j<5 ; j++)   
          a[j]=a[j]+1 ;
    p=a;
   for ( j=0 ; j<5 ; j++)
        printf("%3d", *(p+j) ) ;//使用指针变量时要注意它的当前值
   printf("\n") ;
 }     

3. 指向数组的指针变量作函数参数

二维数组与指针

二维数组名a表示二维数组的首地址, 也是第0个元素(a[0])的地址

a+1代表第1个元素(a[1])的地址    a+1 等价于&a[1]

a+2代表第2个元素(a[2])的地址    a+2 等价于 &a[2]

a[0] <=>&a[0][0]          a[0]+1 <=>&a[0][1]

a[2] <=> &a[2][0]          a[2]+3 <=>&a[2][3]

*a, *a+1, *(a+1), *(a+2)+3分别表示什么值?

因a 等价于&a[0]

*a表示元素a[0][0]的地址

*a+1表示元素a[0][1]的地址

*(a+1)表示元素a[1][0]的地址

*(a+2)+3表示元素a[2][3]的地址

二维数组元素的地址

元素地址元素地址元素地址
a[0][0]&a[0][0]*a[0]a[0]**a*a
a[0][1]&a[0][1]*(a[0]+1)a[0]+1*(*a+1)*a+1
a[1][0]&a[1][0]*a[1]a[1]*(*(a+1))*(a+1)
a[1][2]&a[1][2]*(a[1]+2)a[1]+2*(*(a+1)+2)*(a+1)+2
a[2][3]&a[2][3]*(a[2]+3)a[2]+3*(*(a+2)+3)*(a+2)+3

倒数第二列在c语言中有专门的名字:解引用。

"*"的作用是引用指针指向的变量值,引用其实就是引用该变量的地址,"解"就是把该地址对应的东西解开,解出来,就像打开一个包裹一样,那就是该变量的值了,所以称为"解引用"。也就是说,解引用是返回内存地址中对应的对象。

指向二维数组的指针

定义格式:类型名 (*指针变量名)[数组长度];

实例1.用指针变量输出二维数组里的元素

#include<stdio.h>
int main(void){
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};//定义一串二维数组
int (*p)[4];//定义指针 p指向一个包含有4个整型数据的一维数组
p=a;
int i,j;
for(i=0;i<3;i++){
	for(j=0;j<3;j++)
	{
		printf("%d\t",*(*(p+i))+j);//输出
	}
	printf("\n");
}	
}

虽然在定义p的时候只用了一个*,但p实际上是一个二级指针变量(指向指针的指针)

在这里解释一下二级指针吧

指针变量也有自己的地址,那指针变量的地址存放在哪里? 这就是二级指针(指地址) 。

int t=20;
int *pt=&t;//t的地址存放在pt中
int **ppt=&pt;//pt的地址存放在ppt中
//pt是一级指针 ppt是二级指针

实例2.如果不上机,你知道打印结果是什么吗

#include <stdio.h>
int main()
{
        char matrix[2][5] = {
                'I', 'l', 'o', 'v', 'e',
                'C', 'S', 'D', 'N', '!'
        };
        char *p;

        p = &matrix[0][3];

        printf("%c", *p);
        printf("%c", *p++);
        printf("%c", *++p);
        printf("\n");

        return 0;
}

答案:vvC

分析:

  • 字符指针 p 指向二维数组 matrix 第一行第四个元素('v')
  • 由于 p 是一个字符指针(其跨度为一个字符),所以 p++ 指向 matrix 第一行第五个元素('e'),但加加运算符(++)作为后缀使用时,是先使用原来的值再进行加一运算,因此 *p++ 仍然打印 'v',随后指针指向 'e'
  • 加加运算符(++)作为前缀使用时,则先让指针(p)指向下一个元素,再间接取出里边的值。由于二维数组实质上是一维数组的线性扩展,所以 ++p 指向的是第二行第一个元素('C')

练习1.将数组a[M][N]中的每个元素向右移一列, 最后一列换到最左一列 要求:必须用指针实现

练习2.将数组a[2][3]中的每个元素向右移一列, 最后一列换到最左一列 要求:必须用指针实现

指针和数组的区别

1.含义上的区别。 
数组对应着一块内存区域,而指针是指向一块内存区域。其地址和容量在生命期里不会改变,只有数组的内容可以改变;而指针却不同,它指向的内存区域的大小可以随时改变,而且当指针指向常量字符串时,它的内容是不可以被修改的,否则在运行时会报错。

2.计算内存容量的区别。 
用运算符sizeof可以计算出数组的容量(字节数),而用sizeof却无法计算指针所指内存的容量,在进行参数传递时,数组会自动退化为同类型的指针。

3.指针是动态分配空间,通过malloc在堆上分配所需要的空间,分配的空间不一定连续,在使用完之后需要调用free()来释放分配空间。而数组是静态分配空间,在全局变量区或者栈上分配空间分配的空间是连续的,局部变量在生命周期结束后自动释放,全局变量在程序结束完自动释放。

4.指针是通过地址间接访问,而数组是直接访问数值。因此指针的访问效率低,数组的访问效率高。指针使用不当会造成内存泄漏,数组使用不当会造成数组越界。

5.函数参数 数组要用相应的指针当形参,而指针要用指针的指针来当形参。


6。指针名是变量,数组名是指针常量。所以指针p可以进行p++,而数组名不可以用于a++。

7.指针保存的是地址,数组保存的是数值。

练习:将5个字符串从小到大排序后输出(用指针数组实现)

先看看如果是5个数字该怎么排序

6.指针与结构类型

可以声明一个指向结构类型对象的指针。实例:

struct MyStruct { 
int a; 
int b;
int c; }; 
struct MyStruct st={20,30,40}; //结构体初始化
struct MyStruct *ptr=&st; //声明了一个指向结构对象st的指针。它的类型是MyStruct *,它指向的类型是MyStruct。 
int *pstr=(int*)&st; //声明了一个指向结构对象st的指针。但是pstr和它被指向的类型ptr 是不同的。

请问怎样通过指针 ptr 来访问 st的三个成员变量?

答案:

ptr->a; //指向运算符,或者可以这们(*ptr).a,建议使用前者

ptr->b;

ptr->c;

又请问怎样通过指针 pstr 来访问 st的三个成员变量?

答案:

*pstr //访问了st 的成员a。*(pstr+1); //访问了st 的成员b。*(pstr+2) //访问了st的成员c。

7.指针与字符串

定义指向字符串的指针变量    

char   *p = “China”; //将字符串的首地址赋给p

说明 : 这里没有定义字符数组, 但字符串在内存中还是以数组 形式存放的. 字符串在内存中占有一片连续的存储单元, 以‘\0’结束。

输出字符串

  printf(“%s\n” , p ) ; 输出字符串时, 先输出p 指向的 第一个字符, 然后系统自动执行p++, 使p 指向下一个字符, 再输出该字符, 直到遇到‘\0’ 为止

也可以用循环逐个输出字符串中的字符:  for ( ; *p!=‘\0’; p++ )    printf(“%c”, *p ) ;   

字符数组与字符指针变量的区别  

char   s[ ] =“C  Language” ;    char   *p =“C  Language” ; 

1. 存储方式不同:   s 存放的是字符串中的字符和‘\0’                                  

                             p 存放的是字符串的首地址

2.  虽然s 和p 都能代表字符串的首地址, 但s是数组名, 是一个常量, 而p是一个指针变量, 因此 s++ 的 写法是错的,  而p++ 的写法是对的

3. 赋值方式不同 :s 可以进行初始化,但不能使用赋值语句。p 既可以初始化, 也可以赋值。

4.定义数组s 时, 系统会给s 分配一片连续的存储单元定义指针变量p 时, 只给p 分配一个存储单元。

字符串指针作函数参数

字符串覆盖 代码如下:

//方法1
#include<stdio.h>
#include<string.h>

void   copystr(char a[ ] , char  b[ ])
{   int   i = 0 ;
     while ( a[i]!='\0' )
         {   b[i] = a[i] ;   
              i++; 
          }
    b[i] = '\0' ;
}
int  main(void)
{  char  a[ ] = "cat", b[ ] = "tiger";
   puts(a);     puts(b);
   copystr(a , b);
   puts(a);     puts(b);
}
//方法2
#include<stdio.h>
#include<string.h>

void   copystr(char *a , char  *b)
{   int   i = 0 ;
     for(;*a!='\0';a++,b++)
     *b= *a;   
    *b = '\0' ;
}
int  main(void)
{  char  *a= "cat", *b= "tiger";
   puts(a);     puts(b);
   copystr(a , b);
   puts(a);     puts(b);
}

进阶:单词索引

一个单词表存放了5个表示颜色的单词,输入一个字母,在单词表中查找并输出所有以此字母开头的单词,若没有找到,输出Not Found

#include<stdio.h>
int main(){
	int i,flag=0;
	char ch;
	char *color[5]={"red","blue","yellow","green","black"};
	printf("Input a letter");
	ch=getchar();
	for(int i=0;i<5;i++){
		if(*color[i]==ch){
			flag=1;
			puts(color[i]);
		}
	}
	if(flag==0){
		printf("Not Found");
	}
	return 0;
}

8.指针的类型转换

当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指 针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大 多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的 类型和指针表达式所指向的类型是一样的。
float f=12.3;
float *fptr=&f;
int *p;

在上面的例子中,假如我们想让指针 p 指向实数 f,应该怎么办? 是用下面的语句吗?

p=&f;

不对。因为指针 p 的类型是 int *,它指向的类型是 int。表达式 &f 的结果是一个指针,指针的类型是 float *,它指向的类型是 float。 两者不一致,直接赋值的方法是不行的对 指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致。为了实现我们的目的,需要进行"强制类型转换":
p=(int*)&f;
如果有一个指针 p,我们需要把它的类型和所指向的类型改为 TYEP *TYPE, 那么语法格式是:
(TYPE *)p;
这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE *,它指向的类型是 TYPE,它指向的地址就是原指针指向的地址。 而 原来的指针 p 的一切属性都没有被修改。(切记)
一个函数如果使用了指针作为形参,那么在函数调用语句的实参和 形参的结合过程中,必须保证类型一致 ,否则需要强制转换
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值