C语言小白养成计划七:指针(中)

🎁毫无疑问,指针是c语言中一座大山,🎁
😄但是咋们只要迎难而上,坚持努力,😄

🔥迟早会将这大山踩在脚下。🔥

一.指针(中)

1.1字符串与指针

c语言里面是没有字符串这么一个类型的,c语言是通过一个叫 char *的字符指针取实现这个字符串的,字符串:一坨的字符串在一起就是字符串,c语言里面字符串是以 \0这个字符结尾的,你的字符串不管有多长,碰到第一个 \0 就结束了,c语言里面用"“来表示一个字符串,”"引起来的一些字符的末尾默认带一个 \0,“abcdefg” :实际上 “abcdefg\0”,“abcde\0fg”:这个字符串实际上的处理是以 "abcde"来算的
在c语言里面 “abcdefg”:c语言处理这个字符串的行为:,在.rodata这个空间开辟 8个字节 abcdefg\0存储这个字符串,然后将存放a的内存的地址返回回来,typeof(“abcdefg”) -> const char *,因此我可以直接用一个字符指针取接受这个字符串的地址
const char * p = “abcdefg”;
char c = *p;//‘a’
char d = p[1] -> *(p + 1);‘b’
p[2] = ‘f’;//不行 错误的 error的 p是一个const只读的 因此不能写

		c语言里面对const这个玩意儿审核不是很严
		这就意味着我可以用一个char * 去保存这个地址
		char * q = "abcdefg";
		char c = *q;
		q[1] = 'k';//不行 错误的 error的 q指向的内存空间是只读的	
		char d = 'L';
		q = &d;//可以的  q本身自己的内存是可写的 因此没有毛病
		*q = 'P';//可以  q这个时候指向了d这个可读可写的内存空间
		q[1] = 'M';//不可以 q[1] -> *(q + 1) ->往后面走了一个字节
					//对于我们这些代码来说你是不知道这个内存是否是能用的
					//因此会内存非法访问,不行
	
		char str[] = "abcdefg";//可以
		char c = *str;//可以  'a'
		str[1] = 'K';//完全没有问题
		sizeof(str) == 8
		
		
		
		char str[] = "abcdefg";//这个代码不是接收这个字符串的首地址
							//利用这个字符串里面的字符去初始化这个数组
							
	字符串的长度:字符串的长度为第一个字符到第一个\0中间的所有的字符串的数量
				不算\0,注意:必须找到第一个\0 不然这个字符串就没有完
				
		char str[7] = "abcdefg";
		sizeof(str) == 7
		str代表的这个字符串的长度为多少?
		strlen(str) == 根据这个代码,算出来应该是7
					但是根据我们的内存的访问来说,它会造成内存的非法访问
					这个str里面没有 \0   所以不允许
					用数组去保存这个字符串一定要注意:你的内存要开的足够大
					利用内存的原则能节约就节约
					不要忘记最后的\0

strlen这个函数是求字符串的长度,不包含这个 \0
请将strlen这个函数完成

int mystrlen(const char * str)
		{
			int size = 0;//字符串的长度
			
			while(*str)//一个个数,数到第一个 \0
			{
				size++;
				str++;
			}
			
			
			return size;
		}

%s ->输入输出字符串
char buf[128] = {0};
scanf(“%s”,buf);
printf(“%s\n”,buf);

字符串之间不能比较大小,不能直接赋值,不能直接判断相等…

char * p = "abcdefg";
char * q = "abcdef";
if(q == p)//可以 但是这不是比较两个字符串  是看q和p这两个地址是否相等
{

}

q = p;//可以   将p里面保存的一个地址赋值给q

char str[64] = "abcdefg";
//str = q;//不可以 str本身是只读的,不能被赋值
//q = str;//可以  将str代表的地址赋值给q

我现在希望将q代表这个字符串里面的所有的字符弄到str里面去
请问应该怎么实现??
只能一个个的字符去赋值

请完成mystrcpy这个函数
//成功返回0 失败返回-1

	int mystrcpy(char * to,const char * from)
	{
		if(!to || !from)//他们两个不能有一个空指针
			return -1;
		//请完成这个copy操作 注意  \0也要复制	
		while(*from)//到\0退出
		{
			*to = *from;
			to++;
			from++;
		}
		//最后的那个\0还没有复制过去
		*to = 0;//*to = '\0';	
		return 0;
	}
