C语言的指针

目录

指针的基本介绍

什么是指针

指针的算术运算

 指针的比较

指针数组

定义一个指向字符的指针数组来存储字符串列表

 多重指针

基本介绍

练习

传递指针(地址)给函数

传数组给指针变量

返回指针的函数

 指针函数的注意事项和细节

 编写一个函数,生成10个随机数,并使用表示指针的数组名(一个数组元素的地址)来返回它们

函数指针(指向函数的指针)

基本介绍

函数指针定义

回调函数

指针的注意事项和细节

指针的基本介绍

  • 指针是C语言的精华,也是C语言的难点
  • 指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量
  • 获取变量的地址,用&
  • 指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值
  • 获取指针类型所指向的值,使用*ptr

什么是指针

  • 指针是一个变量,其值为另一个变量的地址,即内存地址的直接地址。在使用指针存储其他变量地址之前,对其进行声明

指针的算术运算

  • 数组在内存中是连续分布的
  • 当对指针进行++时,指针会按照它指向的数据类型字节数大小增加,每++,就增加4个字节
#include<stdio.h>

const int MAX = 3;//常量
int main () {
	int  var[] = {10, 100, 200}; // int 数组
	int  i, *ptr; // ptr 是一个 int* 指针

	ptr = var; // ptr 指向了 var 数组的首地址
	for ( i = 0; i < MAX; i++) {

		printf("var[%d]的地址= %p \n", i, ptr ); 
		printf("存储值:var[%d] = %d\n\n", i, *ptr );

		ptr++;// ptr = ptr + 1(1个int字节数); ptr 存放值+4字节(int)
	}
	getchar();
	return 0;  
}

int main () {
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   ptr = var; // 将 var 的首地址 赋给 ptr
   ptr += 2;  // ptr 的存储的地址 + 2个int的字节 (8个字节)
   printf("var[2]=%d \nvar[2]的地址=%p \nptr存储的地址=%p \nptr指向的值=%d", 
	   var[2], &var[2], ptr, *ptr);

   getchar();
   return 0;
}

const int MAX = 3;//常量
int main () {
	int  var[] = {10, 100, 200}; // int 数组
	int  i, *ptr; // ptr 是一个 int* 指针

	/* 指针中最后一个元素的地址 */
   ptr =  &var[MAX-1]; // &var[2]
   for ( i = MAX; i > 0; i--) {// 反向遍历
      printf("ptr存放的地址=%p\tvar[%d]的地址=%p\n", ptr,i,&var[i-1]);
      printf("存储值:var[%d] = %d\n\n", i-1, *ptr );
 
      ptr--; // ptr = ptr - 1(1个int的字节数 [4个字节]) 
   }
   getchar();
   return 0;
}

 指针的比较

#include <stdio.h>

int main () {
   int  var[] = {10, 100, 200};
   int  *ptr;
   ptr = var;//ptr 指向var 首地址(第一个元素)
   if(ptr == var[0]) {//错误,类型不一样 (int *)  和  (int ) 
	   printf("ok1");//不输出
   }
   if(ptr == &var[0]) { // 可以
	   printf("\nok2"); //输出
   }
   if(ptr == var) { //可以
	printf("\nok3"); //输出
   }
   if(ptr >= &var[1]) { //可以比较,但是返回false
	printf("\nok4");//不会输出
   }
   getchar();
   return 0; 
}
const int MAX = 3; 
int main () {
   int  var[] = {10, 100, 200};
   int  i, *ptr;
   ptr = var;
   i = 0;
   while ( ptr <= &var[MAX - 2] ){//&var[1]
      printf("Address of var[%d] = %x\n", i, ptr );//16进制输出
      printf("Value of var[%d] = %d\n", i, *ptr );
      ptr++;
      i++;
   } //会输出 10 , 100
   getchar();
   return 0; 
}

指针数组

#include <stdio.h>
const int MAX=3;
int main (){
    
    //指针数组
   int  var[] = {10, 100, 200};
   int i, *ptr[3];

   for ( i = 0; i < MAX; i++){

      ptr[i] = &var[i]; /* 赋的值为整数的地址 */
   }
   for ( i = 0; i < MAX; i++){  //指针数组来获取各个值

      printf("Value of var[%d] = %d \tptr[%d]本身的地址=%p\t ptr[%d]保存的地址=%p\t var[%d]的地址=%p\n",
          i, *ptr[i] , i, &ptr[i],i,ptr[i],i,&var[i]); // 10, 100, 200

   }
   getchar();
   return 0;
}

定义一个指向字符的指针数组来存储字符串列表

(定义一个指针数组,该数组的每个元素,指向的是一个字符串)

#include <stdio.h>

