06-内存与指针

概述

内存与地址

指针=地址=编号
启动一个程序,系统在内存上给程序分配一块内存空间
32位,4G 一个一个字节组成的,每个字节都会有地址编号

地址,也就是内存的编号,我们把这个编号(地址)也叫做指针

指针变量

存放指针(地址)的变量
因为地址编号(0x0000 0000)如此,所以哦我们的指针变量占4个字节就可以存下
64位编译器,内存的编号范围是0x0000000000000000 - oxffffffffffffffff ,这样的编号,需要8个字节存下,所以指针变量也需要8个字节

指针变量的定义和初始化

int main()
{
	int a = 10;
	// 定义指针三步骤
	// 1. *与符号结合代表室一个指针变量
	// 2. 要保存谁的地址,将他的定义形势放在此处
	// 3. 用*替换掉定义的变量
	int *p = &a;

	// 分析
	// 1. 与*结合代表这是一个指针变量
	// 2. p是变量,p的类型是将变量p本身拖黑,剩下的类型就是指针变量的类型,int *
	// 3. 指针变量p用来保存什么类型数据的地址,将指针变量p和指针变量p最近的*拖黑
	// 剩下的什么类型就保存什么类型数据的地址
	// p = &a;
	
}

指针基础知识

指针变量保存谁的地址就指向了谁

// 在使用时,*与p结合,取p指针所指向那块空间的内容
*p = 100;
printf("%d", a); 

在使用时,对一个表达式取*,就会对表达式减一级*,如果对表达式取&,就会加一级*

int *p;
int **q;
// int **    int *,&加一级*,所以两边相等
q = &p;
// 
*q = p;

指针变量的大小

不管什么类型的指针,大小只和系统编译器有关,因为其存放的都是地址,地址大小在一个编译器中是相同的

int main()
{
        char  *p1;
        short *p2;
        int *p3;
        int **p4;//p4也是一个指针变量  int **
        printf("%d\n",sizeof(p1));
        printf("%d\n", sizeof(p2));
        printf("%d\n", sizeof(p3));
        printf("%d\n", sizeof(p4));
        system("pause");
        return 0;
}

不同类型的指针变量,取指针指向的内容的宽度

指针的宽度=sizeof(将指针变量与指针变量最忌你的 拖黑,剩下的类型)*
宽度也叫步长
步长:指针加1跨过多少个字节

// char *p 1
// short *p 2
// int *p 4
// int **p sizeof(int *) 4
int main()
{
	// 虽然指针都是存放的地址,但是不同类型的指针从地址中取出的数据多少不一样
	
	int  num = 0x01020304;
	char  *p1 = (char *)#//int *
	short *p2 = (short *)#
	int *p3 = #
	
	//通过*取指针变量所指向那块空间内容时,取的内存的宽度和指针变量本身的类型有关
	printf("%x\n",*p1); // 04 (04是地位)
	printf("%x\n", *p2); // 0403
	printf("%x\n", *p3); // 1020304

	// 因为这三个变量中保存的都是同一个地址,所以打印出来相同
	printf("%x\n",p1);
	printf("%x\n",p2);
	printf("%x\n",p3);

	// +1求其步长,也就是sizeof(将指针变量与指针变量最忌你的* 拖黑,剩下的类型)
	printf("%x\n",p1+1);
	printf("%x\n",p2+1);
	printf("%x\n",p3+1);
	system("pause");
	return 0;

}

野指针与空指针

指针变量也是变量,是变量就可以任意赋值,不要越界即可(32 位为 4 字节,
64 位为 8 字节),但是,任意数值赋值给指针变量没有意义,因为这样的指针
就成了野指针
,此指针指向的区域是未知(操作系统不允许操作此指针指向的
内存区域)。所以,野指针不会直接引发错误,操作野指针指向的内存区域
会出问题。
![[Pasted image 20230611152524.png]]

空指针
如果使用完指针,将指针赋值为NULL,使用时判断一下指针是否为NULL,就知道指针是否被使用

int *p = NULL;
*p = 200; // 因为p保存了0x0000 0000,这个地址是不可以使用的
printf("%d", *p);

万能指针


	int a = 10;
    void *p = &a;
    //printf("%d\n", *p); err:表达式必须是指向完整对象类型的指针
    // 万能指针使用时必须强转成想要的类型,指针转指针,将void*转为int*,然后再去*取地址,得到地址内的数据
    printf("%d\n", *(int *)p);

