指针深入解析(C语言基础)带你走进指针,了解指针

内存和地址

  CPU(中央处理器)在处理数据的时候,中央的数据在内存中读取,处理后的数据也会放回内存中。

  内存会被划分为一个个单元格,每个内存单元的大小取一个字节。每个字节可以放8个比特位,每个内存单元也有一个编号,有了这个编号,CPU可以迅速找到一个内存空间。C语言中把这个编号也叫地址

C语言中给地址起了一个名字叫指针

 指针变量

 1.和地址

在C语言中创建变量就是向内存申请空间我们已经知道啦,比如,创建整型变量就是向内存申请4个字节,每一个字节都有地址,

2.和解引用操作符

  我们通过取地址操作符拿到是一个地址的数值,这个数值有时候需要存储下来,方便后期使用,所以我们这个放在  指针变量  中。

#include
int main()
{
	int a = 0;
	int* pa = &a;
	return 0;
}

 这里出现的Int *,*就是代表这个是指针变量。

  我们拿到了指针就可以找到指针指向的对象,需要用到一个操作符叫解引用操作符----*

#include
int main()
{
	int a = 0;
	int* pa = &a;
	*pa = 0;
	printf("%d", a);
	return 0;
}

这里就改变了a的值。

指针变量的大小

  32位平台下地址就是32个bit,指针变量的大小的4个字节 

  6位平4台下地址就是64个bit,指针变量的大小的8个字节 

并且指针变量的大小与类型是无关的,管他什么整型,浮点型,在相同的平台下都是一样的。

指针变量类型的意义

  指针的类型决定了对指针解引用的时候有多大的权限,比如char类型只能1个字节,而int类型可以4个字节。

void*指针

  可以理解为无具体类型的指针,但是不可以进行指针的+-运算和解引用运算。

const修饰指针

  变量是可以被修改的,如果我们需要不可以被修改,那我们需要用到const,

对于 int*pa=&a;

     const 放在*左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变,但是指针变量本身的内容可以改、

     const放在*右边,修饰是指针变量本身

 

const char *ptr="hello ", 本质就是把hello的首元素存到ptr中

指针运算

1.指针+-整数

2.指针-指针

3.指针的关系运算

   1.指针+-整数:数组在内存中是连续存放, 所以我们只要知道第一个元素的地址,就可以找到所有元素。

   2.指针-指针:

#include
int strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
	{
		p++;
	}
	return p - s;
}
int main()
{
	printf("%d\n", strlen("abc"));
	return 0;
}

野指针

  野指针就是指针指向的位置是不可知的。

原因:没有初始化,指针越界访问,指针指向的空间释放(就是可能创建的指针变量被销毁了)

数组名的理解

1.数组名就是数组首元素的地址,如下图

 指针深入解析(C语言基础)带你走进指针,了解指针_第1张图片

注意:

sizeof(数组名):计算的是整个数组的大小,单位是字节

&数组名:表示的是取出的是整个数组的地址 

 使用指针访问数组

#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
	int arr[10];
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;//首元素
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		scanf("%d", p+i);
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

一维数组传参的本质 

本质来说,一维数组传参的本质传的是数组的首元素的地址 

并且一维数组传参形参部分可以写成数组,也可以写成指针

#include
void test(int arr[])
{
	printf("%d\n", sizeof(arr));
}
void test(int* arr)

{
	printf("%d\n", sizeof(arr));

}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	test(arr);
	return 0;
}

 二级指针

二级指针就是存放一级指针的地址 

 因为你的一级指针存放地址的时候,也需要向内存申请一块空间,它自己本身是需要地址

#include 
int main()
{
	int a = 0;
	int* p = &a;
	int** pa = &p;
	return 0;

}

指针数组

整型数组:存放整型的数组

字符数组:存放字符的数组

指针数组:存放指针的数组,也就是说,这个数组里面的每一个元素都是指针

指针数组模拟二维数组 

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2 [] = {2,3,4,5,6};
	int arr3[] = { 3,4,5,6,7 };
	//数组名是元素首元素的地址
	int* p[3] = { arr1,arr2,arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", p[i][j]);
		}
		printf("\n");
	}
	return 0;
}

 数组指针变量

定义

整型指针变量: int*pint,存放的是整型变量的地址,指向的是整型数据的指针

浮点指针变量:float*pint,存放的是浮点型变量的地址,指向的是浮点型数据的指针

数组指针变量:int(*p)[ ] 存放的是数组的地址,能够指向数组的指针变量

数组指针变量初始化

#include
int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;
	return 0;
}

&arr与*p的类型是完全一致的

二维数组传参的本质

   二维数组的首元素就是第一行,是一个一维数组

在本质上传的也是地址,传递的就是这个一维数组的地址

函数指针变量

  函数是有地址的,函数名就是函数的地址,也可以用   &函数名   来表示

int       (*p)      (int x, int y)........p指向函数的参数类型和个数的交代

.               .

.               .

.               .

.            函数指针变量名字

.

p指向函数返回的类型

 函数指针变量的使用

#include
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*p)(int, int) = Add;
	printf("%d\n", (*p)(2, 3));
	printf("%d\n", p(2, 3));

	return 0;
}

typeof关键字 

 可以将复杂的类型简单化

比如,你觉得unsigned int 写起来不方便

可以改成 typeof unsigned int uint;

之后你使用就可以直接使用 unit

指针类型也可以改名字

typeof int * ptr-t; 

回调函数

  回调函数就是通过函数指针调用的函数

  如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数,被调用的函数就是回调函数

回调前

#include
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("****  0. exit             ****\n");
		printf("******************************\n");
		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:
			break;
		}

	} while (input);
	return 0;
}

回调后

#include
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;
}
void calc(int(*pf)(int, int))
{
	int ret = 0;
	int x, y;
	pintf("输入操作数:");
	scanf("%d%d", &x, &y);
	ret = pf(x, y);
	printf("ret=%d\n", ret);

}
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("****  0. exit             ****\n");
		printf("******************************\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(add);
			break;
		case 2:
			calc(sub);
			break;
		case 3:
			calc(mul);
			break;
		case 4:
			calc(div);
			break;
		case 0:
			break;
		}

	} while (input);
	return 0;
}

qsort函数使用举例

  函数实现排序

#define _CRT_SECURE_NO_WARNINGS
#include
#include
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

int main()
{
	int arr[] = { 1,3,5,6,8,2,4 };
	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;
}

非常感谢您的阅读,多多关注呀,后面我也会尽心尽力为大家带来优质好文的

 

你可能感兴趣的:(C语言学习和题目解析,c语言,开发语言)