菜鸡日记_C语言学习之指针_2

前言

​ 本菜鸡开始了学习C语言的旅途。用这个来记录一下上课所学到的一些东西。仅作为自己的一个学习笔记。里面的内容也只是我对学到的东西的一些理解,可能有很多错误,大佬们勿喷。如果发现我理解的有哪些错误,欢迎评论区指出,希望大佬们不吝赐教。




6.数组与指针

数组名字的含义:

  • 整个数组
  1. 在数组定义的时候表示整个数组
  2. 在使用sizeof的时候表示整个数组
  3. 在使用取地址符号时 &
  • 首元素的首地址

    其他

int   a [4] ;  // a 表示整个数据
printf("sizeof(a):%d\n" , sizeof(a)) ;  // a 表示整个数组 。输出为16 4*整型
printf("%p\n" , &a ) ; // a表示整个数组的首地址
printf("%p\n" , &a+1 ) ; // a表示整个数组的首地址  + 1 表示怎加一个数组的单位  16字节




练习:

int a[10] = {0,1,2,3,4,5,6,7,8,9};
	
	printf("a=%p a+1=%p &a+1=%p\n", a, a+1, &a+1);

这里的a就是数组首元素的首地址。 a+1就是a[1]的地址。&a+1:&a就是a的地址,指向的是a这个数组,再+1,就是加指向数据的长度,也就是加上一个数组a的长度。地址加了40字节。

即:a:      &a[0]
  a+1:     &a[1]
  &a+1:    &a[10]    //a[10]其实就是指向了a这个数组后面的那个地址,只是暂时这么称呼,实际中不能这么使用,这样是数组越界。



int b[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
	
	printf("b=%p, b+1=%p, &b[0]+1=%p &b+1=%p\n", b, b+1, &b[0]+1, &b+1);

b就是数组首元素的首地址,即b[0]的地址(因为二维数组本质上还是一个一维数组,只是数组里面存的元素也是一个一维数组。)

b+1:b是b[0]的地址,所以b+1就是加一个b[0]的长度,也就是16字节,即变成了b[1]的地址。

&b[0]+1 : 这个和b+1是一样的,也是b[1]的地址。

&b+1 :&b是b这个数组的地址,指向的是b这个数组,所以+1就是加一个数组b的长度,即加了48个字节。

b:    &b[0]

b+1:    &b[1]

&b[0]+1:   &b[1]

&b+1:    &b[3] [0] //其实就是指向了b这个数组后面的那个地址,只是暂时这么称呼,即数组b的首地址加48个字节

int a[5] = {1,2,3,4,5};
	int *ptr = (int *)(&a+1);
	printf("%d %d\n", *(a+1), *(ptr-1));	//2 5
	/*
		ptr 保存了 &a[5]  ptr 指向 a[5]
		a+1: &a[1]
		*(a+1): *(&a[1])   ==  a[1]
		ptr-1: -1*sizeof(a[5]) --->  -4 --> &a[4]
		*(ptr-1): *(&a[4]) == a[4]
	*/

	int a[5] = {1,2,3,4,5};
	int *ptr = (int *)&a+1;//&a本来是指向a这个数组的,但是前面加了个int *,强制转化成一个指向int类型的指针,所以+1就是+4个字节。
	printf("%d %d\n", *(a+1), *(ptr-1));	//2 1
	/*
		(int *)&a+1: +1*sizeof(int) -->  +4
			&a[1]
		ptr保存了 &a[1]   p->a[1]
		ptr-1: -1*sizeof(int) ---> -4  --->  &a[0]
        *(ptr-1):*(&a[0]) ==  a[0]	
	*/
7.多维数组与指针

​ 二维数组其实就是一个一维数组,只不过一维数组中的元素又是一维数组

//假设有数组 int a[3][4] ----  0代表第0行
	/*
						表达的含义							表达式的值
		a(数组名)		1.代表整个数组				代表整个数组的空间 / &a[0]	
        					sizeof(a)
        				2.指针				
        				
		a[0](数组名)	1.代表整个数组sizeof(a[0])	代表a[0]这一行数组的空间 / &a[0][0] 
        				2.指针
        				
		&a[0][0]		a[0][0]元素的地址					&a[0][0]
		
		a+1				指向第1行数组的首元素					&a[1]
						a->a[0] +1 --> 1*sizeof(a[0]) ---> 1*sizeof(int [4]) --> 16
						
		&a[1]			指向第1行数组的首元素					&a[1]
		
		&a				取数组整个空间的地址					&a
		
		&a+1			整个数组a的下一个的地址			  数组a的地址+ 1*sizeof(a) 
		
		a[1]+2			a[1]指针,指向第1行的首元素			&a[1][2]
						a[1] -》 a[1][0]	
						+2 --》 2*sizeof(a[1][0])-->8
						a[1]+2 指向第1行的第二个元素
		
		*(a+1)+2		a[1]+2 指向第1行的第二个元素			&a[1][2]
		
		&a[1][2]		指向第1行的第二个元素					&a[1][2]
		
		*(a[1]+2)		第一行第二列的元素					 a[1][2]
		
		*(*(a+1)+2)		第一行第二列的元素					 a[1][2]
		
		a[1][2]			第一行第二列的元素					 a[1][2]	
	*/

8.指针数组与数组指针

指针数组:是一个数组,数组里面的元素是指针。

int *p[4]

int a;
		int *p[4];
		p[0] = &a;  

数组指针:是个指针,指向一个数组的指针。

int (*p)[4]; //p是一个指针,指向一个int [4]类型的数组

​ p+1 —> +1*sizeof(int [4]) --> +16



9.指针常量与常量指针
  1. 指针常量

int * const p;

const 修饰的是指针本身,表示P不能被修改,只能指向原本所指向的内容。

但是可以通过该指针来修改他所指向的内存内容。

//    指针常量 
 	int a=5;
 	int b=10;
    int  *  const  p = &a;
    printf("%d",*p);   //输出5
    p=&b;			 //  assignment of read-only variable ‘p’   报错!!!
                    // p 是一个常指针, 不能够再初始化后再修改他的指向
 	*p=b;			//可以修改指向空间的内容。
    printf("%d\n", *p );//输出10

2.常量指针

const int * p;或 int const * p

指向一个常量的指针,比较常见,用来限制指针的读写权限, 不可以通过该指针来修改他所指向的内存的内容,

但是可以修改他的指向。

//    常量指针 
 	int a=5;
 	int b=10;
    const int  * p = &a;
    printf("%d",*p);   //输出5
    *p=b;		 // assignment of read-only location ‘*p’ 报错
                // p 是一个常量指针 不可以通过该指针来修改他所指向的内容
 	p=&b;			//可以修改p的指向
    printf("%d\n", *p );//输出10

​ 如果我既不想改变指向,也不想改变指向空间的内容:

const int * const p = &a;

10.函数指针与指针函数

函数指针:本质是一个指针,是一个指向函数的指针。

1) :定义:指向的类型 *指针变量名 {=初始值};

