C语言-学习之路-06

指针
内存
  • 内存:内部存储器,断电即丢失数据,如DDR、DDR2、DDR3、DDR4、SRAM等。
  • 外存:外部存储器,断电也仍然保证存储,如硬盘、ROM、光盘、U盘等。
物理存储器与存储地址空间
  • 物理存储器:实际存在的存储器芯片。例如:内存条、显卡上的RAM等。
  • 存储地址空间:对存储器编码的范围。编码:对每个物理存储单元(一个字节)分配一个号码。寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写。
内存地址
  • 将内存抽象成一个很大的一维字符数组。
  • 编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)。
  • 这个内存编号我们称之为内存地址。内存中的每一个数据都会分配相应的地址:例如:char 一个字节分配一个地址空间、int 4个字节分配四个地址空间、float 8个字节分配8个地址空间等
指针和指针变量

内存中每一个字节都对应一个编号,即 ——“地址”。
如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址。
指针的实质就是内存地址。指针就是地址,地址就是指针。
指针时内存单元的编码,指针变量是存放地址的变量。

指针变量的定义和使用
  • 指针也是一种数据类型,指针变量是一个变量。
  • 指针变量指向谁,就把谁的地址赋值给指针变量。
  • **“ * ”**操作符操作的是指针变量指向的内存空间。
#include <stdio.h>

int main()
{
	int a = 0;
	char b = 100;
	printf("%p,%p\n",&a,&b);

	int* p; // int * 整型指针,p是变量名  可以指向一个int类型变量的地址,所以也被称为指针变量
	p = &a; //将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号。
	printf("%d\n",*p); //p指向的是a的地址,*p就是a的值。

	char* p1 = &b;
	printf("%c\n",*p1);


	return 0;
}

注:& 可以取一个变量在内存中的地址。但是,不能取寄存器变量,因为寄存器变量不在内存立,而在CPU内,所以是没有地址的。

由于指针指向的是变量的地址,我们也可以通过指针间接修改变量的值!

#include <stdio.h>

int main()
{
	int a = 0;
	int b = 12;

	int *p1 = &a;
	int *p2 = &b;

	*p1 = 100;
	printf("%p\n",p1);
	printf("%d\n", *p1);
	printf("%d\n", a);

	return 0;
}

//006FFCD8
//100
//100
指针大小

可以使用sizeof()获取指针的大小,不过得到的是4(32位平台)或8(64位平台)。

	printf("%d\n", sizeof(char *));
	printf("%d\n", sizeof(int *));
	printf("%d\n", sizeof(double *));
野指针和空指针

指针变量也是变量,是变量就可以任意赋值,不要越界即可。但是,任意数值赋值给指针变量是没有意义的,因为这样的指针就成了野指针,此指针指向的区域是未知的(操作系统不允许操作此指针指向的内存区域)。所以,野指针不会直接引发错误,操作野指针指向的内存是错误的。

#include <stdio.h>

int main()
{
	int a = 100;
	int *p;

	p = a; //把a的值赋值给指针变量p,p为野指针,是没有意义的。

	p = 0x12345678; //把地址赋值给指针变量p,p为野指针,也是无意义的。
	
	*p = 1000; //错误操作。
	

	return 0;
}

那么我们就不能定义一个没有指向任何变量的指针变量吗?答案是:可以的,空指针。

int main()
{
	int * p = NULL;
	

	return 0;
}
万能指针 void *

void * 指针可以指向任意变量的内存空间:

#include <stdio.h>


int main()
{
	void * p = NULL;
	int a = 10;
	p = (void*)&a;

	*((int*)p) = 11;

	printf("%d\n", *((int*)p));
	

	return 0;
}
const修饰的指针

指针常量/常量指针

#include <stdio.h>
#include <stdlib.h>


int main()
{
	int a = 10;
	int b = 20;

	const int c = 100;  //常量
	printf("%d\n", c);

	//int * p = &c;  // 此方式,仍然可以修改常量c的值,是不安全的,不建议使用!
	//*p = 101;
	//printf("&d\n", *p);

	//常量指针
	//修饰 *,指针指向内存区域不能修改,指针指向可以改变
	const int* p1; 
	p1 = &a;
	printf("%d\n", *p1);  //10
	p1 = &b;
	printf("%d\n", *p1);  //20

	//指针常量
	//修饰p2,指针指向不能变,指针指向的内存可以修改
	int* const p2 = &a;
	//p2 = &b;
	*p2 = b;
	printf("%d\n", *p2);  //20

	

	return 0;
}

指针和数组
数组名

数组名字是数组的首元素地址,但它是一个常量:

#include <stdio.h>

int main()
{
	int a[] = {1,2,3,4,5,6};

	printf("%p\n", a);
	printf("&a[0]=%p\n", &a[0]);

	return 0;
}
指针法操作数组元素

(1)加法运算

  • 指针计算不是简单的整数相加
  • 如果是一个int * ,+1的结果是增加一个int的大小
  • 如果是一个char *, +1的结果是增加一个char的大小
#include <stdio.h>

int main()
{
	int a = 0;
	int* p = &a;
	printf("%d\n", p);
	p = p + 1;

	printf("%d\n",p);

	return 0;
}
//7600584
//7600588

通过改变指针指向操作数组元素:

#include <stdio.h>