void main() {
	
	//定义一个指针数组,该数组的每个元素,指向的是一个字符串

	//当输出数组所指字符串直接是books[i]
	char *books[] = {
		"三国演义",
		"西游记",
		"红楼梦",
		"水浒传"
	};
	char * pStr = "abc";
	
    int strLen = sizeof(pStr);

	//遍历
	int i, len = 4;
	for(i = 0; i < len; i++) {
		printf("\nbooks[%d] 指向字符串 = %s,books[%d]的地址 = %p\n", i, books[i],i,&books[i]);

		printf("pStr指向的内容 = %s 地址 = %p\n\n",pStr,pStr);

        printf("pStr指针的长度为%d\n\n", strLen);
	}
	getchar();

}

  •  为什么books[]指针字符数组每一个地址长度都为4?

        这是指针的长度,不是数组的长度 

  • 若求的是books[]的长度,则可以使用sizeof()函数中的形参为books.即sizeof(books)

 多重指针

基本介绍

        指向指针的指针是一种多级间接寻址的方式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

  • 一个指向指针的指针变量在变量名前放置两个星号。
int **ptr//ptr的类型是int **

练习

#include<stdio.h>

void main() {
	int var;
	int* ptr;
	int** pptr;
	var = 3000;

	ptr = &var;//var变量的地址赋给ptr
	pptr = &ptr;//将ptr的地址,赋给pptr

	printf("var的地址 = %p, var = %d\n\n", &var, var);
	printf("ptr的本身地址 = %p, ptr存放的地址 = %p, *ptr = %d\n\n", &ptr, ptr, *ptr);
	printf("pptr的本身地址 = %p, pptr存放的地址 = %p, **pptr = %d\n", &pptr, pptr, **pptr);

	getchar();

}

传递指针(地址)给函数

//指针传递给函数
//数组默认也是指针传递
#include<stdio.h>

void test2(int *p); //函数声明,接收int *
void main() {
	int num=90;
	int *p = &num; //将 num 的地址赋给 p

	test2(&num); //传地址
	printf("\nmain() 中的num=%d", num); // num = 91

	test2(p); //传指针
	printf("\nmain() 中的num=%d", num);// num = 92
	getchar();
}
void test2(int *p) {
	*p += 1; //*p 就访问 num的值 
}

传数组给指针变量

        数组名本身就代表该数组首地址,因此传数组的本质就是传地址

#include <stdio.h>


/* 函数声明 */
double getAverage(int *arr, int size); //函数声明
double getAverage2(int *arr, int size); //函数声明
int main ()
{
   /* 带有 5 个元素的整型数组  */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;
   /* 传递一个指向数组的指针作为参数 */
   avg = getAverage( balance, 5 ) ;
   /* 输出返回值  */
   printf("Average value is: %f\n", avg );
   getchar();
   return 0;
}
//说明
//1. arr 是一个指针,
double getAverage(int *arr, int size){

  int i, sum = 0;       
  double avg;          
  for (i = 0; i < size; ++i){
	  // arr[0] = arr + 0
	  // arr[1] = arr + 1个int字节(4) 
	  // arr[2] = arr +  2个int字节(8)
	  //...
    sum += arr[i];// arr[0] =>数组第一个元素的地址    
			//此时函数的指针指向的地址(保存的地址)并不会改变,而是直接通过下标取值
	 printf("\narr存放的地址=%p ", arr);
  }
  avg = (double)sum / size;
  return avg;
}

#include <stdio.h>


/* 函数声明 */
double getAverage(int *arr, int size); //函数声明
double getAverage2(int *arr, int size); //函数声明
int main ()
{
   /* 带有 5 个元素的整型数组  */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;
   /* 传递一个指向数组的指针作为参数 */
   avg = getAverage2( balance, 5 ) ;
   /* 输出返回值  */
   printf("Average value is: %f\n", avg );
   getchar();
   return 0;
}
//说明
//1. arr 是一个指针,

double getAverage2(int *arr, int size){

  int i, sum = 0;       
  double avg;          
  for (i = 0; i < size; ++i){

    sum += *arr;
	printf("\narr存放的地址=%p ", arr);
    arr++;  // 指针的++运算, 会对arr 存放的地址做修改
	
  }
  avg = (double)sum / size;
  return avg;
}

 如果在getVerage()函数中,通过指针修改了数组的值,那么main函数的balance数组也会发生相应变化。getVerage函数中的指针,指向的就是main函数的数组

返回指针的函数

        C语言允许函数的返回值是一个指针(地址),这样的函数称为指针函数

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

char *strlong(char *str1, char *str2){ //函数返回的char * (指针)
	printf("\nstr1的长度%d str2的长度%d", strlen(str1), strlen(str2));
	if(strlen(str1) >= strlen(str2)){
		return str1;
	}else{
		return str2;
	}
}
int main(){
	char str1[30], str2[30], *str; // str 是一个指针类型,指向一个字符串
	printf("\n请输入第1个字符串");
	//gets(str1);//和scanf()函数的功能一致
	scanf("%s", str1);
	getchar();
	printf("\n请输入第2个字符串");
	gets(str2);
	str = strlong(str1, str2);
	printf("\nLonger string: %s \n", str);
	printf("\nstr指针本身的地址为: %p \n", &str);
	printf("\nstr指针指向的地址为: %p \n", str1);
	printf("\nstr1数组的地址为: %p \n", str1);
	printf("\nstr2数组的地址为: %p \n", str2);
	getchar();
	return 0;
}

 指针函数的注意事项和细节

  • 用指针作为函数返回值时需要注意,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针不能指向这些数据。
  • 函数运行结束后会销毁该函数所有的局部数据,但是这所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存。
  • C语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为static变量