如:

假设有一个sum函数
int sum(int x,int y)
{
    return (x+y);
}
定义一个指针变量p,来保存sum的地址(指向sum函数),sum是什么类型?
     typeof(sum) *p;

如何描述一个函数的类型呢?

​      函数类型:

​      函数返回值类型 (函数参数类型列表)

​      上面sum函数的类型: int (int , int)

​ 定义一个指针p指向sum函数如下:

int (int, int) *p;
	格式改变如下:
        int (*p)(int ,int) = sum;	
	这个p就是函数指针,指向了一个返回值类型为int,有两个int类型参数的函数
        p = sum;

2):怎么给函数指针赋值

​    p = 函数的地址;

​    函数的地址怎么获取?

​    &函数名 或者 函数名

​    p = sum;

​    p = & sum;

3):如何通过函数指针p去调用函数呢?

​       sum(3,5); //这是直接调用函数的方式。


​      p = sum;

​       p(3,5); //通过指针调用函数。

​    或:

​      p = & sum;

​      (*p)(3,5); ==> ( * &sum)(3,5);

​ 函数指针调用函数的格式:

​    (*函数指针)(函数实参列表);

​ 或者

​    函数指针(函数实参列表);

练习:有一个用来求一个一维数组的最大值,请利用函数指针调用它

#include<stdio.h>
int max(int a[],int n)
{
	int max=a[0];
	int i;
	for(i=1;i<n;i++)
	{
		if(a[i]>max)
		max=a[i];
	}
	return max;
}
int main()
{
	int a[5]={12,4,78,6,13};
	int (*p)(int * ,int)=max;
	printf("%d\n",p(a,5));
	return 0;
}