const修饰的指针变量


    int a = 10;
    int b = 20;
    // const修饰的是*还是p,取决于const的位置

    // 不能通过*p改变指向空间的内容
    const int *p = &a;
    p = &b;
    printf("%d\n", *p);
 
    // const修饰的变量p
    // p保存的地址不可以修改
    int * const p2 = &a;
    *p2 = b;
    printf("a=%d\n",a);

多级指针

定义多级指针保存数据的地址时,定义的指针的类型只需要比保持的数据的类型多一级

	int a = 10;

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

	// *q int *p int *(*q)
	int **q = &p;

	// 如果*和&相遇,则抵消
	// **q == *(*q) == *(p) == a
	// **q == *(*q) == *(&a) == a

	// *符号结合,代表这个k是一个指针变量
	// k是一个变量,k的类型,将变量k拖黑,剩下的类型
	// k用来保存谁的地址,将变量k和最近的*一起拖黑,剩下的类型

	int *********g;
	int **********f = &g;
	

指针数组

指针结合数组

指针加1,扩过一个步长

int *p;

步长 = sizeof(int)
想要的到内存的数据,就该先得到数据的地址

// *(地址)得到的是地址里的内容

	//int  a[10] = {1,2,3,4,5,6,7,8,9,10};
	int  a[10] = { 0 };
	//a 数组名,首元素的地址
	int  *p = a;//指针p保存的是首元素的地址
	for (int i=0;i<sizeof(a)/sizeof(a[0]);i++)
	{
		   //printf("%d ",a[i]);
		   //printf("%d ", *(p+i));
		   *(p + i) = i;
	}
	for (int i = 0; i<sizeof(a) / sizeof(a[0]); i++)
	{
		   printf("%d ",a[i]);
	}
	system("pause");
	return 0;

指针运算

两指针(类型一致)相减,得到的是中间夸多多少个元素(如果一个字符串中间有\0,strlen测不到,使用指针相减可以)
两指针相加,没有意义,报错

	int  a[10] = {1,2,3,4,5,6,7,8,9,10};
	//sizeof(int [10])
	int *p = a;
	//  得到最后一个元素地址的方式,
	// 因为(&a+1)跨过一整个数组,所以将其转为int *,再减1,就会得到最后一个元素的地址
	//int  *q = (int *)(&a + 1) - 1;
	int  *q = &a[9];
	printf("%d\n",q-p);//  p+9 ==  q
	printf("%d\n",*(p+3));
	//两指针相加没有意义
	// printf("%d\n", p+q);err
	system("pause");
	return 0;

[]不是数组的专属

[] == *()
int main()

{
	    //[]是不是数组的专属
        //[] == *()

        int  a[10] = { 1,2,3,4,5,6,7,8,9,10 };
        int *p = a;
        for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
        {
	        // 这些方式都可以
		   //printf("%d ",a[i]);//a[i] == *(a+i)
		   //printf("%d ", *(p+i));
		   //printf("%d ", p[i]);// p[i]  == *(p+i)
		   printf("%d ", *(a + i));

        }

		/int  a[10] = { 1,2,3,4,5,6,7,8,9,10 };
        //  []  ==  *()
        //p[0]   ==  *(p+0)
        int a = 10;
        int *p = &a;
        p[0] = 100;
        // 会造成内存污染
        //p[1] = 200;
        printf("a=%d\n",a);

        system("pause");

        return 0;

}

指针数组

整形数组是一个数组,数组的每一个元素是整形
指针数组是一个数组,数组的每一个元素都是指针

通过二级指针操作指针数组


	int a = 10;
	int b = 20;
	int c = 30;
	// int *p1 = &a int *p2 = &b int *p3 = &c;

	int *num[3] = {&a, &b, &c};
	printf("%d\n", sizeof(num));
	// &a == num[0]
	for(int i = 0; i < sizeof(num) / sizeof(num[0]); i++) {
		printf("%d\n",*num[i]);
	}

	// 定义一个指针用来保存数组num首元素的地址
	// num = &num[0] = &(int *) = int **
	// 这种解释好理解:num[0]是int *类型。要保存int *类型的地址,需要比它多一级*
	int **k = num;
	for(int i = 0; i < sizeof(num) / sizeof(num[0]); i++) {
		printf("%d ", **(k+i));
	}

![[Pasted image 20230614142933.png]]

指针作为函数的形参

指针作为函数的形参,可以改变实参的值

void swap(int *x, int *y) {
	int k = *x;
	*x = *y;
	*y = k;
}
int main()
{
	int a = 10;
	int b = 20;
	swap2(&a, &b);
	printf("a=%d b=%d\n", a, b);
	system("pause");
	return 0;
}

![[Pasted image 20230614145724.png]]

数组作为函数的形参

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


