高阶C语言|指针的进阶

news2024/11/15 8:17:20

指针的主题,在指针初阶阶段,我们知道了指针的概念:
1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2.指针的大小是固定4/8个字节(32为平台/64位平台)。
3.指针是有类型,指针的类型决定了指针的

指针的进阶

  • 一、字符指针
  • 二、指针数组
  • 三、数组指针
    • 3.1数组指针的定义
    • 3.2 &数组名 VS 数组名
    • 3.3数组指针的使用
  • 四、数组传参和指针传参
    • 4.1一维数组传参
    • 4.2二维数组传参
    • 4.3一级指针传参
    • 4.4二级指针传参
  • 五、函数指针
  • 六、函数指针数组
  • 七、指向函数指针数组的指针
  • 八、回调函数
  • 九、指针和数组面试题的解析

一、字符指针

在指针的类型中我们知道有一种指针类型为字符指针char*
一般使用:

int main()
{
   char ch='w';
   char *pc=&ch;
   *pc='w';
   return 0;
}

还有一种使用方式:

int main()
{
  const char* pstr = "hello bit.";
  printf("%s\n", pstr);
  return 0;
}

注意:这里特别容易误以为把hello bit.放到字符指针pstr里了,实际上/本质上是把字符串hello bit.首字符的地址放到了pstr中,也就是pstr指向首字符h的地址。

面试题:

#include <stdio.h>
int main()
{
	char str1[] = "hello bit";
	char str2[] = "hello bit";
	const char* str3 = "hello bit";
	const char* str4 = "hello bit";
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");
	return 0;
}

输出结果:
在这里插入图片描述

这里的str3和str4指向的是同一个常量字符串。c/c++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块,即str1的地址和str2的地址不相同,而str3和str4地址相同

二、指针数组

指针数组是一个存放指针的数组。指针只是一个修饰词,就像整形数组还是数组,存放的是整形类型的元素。
例如:
int arr1[4]={0};
int arr2[5]={0};
int arr3[3]={0};
int* arr[3]={arr1,arr2,arr3};
数组中的元素arr1,arr2,arr3表示一维数组中的首元素地址,都是int* 类型
在这里插入图片描述
同理char* arr2[3];
数组中的元素都是char*类型
在这里插入图片描述
char** arr[3][3];
数组中的元素都是char**类型
在这里插入图片描述

三、数组指针

3.1数组指针的定义

数组指针是指针。本质是指针,数组只是修饰词,就像:
整形指针:int* pint;能够指向整形数据的指针。
浮点型指针:float* pf;能够指向浮点型数据的指针。
那么数组指针:能够指向数组的指针(存放的是数组的地址的指针变量)
例如:
int (*p)[10];p先和 *结合,说明p是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。

3.2 &数组名 VS 数组名

对于下面的数组:
int arr[10]; arr和&arr分别是啥?
我们知道arr是数组名,数组名表示数组首元素地址,那&arr数组名到底是啥?看代码:

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

运行结果:
在这里插入图片描述

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

	printf("arr+1=%p\n", arr + 1);
	printf("&arr+1=%p\n", &arr + 1);
	return 0;
}

运行结果:
在这里插入图片描述
根据上面的代码结果我们发现,其实&arr和arr,虽然值是一样的,但是意义是不一样的。
实际上:&arr表示的是整个数组的地址,而不是数组首元素的地址。arr表示的是数组首元素的地址,只是因为指针一开始都指向同一个位置(即数组首元素的地址),因此一开始他们的地址是相等的,但当他们发生变化时能够明显的看出变化,数组的地址+1,表示跳过整个数组的大小,所以&arr+1相对于&arr的差值是40个字节,40个字节大小是因为整形数组有十个元素,每个元素占4个字节的大小。同时在本例中&arr的类型就是:int (*)[10],是一种数组指针类型

3.3数组指针的使用

既然数组指针指向的是数组,那么数组指针中存放的应该是数组的地址。
看代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int (*p)[10]=&arr;
	int i = 0;
	int j = 0;
	for (i = 0; i < 10; i++)
	{		
		printf("%d ", *(*p + i));
	}
	return 0;
}