int main()
{
	int a[] = {0,1,2,3,4,5};
	int i = 0;
	int n = sizeof(a) / sizeof(a[0]);

	int* p = a;
	for (i = 0; i < n; i++)
	{
		printf("%d\n", *p);
		p++;
	}


	return 0;
}

(2)减法运算

#include <stdio.h>

int main()
{
	int a[] = {0,1,2,3,4,5};
	int i = 0;
	int n = sizeof(a) / sizeof(a[0]);

	int* p = a+n-1;
	for (i = 0; i < n; i++)
	{
		printf("%d\n", *p);
		p--;
	}


	return 0;
}
#include <stdio.h>

int main()
{
	int a[] = {0,1,2,3,4,5};
	int *p1 = &a[2];
	int* p2 = &a[1];

	int n1 = p1 - p2;
	int n2 = (int)p1 - (int)p2;

	printf("%d\n", n1 ); // 1
	printf("%d\n", n2);  // 4



	return 0;
}
指针数组

指针数组,它是数组,数组的每个元素都是指针类型。

#include <stdio.h>

int main()
{
	//指针数组
	int* p[3];
	int a = 1;
	int b = 2;
	int c = 3;
	int i = 0;

	p[0] = &a;
	p[1] = &b;
	p[2] = &c;

	for (i = 0; i < 3; i++)
	{
		printf("%d\n", *(p[i]));

	}
	return 0;
}
//1
//2
//3

指针冒泡排序:

多级指针
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void bubble(int *p,int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - i - 1; j++)
		{
			//*(p+j) = p[j]
			if (p[j]>p[j+1])
			{
				int temp = p[j];
				p[j] = p[j+1];
				p[j+1] = temp;
			}
		}
	}
}

int main()
{
	int arr[10] = { 4,9,10,3,5,7,1,8,2,6 };
	int* p = arr;
	int len = sizeof(arr)/sizeof(arr[0]);
	printf("%d\n",sizeof(arr));
	printf("%d\n", sizeof(p));
	
	bubble(arr, 10);

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

	system("pause");
	return 0;
}
  • C语言允许有多级指针存在,在实际的程序中一级指针最常用,其次是二级指针。
  • 二级指针就是指向一个一级指针变量地址的指针。
  • 三级指针基本用不着,但是考试会考。
#include <stdio.h>

int main()
{
	//多级指针

	int a = 10;
	int* p = &a; //一级指针
	*p = 100;  //*p 就是a ,通过指针改变a的内容。

	int** q = &p;  // *q = p  **q = a

	int*** t = &q;  //*t = q   **t = p  ***t = a

	return 0;
}

指针和函数
函数形参改变实参的值
#include <stdio.h>

void swap01(int x, int y)
{
	int temp;
	temp = x;
	x = y;
	y = temp;

	printf("x=%d,y=%d\n",x,y);
}

void swap02(int* x, int* y)
{
	int temp;
	temp = *x;
	*x = *y;
	*y = temp;
}

int main()
{
	int a = 3;
	int b = 5;
	swap01(a,b); //值传递
	printf("a=%d,b=%d\n",a,b);

	a = 3;
	b = 5;
	swap02(&a, &b); //地址传递
	printf("a2=%d,b2=%d\n",a,b);

	return 0;
}
数组名作函数参数

数组名作函数参数,函数的形参会退化为指针:

#include <stdio.h>

void printArray(int* a, int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d\n",a[i]);
	}
}

int main()
{

	int a[] = { 1,2,3,4,5,6 };
	int n = sizeof(a) / sizeof(a[0]);

	printArray(a, n);

	return 0;
}
指针作为函数的返回值
#include <stdio.h>


int a = 10;

int* getA()
{
	return &a;
}

int main()
{
	*(getA()) = 111;
	printf("a=%d\n",a);

	return 0;
}
指针和字符串
字符指针
#include <stdio.h>

int main()
{
	char str[] = "hello world";
	char* p = str;

	*p = 'm';
	p++;
	*p = 'i';
	printf("%s\n", str);
	
	const char *str1 = "mike jiang";
	printf("%s\n",str1);

	return 0;
}
实践中常用的字符串应用模型
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//(1)利用strstr标准库函数找出一个字符串中substr出现的个数
//a)while模型
int main()
{
	const char* p = "11abcd111122abcd333abcd22abcd234234qqq";
	int n = 0;
	//strstr() 为C语言标准库函数,它的函数原型为: char *strstr(const char *str1,const char *str2)
	//其中str1为要在其中查找的字符串,str2为要查找的字符串。
	//如果str2是str1的子字符串,则函数返回str2在str1中第一次出现的位置的指针。
	//否则,函数返回NULL。
	while ((p = strstr(p, "abcd")) != NULL)
	{
		//检查到存在
		//重新设置起点位置
		p = p + strlen("abcd");
		n++;
		if (*p == 0) //如果检查到0,则到结束符
		{
			break;
		}
	}

	printf("n=%d\n", n);

	return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//(1)利用strstr标准库函数找出一个字符串中substr出现的个数
//a)while模型
int main()
{
	const char* p = "11abcd111122abcd333abcd22abcd234234qqq";
	int n = 0;
	do
	{
		p = strstr(p, "abcd");
		if (p != NULL)
		{
			n++;

			p = p + strlen("abcd");
		}
		else
		{
			break;
		}
	} while (*p != 0);

	printf("n=%d\n", n);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想成为风筝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值