void print_arr(int *b,int  len)
{
        int  n = sizeof(b) / sizeof(b[0]);  // *(b+0)  == *b
        printf("n=%d\n",n);
        for (int i = 0; i <len; i++)
        {
               printf("%d ",b[i]);
        }
        printf("\n");
}
int main()
{
        int  a[10] = { 1,2,3,4,5,6,7,8,9,10 };
        print_arr(a,sizeof(a)/sizeof(a[0]));//打印数值的内容//  &a[0]   int *
        system("pause");
        return 0;
}

指针作为函数的返回值

int num = 0;//在函数外面定义的变量叫全局变量,整个工程都可以使用
//整个变量程序启动开辟空间,直到程序结束释放空间
int * getnum()
{
        //{}中定义的变量叫局部变量,局部变量在函数结束之后的空间会被释放
        srand(time(NULL));
        // 这样是不对的,因为局部变量会被释放,所以不能返回局部变量的地址
        // int num = read();
         num = rand();
        return &num;//
}
int main()
{
        int * p = getnum();
        printf("%d\n",*p);
        system("pause");
        return 0;
}

指针和字符串

指针与字符串

	//指针与字符串
	char  a[] = "helloworld";//定义了一个字符数组,字符数组内容为helloworld\0
	//定义一个指针用来保存数组首元素的地址
	char  * p = a;
	printf("%s\n",p);//%s打印一个字符串,要的是首个字符的地址
	printf("%s\n", p + 2);
	printf("%c\n",*(p+3));
	*p = 'm';

	printf("%s\n", p);
	//a++
	p++;
	*p = 'o';
	printf("%s\n", p);
	//printf("%d\n",sizeof(a));//11
	//printf("%d\n", sizeof(p));//4 p是指针变量,大小为4
	
	//printf("%d\n", strlen(a));//计算字符串的长度,从开始地址到\0结束的大小
	//printf("%d\n", strlen(p));//计算字符串的长度,从开始地址到\0结束的大小

字符串常量


	char a[] = "helloworld"; // 定义了一个字符数组,字符数组内容为helloworld

	// 定义一个指针用来保存数组首元素的
	char *p = a;
	p = "abcdef" // 字符串常量存在文字常量区,""在使用时,取的是字符串首元素地址
	// 文字常量区的内容是不可以改变的
	printf("%s\n", p);

	printf("%d\n",sizeof(p));//4
	printf("%d\n", sizeof("abcdef"));//7
	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen("abcdef"));//6
	
	*p = 'm';
    printf("%s\n",p)

字符指针作为形参

char * my_strcat(char * src, char *dst)
{
        int n = strlen(src);
        int i = 0;
        while (*(dst + i) !=  0)
        {
               *(src+n+i) =   *(dst + i);
               //src[n+i] = dst[i] ;
               i++;
        }
        *(src + n + i) = 0;
        return src;
}
int main()
{
        char  str1[128] = "hello";//hello123456\0
        char str2[128] = "123456";
        my_strcat(str1,str2);
        printf("%s\n",my_strcat(str1, str2));
        system("pause");
        return 0;

}

const修饰的指针变量

       //const修饰一个变量为只读
       const int a = 10;
       //a = 100; //err
       
       //指针变量, 指针指向的内存, 2个不同概念
       char buf[] = "aklgjdlsgjlkds";
       //从左往右看,跳过类型,看修饰哪个字符
       //如果是*, 说明指针指向的内存不能改变
       //如果是指针变量,说明指针的指向不能改变,指针的值不能修改
       const char *p = buf;
       // 等价于上面 char const *p1 = buf;
       //*p = '2'; //err 不能通过p修改那块空间的内容
       
       p = "agdlsjaglkdsajgl"; //ok
       char * const p2 = buf;
       p2[1] = '3';
       //p2 = "salkjgldsjaglk"; //err 不能修改k的指向
       //p3为只读,指向不能变,指向的内存也不能变
       const char * const p3 = buf;

字符指针数组*****

//字符指针数组
//是一个数组,每一个元素是字符指针
int main()
{
        /*char *p1 = "heihei";
        char *p2 = "haha";
        char *p3 = "xixi";*/
        //char *num[3] = { p1,p2,p3};
        char *num[3]={ "heihei" ,"haha","xixi"};
        //定义一个指针保存num数组首元素的地址   &num[0]  num
        char **p = num;
        for (int i = 0; i < 3; i++)
        {
        //      printf("%s\n",*(p+i));
               printf("%s\n", p[i]);
        }
        printf("%c\n",*(*(p+1)+3));// *(p[1]+3)   == p[1]3]
		// p+1找到第二个字符指针数组的第二个元素,
		// *(p+1)取地址内的内容0x2000
		// *(p+1)+3获得第三个字母的地址
		// *(*(p+1)+3)取第三个字母的地址里的内容,也就是得到那个字母
        
}