#include <stdio.h>

int *func(){
    //int n = 100;//局部变量, 在func 返回时,就会销毁
    
	static int n = 100; // 如果这个局部变量是 static 性质的,那么n 存放数据的空间在静态数据区
	return &n;
}
int main(){
    int *p = func(); //func 返回指针
    printf("当前返回的指针指向的值为%d", *p);    

	int n;
	//printf("okoook~~"); //可能是使用到 局部变量  int n = 100 占用空间
	//printf("okoook~~"); 
	//printf("okoook~~"); 
    n = *p;
    printf("\nvalue = %d\n", n);// 思考,当直接int n = 100时是否能够输出100? 不一定
								//当用static修饰时,就能直接输出100
	getchar();
    return 0;
}

 编写一个函数,生成10个随机数,并使用表示指针的数组名(一个数组元素的地址)来返回它们

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

//编写一个函数,返回一个一维数组
int * f1() {
	static int arr[10]; //必须加上static ,让arr 的空间在静态数据区分配

	int i = 0;
	srand((unsigned)time(NULL));//随机数需要的,同时还有<time.h>头文件

	for (i = 0; i < 10;i++) {
		//arr[i] = rand();//伪随机数

		arr[i] = rand();//真正随机数
	}

	return arr;
}

void main() {

	int *p;
	int i;
	p = f1(); //p 指向是在 f1 生成的数组的首地址(即第一个元素的地址)
	for ( i = 0; i <10; i++) {
		printf("\n%d", *(p+i));
	}
	getchar();
}

函数指针(指向函数的指针)

基本介绍

  • 一个函数总是占用一段连续的内存区域,函数名在表达式中有时会被转换为函数所在内存区域的首地址,这和数组名非常类似
  • 把函数的这个首地址(入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数,就叫做函数指针

函数指针定义

returnType (*pointerName)(param list);
  • returnType为函数返回值类型
  • pointerName为指针名称
  • param list为函数参数列表
  • 参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称
  • 注意()的优先级高于*,第一个括号不能省略,如果写作
returnType *pointerName (param list);

就成了函数原型,它表明函数的返回值类型为returnType *

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

//说明
//1. max 函数
//2. 接收两个int ,返回较大数
int max(int a, int b){
    return a>b ? a : b;
}

int main(){
    int x, y, maxVal;
   
	//说明 函数指针
	//1. 函数指针的名字 pmax   
	//2. 第一个int 表示 该函数指针指向的函数是返回int 类型
	//3. (int, int) 表示 该函数指针指向的函数形参是接收两个 int 
	//4. 在定义函数指针时,也可以写上形参名  int (*pmax)(int x, int y) = max;
    int (*pmax)(int x, int y) = max;  //pmax函数指针指向的就是max函数
    
	printf("Input two numbers:");
	scanf("%d %d", &x, &y);
	// (*pmax)(x, y) 通过函数指针去调用 函数max
	// 调用方式 2 pmax(x,y)  
	//maxVal = pmax(x,y);同样可以输出正确

    maxVal = (*pmax)(x, y);

    printf("\nmax函数的首地址为%p\n", &max);
    printf("\nMax value: %d pmax保存的地址为%p pmax本身的地址=%p\n", maxVal, pmax, &pmax);
	getchar();
	getchar();
    return 0;
}

回调函数

  • 函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数

  • 回调函数就是由别人的函数执行时调用自己传入的函数

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


// 回调函数
//1.  int (*f)() 
//2. f 就是 函数指针 , 它可以接收的函数是 (返回 int ,没有形参的函数)
//3. f 在这里被 initArray 调用,充当了回调函数角色
void initArray(int *array, int arraySize, int (*f)()) {
    int i ;
	//循环10
    for ( i=0; i<arraySize; i++) {
        array[i] = f(); //通过 函数指针调用了 getNextRandomValue 函数
	}
}
 
// 获取随机值
int getNextRandomValue() {
	
    return rand();//rand 系统函数, 会返回一个随机整数
}
 
int main() {
    int myarray[10],i; //定义一个数组和 int
  
	//说明
	//1. 调用 initArray 函数
	//2. 传入了一个函数名 getNextRandomValue (地址), 需要使用函数指针接收
	initArray(myarray, 10, getNextRandomValue);
   
	//输出赋值后的数组
	for(i = 0; i < 10; i++) {
        printf("%d\n", myarray[i]);
    }
    printf("\n");
	getchar();
    return 0;
}

 输出值为10个整数,也就是一个数组的形式

指针的注意事项和细节

  •  指针变量存放的是地址,指针的本质就是地址
  • 变量声明的时候,如果没有确切的地址赋值,为指针变量赋一个NULL值
  • 赋为NULL值得指针被称为空指针,NULL指针是一个定义在标准库<stdio.h>中值为零的常量
int *p = NULL; // p 空指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值