运行结果:
在这里插入图片描述
我们发现通过数组指针的方法也可以运行代码,但是看起来有点复杂,多次一举,其实数组指针一般用于二维数组比较方便。
在这里插入图片描述

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
void print_arr2(int (*arr)[5], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf("%d ", (*(arr+i))[j]);//*(arr+i)==arr[i]
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
	print_arr1(arr, 3, 5);
	//数组名arr表示二维数组的首元素地址,
	//但是二维数组的首元素是二维数组的第一行
	//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址,可以用数组指针来接收
	print_arr2(arr, 3, 5);
	
	return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

回顾:
int arr[5];//含有5个整型元素的数组
int *parr1[10];//指针数组,含有10个int *类型元素的数组
int (parr2)[10];//数组指针,含有10个int () [10]类型的元素
int (parr3[10])[5];//数组指针数组,parr3[10]说明parr3是一个数组,数组中的元素类型为int()[5]

四、数组传参和指针传参

4.1一维数组传参

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

#include <stdio.h>
void test(int arr[])//ok?(√)
{}
void test(int arr[10])//ok?(√)
{}
void test(int* arr)//ok?(√)
{}
void test2(int* arr2[20])//ok?//(√)
{}
void test2(int** arr2)//ok?(√)
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
	return 0;
}

4.2二维数组传参

#include <stdio.h>
void test(int *arr)//ok?(×)//传过来的是二维数组的首元素的地址即一维数组的地址,而数组的地址要用数组指针来接收(int(*arr)[5])
{}
void test(int* arr[5])//ok?(×)//这是一个数组指针
{}
void test(int (*arr)[5])//ok?(√)
{}
void test2(int** arr)//ok?(×)//这是一个二级指针,用来接收一级指针(int*)
{}
int main()
{
	int arr[3][5] = {0};
	test(arr);
	return 0;
}

4.3一级指针传参

#include <stdio.h>
void print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(p + i));
    }
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一级指针传给参数
	print(p, sz);
	return 0;
}

思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
比如:
void test1(int* p)
{}//可以是 char* p=&n;char *p=数组名;test§;test(&n);test(arr);

4.4二级指针传参

#include <stdio.h>
void test(int** ptr)
{
	printf("num=%d]n", **ptr);
}
int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}

思考:当函数的参数为二级指针的时候,可以接收什么参数?

#include <stdio.h>
void test(int** ptr)
{

}
int main()
{
	char c = 'b';
	char* pc = &c;
	char** ppc = &pc;
	char* arr[10];
	test(&pc);//(√)
	test(ppc);//(√)
	test(arr);//(√)
	return 0;
}

五、函数指针

首先看一段代码:

#include <stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

输出结果:
在这里插入图片描述
输出的是两个地址,这两个地址是test函数的地址。那我们的函数的地址要想保存起来,怎么保存?看代码:

void test()
{
	printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void* pfun2();

pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。所以void (*pfun1)();符和条件,void (*pfun1)()=test或者void (*pfun1)()=&test,*pfun1=test。第二个只能算是函数的声明
阅读两端有趣的代码:

//代码1
(*(void(*)())0)();

首先明白0是啥,他可以是地址(地址也是由数值组成的),也可以是数值,在这里0是作为一个地址,void(*)()是一个类型,这里是将0的类型强制转换为void( * )()类型的函数指针,然后再进行解引用,解引用之后就是函数名再加个括号就是在0地址处的函数调用

//代码2

void (*signal(int, void(*)(int)))(int);

signal是一函数声明,有2个参数int和void(*)(int),返回类型是void( * )(int)
signal函数指针指向的函数的参数是int类型,返回类型是void
注:推荐《C陷阱和缺陷》,这本书中提及这两个代码。

代码2太复杂,如何简化:

typedef void(*pfun_t)(int);//这句代码的意思是将void( * )(int)重命为pfun_t
pfun_t signal(int, pfun_t);

六、函数指针数组

把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (parr1[10])();?
int parr210;
int (
)()parr3[10];
答案是:parr1先和[]结合,说明parr1是数组,数组的内容是什么呢?是int (
)()类型的函数指针。
函数指针数组的用途:转移表

例子:(计算器)

#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*****************************\n");
		printf("  1:add              2:sub\n");
		printf("  3:mul              4:div\n");
		printf("*****************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret=%d\n", ret);
			break;
		case 2:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret=%d\n", ret);
			break;
		case 3:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret=%d\n", ret);
			break;
		case 4:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret=%d\n", ret);
			break;
		case 0:
			
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

运行结果:
在这里插入图片描述

使用函数指针数组的实现:

#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int (*p[5])(int x, int y) = { 0, add, sub, mul, div };//转移表
	while (input)
	{
		printf("*****************************\n");
		printf("  1:add              2:sub\n");
		printf("  3:mul              4:div\n");
		printf("*****************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if (input <= 4 && input >= 1)
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
			printf("输入有误\n");
		printf("ret=%d\n", ret);
	}	
	return 0;
}

运行结果:
在这里插入图片描述

七、指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针;
如何定义?

void test(const char* str)
{
	printf("%s\n", str);
}
int mian()
{
	//函数指针pfun
	void (*pfun)(const char*) = test;
	//函数指针的数组pfunarr
	void (*pfunarr[5])(const char* str);
	pfunarr[0] = test;
	//指向函数指针数组pfunarr的指针ppfun
	void (*(*ppfun)[5])(const char*) = &pfunarr;
}

八、回调函数

回到函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

首先演示一下库函数qsort函数的使用:

#include <stdio.h>
#include <stdlib.h>
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - (*(int*)p2));
}
int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int i = 0;
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

