C语言知识点 -- 指针

C语言知识点 – 指针


一、指针

1.指针的大小

指针的大小在32位平台是4个字节,在64位平台是8个字节。

2.指针运算

1.指针 + - 整数
会跳过n个数据类型大小的空间。

2.指针 - 指针
指针 - 指针得到的是指针之间的元素个数,而不是字节个数,前提是两个指针必须指向同一块地址空间。

用指针 - 指针的方法实现my_strlen()函数,代码如下:

int my_strlen(char* str)
{
	char* start = str;
	while ('\0' != *str)
	{
		str++;
	}
	return str - start;
}

3.指针的关系运算
p1 < p2

3.二级指针

二级指针中存放的是指针变量的地址,指向的是指针变量。

int a = 9;
int* pa = &a;
int** ppa = &pa;//二级指针
**ppa = 2;

4.指针数组

指针数组是存放指针的数组。

int* arr[5];//arr数组中有5个元素,每个元素都是int*类型

在这里插入图片描述

5.常量字符串

int main()
{
	const char* p1 = "abcdef";
	const char* p2 = "abcdef";

	char a1[] = "abcdef";
	char a2[] = "abcdef";
	
	return 0;
}

p1和p2指向的是常量字符串,是不能被修改的,需要用const来修饰,存放在常量区,只会创建一份一摸一样的字符,所以p1和p2指向的地址是一样的。

而a1和a2只是用字符串来创建了两个数组,数组之间的空间是独立的,存放在栈区,所以a1和a2指向的地址是不同的。

6.数组指针

int* p1[5];//p1先和[]结合,是数组,指针数组
int (*p2)[5];//p2先和*结合,是指针,指向数组,数组有5个元素,每个都是int型

打印二维数组元素:

//当二维数组的数组名表示首元素地址时,表示的是首行的地址,所以可以写成数组指针的形式
void PrintArr(int (*pa)[5], int row, int col)
{
	int i = 0;
	for(i = 0; i < row; i++)
	{
		int j = 0;
		for(j = 0; j < col; j++)
		{
			printf("%d ", *(*(pa + i) + j));
			//pa + i指向的是第i行,解引用就取出了第i行的地址,也就是第i行的数组名
			//*(pa + i) + j是第i行第j个元素的地址,解引用就是这个元素
			//也可以直接写成pa[i][j]
		}	
	}
}

判断:

int (*pa1)[10];
//pa1先和*结合,是数组指针,指向有10个int的数组
int (* pa2[10])[5];
//pa2先和[]结合,是指针数组,去掉数组名和元素个数,剩下的就是数组类型
//数组类型是:int(* )[5],每一个元素都是一个数组指针

7.数组传参

1.一维数组传参

void test1(int arr[])
{}
void test2(int arr[10])
{}
void test3(int *p)
{} 
//这三种传参形式都是正确的,因为一维数组的数组名就是首元素的地址

2.二维数组传参

void test1(int arr[3][4])
{}
void test2(int arr[][4])//列不能省略
{}
void test3(int (*pa)[4])//数组指针
{}

8.函数指针

指向函数的指针。

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*pf)(int, int) = Add;//pf就是指向Add函数的指针
	//Add和&Add都表示函数的地址
	int ret1 = pf(2, 3);//使用函数指针调用函数,可以不解引用
	int ret2 = (*pf)(2, 3);//要解引用的话,一定要放到括号里,不能*pf(2,3)
	return 0;
}

9.函数指针数组

代码如下:

int (* pf[4])(int, int) = {Add, Sub, Mul, Div};
//pf先和[]结合,是数组,类型为int(*)(int, int),是函数指针
//去掉数组名,剩下就是数组类型

10.指向函数指针数组的指针

代码如下:

int (* pf[4])(int, int) = {Add, Sub, Mul, Div};//函数指针数组
int (*(*ppf)[4])(int, int) = &pf;//p是指针,指向的类型为int (* [4])(int, int),是函数指针数组

11.回调函数

回调函数就是通过函数指针来调用的函数。如果你把函数指针作为参数传给另一个函数,当这个指针用来调用其所指向的函数时,就构成回调函数。

例如:

void test()
{
	printf("hehe\n");
}

void print_hehe(void (*pf)())
{
	pf();
}

int main()
{
	print_hehe(test);//将一个函数的地址传给另一个函数当作参数

	return 0;
}

采用冒泡排序的方式模拟实现qsort:

int cmp_int(const void* e1, const void* e2)//自己编写一个整形比较函数,将地址传给qsort函数当作参数
											//void*类型的指针没有固定的类型,可以接受任意类型的指针
{
	return (*(int*)e1 - *(int*)e2);//先将e1和e2转换为int*类型的指针,再进行解引用
}
//e1 - e2返回的逻辑代表升序,若使用e2 - e1,则逻辑相反,本应交换的数据不交换,就变成了降序排序

void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void swap(char* buf1, char* buf2, int width)//使用char*类型指针逐字节进行数据交换
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, int num, int width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for (i = 0; i < num - 1; i++)//外循环
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)//内循环
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//若cmp函数的返回值大于0,则进行两数交换
																				//先将base指针强制类型转换成char*类型,一次访问一个字节
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

void test3()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	print(arr, sz);
}

int main()
{
	test3();
	return 0;
}

12.指针与const