把这个搞懂,指针基本上就熟练了
![[Pasted image 20230812164346.png]]

字符指针数组作为main函数的形参

int  main(int  argc,char *argv[])

argc 是执行可执行程序是的参数个数
argv 是一个字符指针数组,保存的是参数(字符串)的首元素地址

//.*.exe    hello   123456
//char *argv[] = { ".*.exe",    "hello"   "123456" };
int main(int  argc,char *argv[])
{
	/*printf("%d\n",argc);
	printf("%s\n", argv[0]);
	printf("%s\n", argv[1]);
	printf("%s\n", argv[2]);
	printf("%s\n", argv[3]);*/
	for (int i = 0; i < argc; i++)
	{
		   printf("%s\n", argv[i]);
	}
}

字符串处理函数

char str1[128]
char str2[128]

  • strcpy(str1,str2);//将str2的字符拷贝至str1数组中,注意,str2遇到\0结束,会将\0拷贝至str1

  • strncpy(str1,str2,n);//将str2中的前n个字符拷贝至str1中,如果拷贝时不足n个,遇到\0拷贝结束

  • strcat(str1,str2)//将str2字符数组中的字符连接到str1字符数组中,遇到\0结束

  • strncat(str1,str2,n)//将str2字符数组中的n个字符连接到str1字符数组中,遇到\0结束

  • strncmp比较函数

	char  str1[] = "a\0bcdef";
	char str2[] = "a\0cdrrr";
	//str1数组中和str2数组拿出一个元素进行比较,相等继续往后比较
	//比较的是字符的ascii值
	//如果str1> str2  返回值等于1
	//如果str1==  str2  返回值等于0
	//如果str1 <str2  返回值等于 - 1
	//printf("%d\n",strcmp(str1,str2));
	printf("%d\n", strncmp(str1, str2,3));
  • sprintf: 组包函数
    int len = sprintf(buf,“格式”,“数据”);
    将数据安装格式组包,存放在数组buf中,sprintf函数的返回值是组完包的有效长度
int main()
{
	int  year = 2018;
	int  month = 10;
	int day = 20;
	char buf[1024] = "";
	//printf("year=%d month=%d day=%d \n",year,month,day);
	int len = sprintf(buf,"year=%d %cmonth=%d day=%d \n",year, 0,month, day);
	//将数据安装格式组包,存放在数组buf中,sprintf函数的返回值是组完包的有效长度
	//printf("%d\n",strlen(buf));
	printf("len=%d\n",len);
	printf("buf=[%s]",buf);
       
}
  • sscanf 拆包函数
    sscanf(buf,“格式”,数据);//将buf的内容格式化输出到数据
int main()
{

	//%d    0-9的字符
	int  year =0 ;
	int  month = 0;
	int day = 0;
	char  buf[1024] = "beijing:2018:t:10:20";
	//scanf("%d:%d:%d",&year,&month,&day);//从键盘按照相应的格获取数据
	sscanf(buf, "beijing:%d:t:%d:%d", &year, &month, &day);//从buf中按照`相应的格式获取数据
	printf("%d %d %d\n",year,month,day);
}
  • strchr(buf,ch)//在buf字符数组中查找字符ch出现的位置,如果成功返回此字符出现位置的地址,如果没有找到,返回NULL
  • strstr(str1,str2)//在str1字符数组中查找str2字符串出现的位置,并且返回这个位置的地址
  • strock,切割
	char str[] = "15080015225&bangquan#82263&123456";
	char *p[10] = {NULL};//初始化指针数组元素全部为NULL
	//strtok
	//char *p1 = strtok(str,"#");//在str1中将#切割,返回切割前面的字符串
	//printf("%s\n",p1);
	//char *p2 = strtok(NULL,"#");
	//printf("%s\n",p2);
	//char *p3 = strtok(NULL, "#");
	//printf("%s\n", p3);
	int i = 0;
	do {
	   if(i == 0)
			   p[i] = strtok(str, "#&");
	   else
			   p[i] = strtok(NULL, "#&");
	   //i++;
	} while ( p[i++] != NULL);//p[i] != NULL   i=i+1 如果strtok的返回值等于NULL,代表切割完毕
	i = 0;
	while (p[i] != NULL)
	{
	   printf("%s\n",p[i++]);
	}
  • atoi : 将字符串转整数
  • atof: 将字符串转float类型的数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值