运行结果:
在这里插入图片描述

qsort函数的特点:
1.快速排序的方法 2.适合于任意类型数据的排序

在这里插入图片描述
通过cplusplus搜索qsort这个函数的用法,void* base表示指向了需要排序的数组的第一个元素,size_t num排序的元素个数,size_t size一个元素的大小,单位是字节。默认是升序。

对于void的指针是一无具体类型的指针,可以接收任意类型的地址,void类型指针不能直接进行解引用操作,也不能直接进行指针运算。现在用回调函数,来模拟实现qsort函数(采用冒泡的方式),这里也使用了void*的指针。

#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
void swap(char* p1, char* p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *p1;
		* p1 = *p2;
		*p2  = tmp;
		p1++;
		p2++;
	}
}
void bubble(void* base, int count, int size, int(*cmp)(const void*, const void*))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count-1; i++)
	{
		for (j = 0; j < count - i - 1; j++)
		{    //通过函数指针cmp调用int_cmp这个函数,这里就是一个回调函数,大于0为升序,以char类型可以控制比较字节的大小,如果是int类型,那么我要比较3个字节的大小就不好比较了
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{    //通过比较字节的大小
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}
int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int i = 0;
	//将int_cmp的地址传给函数指针
	bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

九、指针和数组面试题的解析

/1.sizeof(数组名),这里的数组名表示整个数组,计算整个数组的大小。
//2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
//3.除此之外所有的数组名都表示首元素的地址。
//4.sizeof计算的是元素类型的大小,若有多个元素,便计算多个元素类型的大小的之和
//5.strlen计算的字符串的个数,接收的是字符串的地址,遇到'\0'便不再统计,且'\0'不统计
#include <stdio.h>
#include <string.h>
int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//a代表整个数组,求的是整个数组的大小,为16字节
	printf("%d\n", sizeof(a+0));//代表首元素地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(*a));//代表元素的大小,即类型的大小,为4个字节
	printf("%d\n", sizeof(a+1));//代表第二个元素的地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(a[1]));//代表第二个元素的大小,即类型的大小,为4个字节
	printf("%d\n", sizeof(&a));//代表整个数组的地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(*&a));//*&a==a,即整个数组的大小,为16字节
	printf("%d\n", sizeof(&a+1));//数组的地址+1,跳过整个数组,虽超出数组的范围,并不影响求大小,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(&a[0]));//代表首元素地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(&a[0]+1));//首元素的地址+1,为第二个元素的地址,本质为指针,大小为4/8个字节
	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//整个数组的大小,为6个字节
	printf("%d\n", sizeof(arr + 0));//代表首元素地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(*arr));//代表元素的大小,即元素类型的大小,为1个字节
	printf("%d\n", sizeof(&arr + 1));//数组的地址+1,跳过整个数组,虽超出数组的范围,并不影响求大小,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(arr[1]));//代表第二个元素所占的大小,即元素类型的大小,为1个字节
	printf("%d\n", sizeof(&arr));//代表整个数组的地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(&arr[0]+1));//首元素的地址+1,为第二个元素的地址,本质为指针,大小为4/8个字节

	printf("%d\n", strlen(arr));//strlen从首元素地址开始搜寻,遇到'\0'停止,而此数组中没有'\0',为随机值
	printf("%d\n", strlen(arr + 0));//从首元素地址开始搜寻,遇到'\0'停止,而此数组中没有'\0',为随机值
	printf("%d\n", strlen(*arr));//strlen接收的是一个地址,从所指向的地址开始计算,而*arr代表首元素,故会报错
	printf("%d\n", strlen(&arr + 1));//数组的地址+1,跳过整个数组,从数组+1处的地址开始搜寻,遇到'\0'停止,故为随机值
	printf("%d\n", strlen(arr[1]));//代表第二个元素,报错
	printf("%d\n", strlen(&arr));//代表数组的地址,从此地址开始搜寻,搜索到下个地址时跳过的是整个数组,接着从数组的地址+11处继续搜寻,遇到'\0'停止,故为随机值
	printf("%d\n", strlen(&arr[0] + 1));//代表第二个元素的地址,从第二个元素的地址开始搜寻,遇到'\0'停止,故为随机值

	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//计算整个数组的大小,包括'\0',每个元素大小为1个字节,总大小为7个字节
	printf("%d\n", sizeof(arr + 0));//代表首元素地址,本质为指针,为4/8个字节
	printf("%d\n", sizeof(*arr));//代表首元素所占的大小,即元素类型的大小,为1个字节
	printf("%d\n", sizeof(&arr + 1));//数组的地址+1,跳过整个数组,虽超出数组的范围,并不影响求大小,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(arr[1]));//代表第二个元素的大小,即元素类型的大小,为1个字节
	printf("%d\n", sizeof(&arr));//代表数组的地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(&arr[0] + 1));//代表第二个元素的地址,本质为指针,大小为4/8个字节

	printf("%d\n", strlen(arr));//strlen从首元素地址开始搜寻,遇到'\0'停止,但不计算'\0',大小为6个字节
	printf("%d\n", strlen(arr + 0));//从首元素地址开始搜寻,遇到'\0'停止,大小为6个字节
	printf("%d\n", strlen(*arr));//strlen接收的是一个地址,从所指向的地址开始计算,而*arr代表首元素,故会报错
	printf("%d\n", strlen(&arr + 1));//数组的地址+1,跳过整个数组,从数组+1处的地址开始搜寻,遇到'\0'停止,故为随机值
	printf("%d\n", strlen(arr[1]));//代表第二个元素,并不是一个地址,报错
	printf("%d\n", strlen(&arr));//代表数组的地址,从此地址开始搜寻,搜索到下个地址时跳过的是整个数组,接着从数组的地址+11处继续搜寻,遇到'\0'停止,故为随机值
	printf("%d\n", strlen(&arr[0] + 1));//代表第二个元素的地址,从第二个元素的地址开始搜寻,遇到'\0'停止,大小为5个字节

	char* p = "abcdef";//字符串常量,实际给p的是该字符串的首地址,即p指向a的地址
	printf("%d\n", sizeof(p));//p为指针,大小为4/8个字节
	printf("%d\n", sizeof(p + 1));//代表b元素地址,本质为指针,为4/8个字节
	printf("%d\n", sizeof(*p));//代表首元素所占的大小,即元素类型的大小,为1个字节
	printf("%d\n", sizeof(&p + 1));//p的地址+1,还是地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(p[0]));//p[0]==*(p+0),代表首元素的大小,即元素类型的大小,为1个字节
	printf("%d\n", sizeof(&p));//代表p的地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(&p[0] + 1));//代表第二个元素的地址,本质为指针,大小为4/8个字节

	printf("%d\n", strlen(p));//strlen从首元素地址开始搜寻,遇到'\0'停止,但不计算'\0',大小为6个字节
	printf("%d\n", strlen(p + 1));//从第二个元素地址开始搜寻,遇到'\0'停止,大小5个字节
	printf("%d\n", strlen(*p));//strlen接收的是一个地址,从所指向的地址开始计算,而*p代表首元素,顾会报错
	printf("%d\n", strlen(&p + 1));//p的地址+1,没有被束缚,为野指针,遇到'\0'停止,故为随机值
	printf("%d\n", strlen(p[0]));//代表首元素,报错
	printf("%d\n", strlen(&p));//代表p的地址,从此地址开始搜寻,搜索到下个地址时为野指针,遇到'\0'停止,故为随机值
	printf("%d\n", strlen(&p[0] + 1));//代表第二个元素的地址,从第二个元素的地址开始搜寻,遇到'\0'停止,大小为5个字节

	//二维数组
	int pa[3][4] = { 0 };
	printf("%d\n", sizeof(pa));//计算整个数组的大小,大小为48个字节
	printf("%d\n", sizeof(pa[0][0]));//代表二维数组第一行第一个元素的大小,即元素类型的大小,大小为4个字节
	printf("%d\n", sizeof(pa[0]));//代表二维数组第一行的数组大小,即一维数组的数组名,大小为16个字节
	printf("%d\n", sizeof(pa[0] + 1));//代表二维数组第一行第二个元素的地址
	printf("%d\n", sizeof(*(pa[0]+1)));//代表二维数组第一行第二个元素的大小,即元素类型的大小,为4个字节
	printf("%d\n", sizeof(pa+1));//代表二维数组第一行数组的地址+1即第二行的数组地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(*(pa + 1)));//代表二维数组第二行数组的地址进行解引用即第二行数组的数组名,大小为16个字节
	printf("%d\n", sizeof(&pa[0] + 1));//代表二维数组第一行数组的地址+1,即第二行数组的地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(*( &pa[0] + 1)));//对第二行数组的地址进行解引用,即第二行数组的数组名,大小为16个字节
	printf("%d\n", sizeof(&pa));//代表二维数组的地址,本质为指针,大小为4/8个字节
	printf("%d\n", sizeof(pa[3]));//超出数组的下标,但本质还是指针,大小为4/8个字节
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java+springboot基于云的学习笔记系统设计与开发 _44va6

学习笔记系统按照权限的类型进行划分&#xff0c;分为管理员和用户共两个模块。系统实现登录、个人信息修改&#xff0c;还可以对个人中心&#xff0c;用户管理&#xff0c;笔记本管理&#xff0c;笔记分享管理&#xff0c;分享类型管理&#xff0c;学习资料管理&#xff0c;购…

Makefile:10分钟带你了解makefile

1、Makefile是什么 在Linux系统中&#xff0c;Makefile是一个脚本文件&#xff0c;通常名为Makefile或者makefile&#xff0c;它使得程序员能够快速便捷地完成调用程序、编译代码、定位故障等工作。 Makefile是一个用于自动化构建和编译程序的脚本文件。它包含了程序的所有源…

Ubuntu下配置Redis哨兵集群

目录 准备实例和配置 启动哨兵集群 测试配置 搭建一个三节点形成的Sentinel集群&#xff0c;来监管Redis主从集群。 三个sentinel哨兵实例信息如下&#xff1a; 节点IPPORTs1192.168.22.13527001s2192.168.22.13527002s3192.168.22.13527003 准备实例和配置 要在同一台虚…

01. Docker基础环境构建

目录 1、前言 2、关于Docker 2.1、几个术语 2.2、Docker容器化的价值 3、搭建基础环境 3.1、安装VMware 3.2、安装Doker 3.3、启动 3.4、验证Docker环境 4、小结 1、前言 在这里我们将学习关于Docker的一些技能知识&#xff0c;那么首先我们应该怼Docker有一个基础的…

服务器离线部署docker,镜像迁移,mysql主从搭建等服务

公司项目要上线项目&#xff0c;买了两台云服务器&#xff0c;需进行环境部署&#xff08;1台接入公网&#xff0c;一台只能局域网访问&#xff09;&#xff0c;主要部署以下内容 1、服务器之间配置ssh免密 2、离线docker部署 3、docker镜像迁移 4、redis服务 5、minio文件…

Idea配置Maven优先从本地仓库获取依赖

idea配置maven依赖优先从指定本地仓库获取 在设置中搜索 Runner ,在VM Option中设置参数-DarchetypeCataloginternal <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http…

沐风老师MaxScript快速入门教程

Maxscript是将自定义3dMax应用的更强大的方法之一。结合内置的侦听器和编辑器&#xff0c;我们在运行时操作和测试代码&#xff0c;使其成为用户试验和探索改进软件体验的强大选项。通过Maxscript&#xff0c;我们几乎可以操作软件中的每一个对象&#xff0c;包括但不限于&…

【C++】-vector的模拟实现(以及memcpy如何使用)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

Spring-MVC的注解扫描-spring17

包括我们业务层和Dao层&#xff0c;去帮助别人去扫 只扫controller下的注解

00_YS_硬件电路图

1.主控制芯片的型号 STM32F407IGT6&#xff0c;LQFP-176&#xff0c;1MB 内部 FLASH&#xff0c;192KB RAM USART3 RS485 通信&#xff0c;芯片使用 SP3072EEN; UART5 RS232 通信&#xff0c; CAN 1 路&#xff0c;型号 SN65HVD230 USB 支持 …

Python应用实例(二)数据可视化(三)

数据可视化&#xff08;三&#xff09; 1.使用Plotly模拟掷骰子1.1 安装Plotly1.2 创建Die类1.3 掷骰子1.4 分析结果1.5 绘制直方图1.6 同时掷两个骰子1.7 同时掷两个面数不同的骰子 1.使用Plotly模拟掷骰子 本节将使用Python包Plotly来生成交互式图表。需要创建在浏览器中显示…

代码随想录算法训练营第十九天 | 动态规划系列5,6,7,8

动态规划系列5,6,7,8 377 组合总和 Ⅳ未看解答自己编写的青春版重点代码随想录的代码我的代码(当天晚上理解后自己编写) 爬楼梯进阶322 零钱兑换未看解答自己编写的青春版写完这道题后的感受重点代码随想录的代码动态规划&#xff0c;也要时刻想着剪枝操作。我的代码(当天晚上理…

异常执行结果随笔

前段时间有朋友问我异常执行顺序问题&#xff0c;这里简单记录下哈。 伪代码描述&#xff0c;当j0和j1&#xff0c;输出结果分别是什么&#xff1f; int i 0; int j 0或1; try {j i / j&#xff1b;System.out.println(i);return i; } catch (Exception e) {System.out.pri…

win10如何使用wsl配置Ubuntu并使用vscode连接

文章目录 0. 前置资料1. 下载wsl2. 下载Ubuntu3. vscode连接wsl 0. 前置资料 wsl为适用于 Linux 的 Windows 子系统&#xff0c;可参考以下微软的官方文档 https://learn.microsoft.com/zh-cn/windows/wsl/ 1. 下载wsl 点击屏幕左下角的放大镜&#xff0c;直接在输入框键入P…

第 354 场LeetCode周赛

A 特殊元素平方和 模拟 class Solution { public:int sumOfSquares(vector<int> &nums) {int res 0;int n nums.size();for (int i 0; i < n; i)if (n % (i 1) 0)res nums[i] * nums[i];return res;} };B 数组的最大美丽值 差分数组: n u m s [ i ] nums[…

cmake处理参数时的一些问题说明

cmake处理参数时的一些问题说明 函数传参空格和分号的坑函数转发的坑demo 函数传参遇到不平衡方括号的坑 函数传参空格和分号的坑 我们在处理函数和宏的时候不过不小心会遇到很多坑例如: someCommand(a b c) someCommand(a b c)因为cmake中使用空格或者分号分隔符所以上面代…

Django实现接口自动化平台(十一)项目模块Projects序列化器及视图【持续更新中】

相关文章&#xff1a; Django实现接口自动化平台&#xff08;十&#xff09;自定义action names【持续更新中】_做测试的喵酱的博客-CSDN博客 本章是项目的一个分解&#xff0c;查看本章内容时&#xff0c;要结合整体项目代码来看&#xff1a; python django vue httprunner …

LangChain 本地化方案 - 使用 ChatYuan-large-v2 作为 LLM 大语言模型

一、ChatYuan-large-v2 模型 ChatYuan-large-v2是一个开源的支持中英双语的功能型对话语言大模型&#xff0c;与其他 LLM 不同的是模型十分轻量化&#xff0c;并且在轻量化的同时效果相对还不错&#xff0c;仅仅通过0.7B参数量就可以实现10B模型的基础效果&#xff0c;正是其如…

自动化测试-selenium环境搭建

文章目录 1. 什么是自动化2. 自动化测试分类3. selenium的环境搭建4. 测试selenium 1. 什么是自动化 自动化是指使用软件工具、脚本或程序来执行一系列的任务、操作或流程&#xff0c;而无需人工干预或指导。 自动化测试&#xff1a;使用自动化工具和脚本来执行测试用例&#x…

浅谈端口 | 80端口和8080端口是2个不同的端口吗?有何区别?

目录 写在前面 端口及其特点 端口号的范围和分类 在HTTP超文本传输协议中80端口有正式的身份 写在前面 首先&#xff0c;确定以及肯定的是他们俩是完全不同的端口。一般80作为网页服务器的访问端口&#xff0c;比如一个网站的ip地址是119.119.119.119&#xff0c;我们在浏…