判断两个字符串是否完全一样?
	到第一个碰到\0的为止,最后的\0也要判断
		"abcdefg" 
		"abcde"这两个字符串不同
		
		"abcde\0fg" 
		"abcde"
		
如果完全一样返回0
不完全一样返回非0
	mystrcmp函数
	int mystrcmp(const char * str1,const char * str2)
	{
		if(!str1 || !str2)//他们两个不能有一个空指针
			return -1;
		while(*str1 == *str2)//这里是没有判断两个人是否等于 \0
		{
			//碰到第一个\0  这个循环都没有退出
			if(*str1 == 0)//这个时候 str2也是\0
			{
				return 0;
			}
			str1++;
			str2++;		
		}
		//只要退出这个循环,那么他们就不相同了
		return *str1 - *str2;
	}

	完成字符串连接函数
	mystrcat
	int mystrcat(char * str1,const char *str2)
	{
		//此函数的功能为 将str2连接到str1的后面去
		if(!str1 || !str2)//他们两个不能有一个空指针
			return -1;
		
		str1 += mystrlen(str1);//让str1指向它后面的那个\0
		//然后将str2复制到str1那里去就可以了
		mystrcpy(str1,str2);
			
		return 0;
			
			
	}

c语言是不会做边界检查的,你越界了它也会继续做,直到操作系统将你干掉这种情况一般都会报段错误。

1.2数组指针与指针数组

1.2.1数组指针:表示是指向一个数组的指针

我需要用一个指针指向一个数组,那么我们应该要怎么办
int a[5] = {1,2,3,4,5};
//指向这个数组跟指向数组的第一个元素是有很大的区别的
//指向数组的第一个元素,这个指针++只会往后面挪动一个元素
//而指向这个数组的指针++,它是跳过整个数组
我们要定义一个指针指向这个a数组
typeof(&a) p = &a; -> typeof(a) * p; int[5] *p = &a;

	int[5] *p = &a;//祖师爷说这么写很不爽  [5]要到后面去  不能这么写
					
	
	#define hehe int * 
	
	int *p[5]; ->hehe p[5];->它是以hehe类型定义出来5个元素的一维数组
	-> 还原 int * p[5] ->它是以int *类型定义出来5个元素的一维数组
	因此指向数组的这个指针不能是一个一维数组
	因此 *不能跟int走 它要跟 p走
	因此指向这个数组的指针的定义:
	int (*p)[5] = &a;//这个玩意儿我们就叫数组指针
		->我们在使用的时候 (*p)[1] -> a[1]
	上面的这个玩意儿是不是就是一个二维数组的指针
	int b[3][4];
	typeof(b) p = b;
	int[4] * p = b;
	int (*p)[4] = b;
	在使用的时候p就直接可以像b一样使用
	(*p)[1] -> (*(p + 0))[1] ->p[0][1]
#include <stdio.h>


//传二维数组
void func(int (*p)[4],int n)
{
	printf("%d\n",p[2][2]);	
}


//更多的时候我们可能都是按照一维数组弄的
void func1(int *p,int n)//我们为什么要传过来这个n
						//这个函数里面是不知道这个数组有多大的
						//我需要你告诉我  有多大 因此我要把元素个数传进来
{
	for(int i = 0;i < n;i++)
		printf("%d\t",p[i]);
	printf("\n");
}


int main()
{
	int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
	
	func(a,3);
	
	func1((int *)a,sizeof(a) / sizeof(a[0][0]));
	
	return 0;
}

int b;
我想定义一个指针指向b
typeof(&b) q = &b;//typeof(b) * q = &b; -> int * q = &b;

1.2.2指针数组:这个数组里面的元素都是一个指针

	int * a[4];//定义了a这个4个元素的数组
			//里面所有的元素都是一个int *
			//a[0]保存了一个地址......
	int b[3][4];
	//我要定义一个指针指向这个数组
	int (*p)[4] = b;//数组指针
	我用一个指针数组将这个二维数组里面的元素全部指向
	我现在可以使用a这个数组将b里面的元素全部访问到,请问我的a应该怎么处理,
	我只需要这个a数组将b的每一行首地址都保存下来就可以了
	a[0] = b[0];
	a[1] = b[1];
	a[2] = b[2];
	//a = b;//明显不行 a是一个数组的名字,不能改
	现在我想通过a访问b[1][2]
	a[1][2]
	*(a[1] + 2)
	*(*(a + 1) + 2)