指针函数:返回值类型是指针的函数。

	int *func(int a)	//这就是指针函数,返回值是指针类型,但参数不一定是指针类型
	{ int *p;
			//....
		return p;
	}

11. 二级指针与多级指针

int a = 5;

​ 我们定义一个指针变量p去保存a的地址:

​    int *p = &a;

​ 我们要保存p的地址,那么应该怎么定义变量?

  ​ 指针变量的定义格式: 指向类型 *指针变量名 {=初始值};

​        int **pp = &p;

​ 二级指针:指向一级指针 的 指针 叫 二级指针

​      这个指针2保存了一个指针1变量的地址,该指针1是一个一级指针

​ 我们要保存pp的地址,那么应该怎么定义变量?

​      int ** *ppp = &pp; //ppp就是三级指针

12.动态内存分配函数

malloc // calloc // realloc 动态内存分配

​ free        动态内存释放

​ malloc : memory allocate 内存分配

​ realloc: repeat allocate

​ malloc / calloc / realloc :是从一个叫堆空间的地方分配内存的

​ heap:堆空间,生存周期随进程持续,作用范围程序内有效

​    从堆空间上分配的内存,一旦分配,那么它就一直存在(可以被你合法使用),

​    直到你手动free或者程序结束,这块内存才被回收(你不可以使用了)

12.1 malloc函数

malloc ( 配置内存空间 )
头文件:
   #include <stdlib.h>
定义函数:
   void *malloc(size_t size);
参数分析:
   size --> 需要申请内存空间的大小
返回值:
   成功 返回一个指向该内存入口地址的指针
   失败 返回NULL

示例:

	int *p = (int *)malloc(120);
	if( p == NULL )
    {
        printf("malloc fail\n");
        return -1;
    }
12.2 calloc函数

calloc ( 配置内存空间\并初始化为0 )

头文件:
   #include <stdlib.h>
定义函数:
   void *calloc(size_t nmemb, size_t size);
参数分析:
   nmemb --> 需要申请内存的块数(多少个房间)
   size --> 每一块内存的大小(房间大小)
返回值:
   成功则返回一指针
   失败则返回 NULL.

示例:

	int *p = (int *)calloc(1, 120);		//等价于 malloc(120)
	if( p == NULL )
    {
        printf("malloc fail\n");
        return -1;
    }
12.3 realloc函数

realloc ( 重新配置内存空间 )
头文件:
   #include <stdlib.h>
函数原型:
   void *realloc(void *ptr, size_t size);
参数分析:
   ptr --> 需要重新配置的原始地址
   size --> 扩容后的大小
返回值:
   成功 返回新内存的入口地址
   失败 NULL

realloc用来把ptr指向的内存扩大到size个字节的大小,原来内容保持不变,后面新增一些内存
这些新增的内存不会初始化,但是如果后面没有足够的空间了(比如要将5字节的空间扩成10字节,但是后面这块空间已经被使用了,不能够扩大),那么系统就会重新分配一块10字节的空间给它,将原来的内容拷贝过来,再将原来的空间释放,realloc的返回值就是新内存的入口地址。

12.4 free函数

free ( 释放原先配置的内存 )
头文件:
  #include <stdlib.h>
定义函数:
   void free(void *ptr);
参数分析:
   ptr --> 需要释放的内存的入口地址
返回值:
   无

示例:

	/*
 			free用来释放ptr指向的堆空间
 			 ptr必须是指向堆空间
 		*/
		void free(void *ptr);
		
	
            free(ptr);

注意:malloc/calloc/realloc 分配的空间如果不使用了,一定要free,否则会造成内存泄漏

​ 内存泄漏:不断的分配空间,空间不使用后,没有释放,找不到这个空间的地址,间接导致了不能释放

​ malloc/calloc/realloc 要和 free 成对出现

​ 示例:如何使用动态分配出来的空间

char *p = (char *)malloc(100);
		p[0] = 'a';
		*p = 'a';

		p[1] = '2';
		*(p+1) = '2';
今日发现的一些问题

1.定义函数指针的时候一定要给*p加一个括号。

int (*p)(int ,int );

之前我一直是int *p(int ,int );就一直报错,找不到错的地方。

2.如果函数指针的函数参数有数组,定义函数指针的时候,函数参数类型就要写指针。

int max(int a[],int n;)

{

}

int (*p)(**int *** ,int );

之前我一直是int *p(int ,int ),也一直报错。后面发现数组在传参的时候就是传的数组首地址,是一个指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值