int a = 0;
const int* p1 = &a;
//const放在指针类型前,表示指针所指向的内容不能改变
//即p1所指向的a的值不能被改变
int* const p2 = &a;
//const放在*后,表示指针的指向不能改变
//即p2不能指向其他变量

13.练习

一维数组

int a[] = { 1,2,3,4 };

printf("%d\n", sizeof(a));//16

printf("%d\n", sizeof(a + 0));//   4 / 8       这里a不是上述的两种例外情况,所以a代表数组名,a+0是数组第一个元素地址,
												// sizeof(a + 0)表示第一个元素地址的大小

printf("%d\n", sizeof(*a));   //   4 / 8      a表示首元素地址,*a表示第一个元素,sizeof(*a)表示第一个元素的大小
printf("%d\n", sizeof(&a));//       4 / 8       &a取出的是数组的地址,地址大小就是4或8字节
printf("%d\n", sizeof(*&a));//       16        &a是整个数组的地址,解引用后拿到的是整个数组,计算的是整个数组的大小
printf("%d\n", sizeof(&a + 1));//    4 / 8        &a 是整个数组的地址,+1跳过整个数组,指向的是4后面的那个元素的地址
printf("%d\n", sizeof(&a[0]));//     4 / 8      取出的是数组第一个元素的地址
printf("%d\n", sizeof(&a[0] + 1));// 4 / 8      取出的是数组第二个元素的地址

字符数组

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//			6			sizeof计算的是占用内存空间的大小,不在乎有没有\0	            
printf("%d\n", strlen(arr));//			随机值		因为strlen计算的是\0之前的字符串长度,而arr数组中没有\0,所以会继续向后找\0

char arr[] = "abcdef";			// [a  b  c  d  e  f  \0]
printf("%d\n", strlen(arr));//			6			数组中\0前有6个字节
printf("%d\n", strlen(arr + 0));//		6			arr+0是首元素地址
printf("%d\n", strlen(*arr));//			err			*arr是首元素,非法访问

二维数组

int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//			12 * 4= 48   siizeof(a)表示整个数组的大小
printf("%d\n", sizeof(a[0][0]));//		4			第一行第一个元素的大小
printf("%d\n", sizeof(a[0]));//			4 * 4 = 16	a[0]是第一行的数组名,sizeof(a[0])计算的是第一行的大小
printf("%d\n", sizeof(a[0] + 1));//		4 / 8		a[0]作为第一行的数组名,没有单独放在sizeof内部,也没有被取地址,
														//所以a[0]就是第一行的第一个元素的地址,加一就是第一行第二个元素的地址
printf("%d\n", sizeof(*(a[0] + 1)));//	4			表示第一行第二个元素
printf("%d\n", sizeof(a + 1));//		4 / 8		a表示首元素的地址,二维数组首元素地址就是第一行的地址,+1表示第二行地址
printf("%d\n", sizeof(*(a + 1)));//		16			对第二行地址解引用,访问的是整个第二行元素,等价于a[1]
printf("%d\n", sizeof(&a[0] + 1));//	4 / 8		a[0]是第一行的数组名,取地址&a[0]取出的是第一行的地址,+1就是第二行地址
printf("%d\n", sizeof(*(&a[0] + 1)));//	16			解引用就是第二行的元素
printf("%d\n", sizeof(*a));//			16			a是首元素地址,就是第一行地址,*a就是第一行;*a -> *(a + 0) -> a[0]
printf("%d\n", sizeof(a[3]));//			16			sizeof是根据数据类型来得出结果的,不会真正访问内存空间
														//所以a[3] 相当于  int[3]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
以下是C语言指针的基础知识点: 1.指针的定义和初始化 指针是一个变量,其值为另一个变量的地址。定义指针变量时需要指定指针所指向的变量类型,然后使用*运算符来声明指针变量。指针变量的初始化可以为其赋值为另一个变量的地址,也可以将其初始化为NULL。 2.指针的运算 指针可以进行加、减、自增、自减等运算。指针加上一个整数n时,指向的地址会增加n个存储单元的大小,减去一个整数n时,指向的地址会减少n个存储单元的大小。 3.指针的比较 指针可以进行相等、大于、小于等比较运算。当两个指针指向同一个变量时,它们相等;当两个指针指向同一数组的不同元素时,它们可以进行大小比较。 4.指针的类型转换 指针可以进行类型转换,但需要注意转换后的指针类型必须与指向的变量类型相同,否则可能会导致程序出错。 5.指针和数组 数组名本身就是一个指针,指向数组的第一个元素。可以使用指针来访问数组的元素,也可以将指针作为函数参数来传递数组。 以下是一个例子,演示了指针的定义、初始化、运算和比较: ```c #include <stdio.h> int main() { int a = 10; int *p = &a; // 定义指向整型变量a的指针p printf("a的值为:%d\n", a); // 输出:a的值为:10 printf("p指向的变量的值为:%d\n", *p); // 输出:p指向的变量的值为:10 printf("p的值为:%p\n", p); // 输出:p的值为:0x7ffeeb5f8a2c printf("p+1的值为:%p\n", p+1); // 输出:p+1的值为:0x7ffeeb5f8a30 printf("p的地址为:%p\n", &p); // 输出:p的地址为:0x7ffeeb5f8a28 printf("p和&a的比较结果为:%d\n", p == &a); // 输出:p和&a的比较结果为:1 return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值