int main()
{
char * p[] = {
“hehe”,//.rodata
“sb250”,
“123456”,
“penglei”,
“zhangfei”
};

sizeof(p) = (4 * 5)或者为(8 * 5);
//typeof("hehe"); ->char *

}

练习:
我需要找到一个特定的名字,如果有返回它的下标,没有返回没有找到

这个特定的名字用 %s进行输入
scanf(“%s”,buf);//这个buf需要开的足够大
找到固定什么字符串开头的那个下标,将找到的这个字符串用一个内存重新保存

#include <stdio.h>
#include <string.h>


//失败返回-1  成功返回找到的下标
int zhaoquanming(char *p[],int n,char * who)
{
	int i;
	for(i = 0;i < n;i++)
	{
		//比较两个字符串是否相同
		if(!strcmp(p[i],who))
		{
			//找到了  将它的下标返回
			return i;
		}
		
	}
	return -1;
}

int zhaokaitou(char *p[],int n,char * who)
{
	//找到了返回下标
	//没有找到 打印没有,然后返回-1
	//我只需要找到前面的几个字节
	int count = strlen(who);//求得字符串的长度
	int i;
	for(i = 0;i < n;i++)
	{
		//比较两个字符串前面count个字节是否相同
		if(!strncmp(p[i],who,count))
		{
			//找到了  将它的下标返回
			return i;
		}
		
	}
	return -1;
	
	
}

int main()
{
	char * p[] = {
		"hehe",//.rodata
		"sb250",
		"123456",
		"penglei",
		"zhangfei"
	};
	printf("找全名 %s :下标为 %d\n","penglei",zhaoquanming(p,sizeof(p) / sizeof(p[0]),"penglei"));
	
	int n = zhaokaitou(p,sizeof(p) / sizeof(p[0]),"zhang");
	printf("找开头 %s :下标为 %d\n","zhang",n);
	char buf[128] = {0};//将找到的这个字符串弄到buf里面去   将buf打印出来
	
	//通过下标找到这个字符串 将n下标代表的字符串弄到buf里面去
	strncpy(buf,p[n],127);//最后的一个字节我要让它一定是 \0
	
	printf("buf = %s\n",buf);
	char name[128] = "你好"; 
	//将上面的buf 接到name的“前面”去
	//buf是为了保存我们有用的那个字符串  因此里面的内容这里应该不要动
	
	char cpbuf[128] = {0};
	
	// 1 strcpy(cpbuf,buf)    2 strcat(cpbuf,name);   3 strcpy(name,cpbuf)
	snprintf(cpbuf,127,"%s%s",buf,name);
	strcpy(name,cpbuf);
	
	printf("name = %s\n",name);
	
	
}

这两个函数用法跟printf一样 都是格式化的输出
int sprintf(char *str, const char *format, …);
int snprintf(char *str, size_t size, const char *format, …);
printf:固定往终端上面输出
sprintf:往str内存里面输出
str可能会内存越界
因此snprintf这个函数就是为了解决上面bug的
最多输出 size个字节

char *
char buf[]
看到这么一些玩意儿之后你就要想到用字符串处理函数***********

练习:
我们的字符串有这些形式,“123”,“345”…,这种我们叫数字字符串,这些数字字符串是不能直接使用 + - …直接去操作的

“123” + “345” = 468;
“123” -> 转变成123这个数字

int atoi(char * str)
{
//数字0 和 字符0
0 == '0' - '0';
1 == '1' - '0';

int sum = 0;
while(*str)
{
	sum = sum * 10 + *str - '0';
	str++;
}

return sum;

}

int main()
{
int sum = atio("123") + atio("345") -> sum的结果为468
printf("%d\n",sum);


}

NAME
atoi, atol, atoll - convert a string to an integer
将一个数字字符串转换成一个整数
SYNOPSIS
#include <stdlib.h>

int atoi(const char *nptr);
nptr:你要转换的字符串
返回值:你转换好了的字符串通过这个返回值返回给你

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值