深入理解指针

内存单元的编号==地址==指针

int a = 10;
int *pa = &a;//取a的地址放在指针变量pa中(*pa是指针变量,int 是pa指向的是整型(int) 类型)

 指针变量的大小:

   指针变量的大小取决于地址的大小:32位平台下地址是32个bit位(4个字节),64位平台下地址是64个bit位(8个字节)。只要是指针变量,大小就是4/8字节

指针的类型决定了对指针解引⽤的时候⼀次能操作⼏个字节。 例 char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。

void*指针:

void*指针 可以理解为⽆具体类型的指针,这种类型的指针可以⽤来接受任意类型地址。但 void* 类型的指针不能直接进⾏指针的+-整数和解引⽤的运算。

const修饰指针变量:


void test2()//测试const放在*的左边情况
{
	int n = 10;
	int m = 20;
	const int* p = &n;
	*p = 20;//error
	p = &m; //ok
}
void test3()//测试const放在*的右边情况
{
	int n = 10;
	int m = 20;
	int* const p = &n;
	*p = 20; //ok
	 p = &m; //error
const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。

指针运算:

一、指针+- 整数:

p-->p+i是跳过i个type类型的数据,跳了i*sizeof(type)个字节

int main()//指针运算---指针 +- 整数
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组中元素地址是连续的
	int* p = &arr[0];//p+1
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *p);
		p++;//p=p+1
		//printf("%d ",*(p+i));
	}
return 0;
}
二、指针-指针:

指针-指针的绝对值是指针之间的元素个数

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

野指针:

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

造成野指针的原因:1,指针未初始化。2,指针越界访问。3,指针指向的空间释放。

一般为了防止野指针:assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”。(assert(p != NULL);)

传值调⽤和传址调⽤:

void Swap1(int x, int y)
{
 int tmp = x;
 x = y;
 y = tmp;
}
int main()
{
 int a = 10;
 int b = 20;
 printf("交换前:a=%d b=%d\n", a, b);//a=10  b=20
 Swap1(a, b);
 printf("交换后:a=%d b=%d\n", a, b);//a=10  b=20
 return 0;
}

在调⽤ Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,x和y确实接收到了a和b的值,但x,y的地址和a,b的地址不 ⼀样,相当于x和y是独⽴的空间,在Swap1函数内部交换x和y的值, ⾃然不会影响a和b,当Swap1函数调⽤结束后回到main函数,a和b的没法交换。Swap1函数在使⽤ 的时候,是把变量本⾝直接传递给了函数,叫传值调⽤。

通过函数并没有实现交换,原因是实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

void Swap2(int*px, int*py)
{
 int tmp = 0;
 tmp = *px;
 *px = *py;
 *py = tmp;
}
int main()
{
 int a = 10;
 int b = 20;
 
 printf("交换前:a=%d b=%d\n", a, b);//a=10  b=20
 Swap2(&a, &b);
 printf("交换后:a=%d b=%d\n", a, b);//a=20  b=10
 return 0;
}

这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数,这种函数调⽤⽅式叫传址调⽤

传址调⽤可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;

若只是需要主调函数中的变量值来实现计算,可以采⽤传值调⽤。若函数内部要修改主调函数中的变量的值,就需要传址调⽤。 

指针访问数组:

数组名是数组⾸元素(第⼀个元素)的地 址。但sizeof(数组名)表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节 。 &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。除此之外,数组名是数组⾸元素的地址。

int* p = arr;   p[i] 是等价于 *(p+i)。因为数组名是首元素地址,可以赋值 给p,其实数组名arr和p在这⾥是等价的。

本质上数组传参传递的是数组⾸元素的地址。

void test(int arr[])//参数写成数组形式,本质上还是指针
{
 printf("%d\n", sizeof(arr));
}
void test(int* arr)//参数写成指针形式
{
 printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}

⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

指针数组

指针数组是存放指针的数组。指针数组的每个元素都是⽤来存放地址(指针)的。类型是int* /char*等。

int main()
{
 int arr1[] = {1,2,3,4,5};
 int arr2[] = {2,3,4,5,6};
 int arr3[] = {3,4,5,6,7};
 //数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
 int* parr[3] = {arr1, arr2, arr3};
 int i = 0;
 int j = 0;
 for(i=0; i<3; i++)
 {
 for(j=0; j<5; j++)
 {
 printf("%d ", parr[i][j]);
 }
return0;
}

上述代码是用指针模拟二维数组。

数组指针:

数组指针变量应该是存放的是数组的地址,能够指向数组的指针变量。例如int  (*p)[10]是数组指针

存放个数组的地址,就得存放在数组指针变量中,例 int(*p)[10] = &arr;

⼆维数组传参的本质:

⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏的地址。

void test(int (*p)[5], int r, int c)
{
 int i = 0;
 int j = 0;
 for(i=0; i

函数指针变量:

函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。

函数指针类型如下:

深入理解指针_第1张图片

函数指针举例:

int Add(int x, int y)
{
 return x+y;
}
int main()
{
 int(*pf3)(int, int) = Add;
 printf("%d\n", (*pf3)(2, 3));
 //printf("%d\n", pf3(3, 5));
 return 0;
}

回调函数:

 回调函数是⼀个通过函数指针调⽤的函数。 如果把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。

例如qsort函数排序整数:

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函数各个参数:

/void qsort(void* base, //指针,指向的是待排序的数组的第一个元素
//          size_t num,  //是base指向的待排序数组的元素个数
//          size_t size, //base指向的待排序数组的元素的大小
//           int (*compar)(const void*, const void*)//函数指针 -  指向的就是两个元素的比较函数
//          );

在本代码中,就是在qsort函数中通过调用比较函数实现整数排序。

sizeof 和 strlen:

sizeof是操作符 ,计算操作数所占内存的⼤⼩,单位是字节 ,不关注内存中存放什么数据。统计时包含“\0”。

strlen是库函数,使⽤时需要包含头⽂件 string.h , srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数 ,关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界。

你可能感兴趣的:(c语言,开发语言,visual,studio)