完善指针(1)

理解指针

    1、指针的本质:地址
    2、指针的类型:type+*
    3、指针的解引用:解引用后所占空间大小由指针的type决定
    4、二级指针:对指针解引用就是其所指向的变量
    5、指针表达式:左值(代表变量的存储空间)和右值(内容)
    6、指针运算:
        指针+-整数(实质是加上其所指向变量类型的大小*整数);
        指针-指针(两个指针之间的元素个数(元素的类型由指针的type决定));
        指针的比较(后两个运算的前提是两个指针必须指向同一块内存)
    7、应用:
        利用指针将数组的元素翻转存储;
        利用指针数组排序(冒泡);
        模拟实现strlen的三种方式;
        模拟实现strcpy;
        模拟实现strcat;
        模拟实现strstr;
        模拟实现strcmp;
    8、两个练习:
         一个数组中只有两个数字是出现一次,其他所有数字都出现了两次, 找出这两个数字;
         喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以有多少瓶汽水。
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<Windows.h>

/*
指针的存在意义:
	指针让地址有地方存放,指针让内存的访问更加方便
指针是存放地址才出现的,地址是为了标示一块地址空间的
*/

//指针(类型)就是地址(指向:由地址找到)
//地址就是指针(可以通过地址找到变量)
//指针变量是变量
//变量指针是变量的地址(指针就是地址,地址就是指针)
void point1()
{
	int a = 10;
	int *p = &a;//存放在指针变量中的内容都被当做地址(指针)处理
	//同放在int变量中的内容都被当做int型处理
}

//指针的类型:type + *
//指针+-整数:实质是+-(所指类型(type)的大小*整数)
void point2()
{
	int n = 10;
	int *p = &n;
	char *cp = (char *)&n;
	printf("%p\n", &n);//n的整个地址
	printf("%p\n", p);//n的整个地址
	printf("%p\n", p + 1);//下一个n的整个地址
	printf("%p\n", cp);//n的最低一个字节(小端)地址
	printf("%p\n", cp + 1);//n的第二个字节地址
}

//指针解引用
//解引用后所占空间大小与其类型(type)大小相同
void point3()
{
	int n = 0x11223344;
	int *p = &n;
	char *cp = (char *)&n;
	*cp = 0x66;//n = 0x11223366
	*p = 0;//n = 0
}

//二级指针
void point4()
{
	int a = 10;
	int *p = &a;
	int **pp = &p;
	printf("%d\n", pp);
	printf("%d\n", pp + 1);//下一个(int*)指针的地址(+4)
	//int **pp:一个指向int *(type)类型的一级指针
	//所以,多级指针在32位平台下+1都是+4
	
	//对指针解引用就是其所指向的变量
	//对pp解引用就是p(*pp:p)
	//*pp = 20;//表示p = 20;(左值:代表存储空间)
	//int n = *pp;//表示n = p = 20;(右值:代表变量内容)
	printf("%d\n", *pp);
	printf("%d\n", *pp + 1);//表示p+1;即加上p所指向的a的类型大小(+4)
	printf("%d\n", (char*)*pp + 1);//表示p+1;此时p的类型为char:所以+1

	//对pp解引用是p;再对p解引用就是a(*(*pp):a)
	**pp = 30;//表示a = 30;
	int m = **pp;//表示m = a = 30;
	printf("%d\n", **pp);
	printf("%d\n", **pp + 1);//表示a+1;(int)就是+1
}

//指针表达式
void point5()
{
	//左值:代表存储空间,右值:代表变量内容
	char ch = 'a';
	char *cp = &ch;
	//&ch;//ch的地址;不能做左值;可以做右值(地址也是数据)
	//cp;//存放ch地址的指针变量;可做左值(变量就有开辟的空间);可做右值(cp中存放的是ch的地址)
	//&cp;//指针变量cp的地址;不可做左值;可做右值;
	//*cp + 1;//ch + 1 = 'a' + 1;是常量;不可做左值;可做右值;
	//*(cp + 1);//cp+1代表cp指向下一个ch空间,解引用后就代表下一个ch;可做左值;可做右值
	//++cp;//cp的内容自增;变量的++不能做左值;可做右值;
	//cp++;//cp的内容自增;变量的++不能做左值;可做右值;
	//*++cp;//先++,后*:代表下一个ch的空间;可做左值;可做右值;
	//*cp++;//先++,后*:同上;
	//++*cp;//代表++ch;变量的++不能做左值(对变量的自增是对变量内容的自增);可做右值
	//(*cp)++;//ch++;同上;
	//++*++cp;//++(ch+1):是对下一个ch的内容进行前置++;不能做左值;可做右值
	//++*cp++;//对ch内容的前置++,同时也进行后置++;不能做左值;可做右值
}

/*指针运算
指针-指针:代表两个指针之间的元素个数(元素的类型由指针的type决定)
指针与指针比较
:此上两种指针运算前提是两个指针必须指向同一块内存
*/
int point6(const char *s)
{
	//求字符串长度:指针-指针
	char *p = s;
	while (*p){
		p++;
	}
	return p - s;//p:字符串尾部;s:字符串头部
}
void point7()
{
	//清空数组:指针比较
	int str[5];
	int *p = str;
	while (p < &str[5]){
		*p++ = 0;
	}
	//标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较
	//但不允许与指向第一个元素前面的那个内存位置的指针进行比较
	//所以在使用时,建议从前向后访问
}

//利用指针将数组翻转存储
void pracPointer1(int arr[], int size)
{
	int *start = arr;
	int *end = arr + size - 1;
	while (start < end){
		*start ^= *end;
		*end ^= *start;
		*start ^= *end;
		start++;
		end--;
	}
}

//利用数组和指针两种方式进行排序
void pracPointer2(int arr[], int size)
{
	////数组形式:冒泡
	//for (int i = 0; i < size - 1; i++){//外层循环控制轮数
	//	int flag = 0;//优化
	//	for (int j = 0; j < size - 1 - i; j++){//内层循环控制每轮比较次数
	//		if (arr[j] > arr[j + 1]){
	//			flag = 1;
	//			arr[j] ^= arr[j + 1];
	//			arr[j + 1] ^= arr[j];
	//			arr[j] ^= arr[j + 1];
	//		}
	//	}
	//	if (!flag){
	//		break;
	//	}
	//}
	//指针形式:冒泡
	int *start = NULL;
	int *end = arr + size - 1;
	while (end > &arr[0]){//外层
		int flag = 0;//优化
		start = arr;
		for (; start < end; start++){//内层
			if (*start > *(start + 1)){
				flag = 1;
				*start ^= *(start + 1);
				*(start + 1) ^= *start;
				*start ^= *(start + 1);
			}
		}
		if (!flag){
			break;
		}
		end--;
	}
}

//模拟实现strlen的三种方式
//方式1:计数器方式
int my_strlen1(const char *str)
{
	int count = 0;
	while (*str){
		count++;
		str++;
	}
	return count;
}
//方式2:递归(不创建临时变量)
int my_strlen2(const char *str)
{
	if (!*str){
		return 0;
	}
	return 1 + my_strlen2(str + 1);
}
//方式3:指针-指针
int my_strlen3(const char *str)
{
	char *p = str;
	while (*p){
		p++;
	}
	return p - str;//尾-头
}

//模拟实现strcpy
char *my_strcpy(char *des, const char *src)
{
	assert(des);
	assert(src);//判断是否为空
	char *ret = des;
	while (*des = *src){//保证src的'\0'也被复制
		des++;
		src++;
	}
	return ret;//返回拷贝后的des
}

//模拟实现strcat
char *my_strcat(char *des, const char *src)
{
	assert(des);
	assert(src);
	char *ret = des;
	while (*des){//从des的'\0'开始复制
		des++;
	}
	while (*des = *src){
		des++;
		src++;
	}
	return ret;//返回拼接完成后的des
}

//模拟实现strstr(子串查找)
char *my_strstr(const char *des, const char *src)
{
	assert(des);
	assert(src);
	char *d = (char *)des;
	char *ds = NULL;
	char *s = (char *)src;
	while (*d){//如果在此字符串des已经判断到了'\0',则退出,未找到子串
		ds = d;
		s = src;
		while (*s && *ds && *ds == *s){
			ds++;
			s++;
		}
		if (!*s){//如果在此*s == 0,则子串已找到
			return d;
		}
		if (!*ds){//如果在此*ds == 0,则des已经找完,未找到子串
			return NULL;
		}
		//走到这说明while循环是因为*ds!=*s跳出的
		d++;
	}
}

//模拟实现strcmp
int *my_strcmp(const char *src, const char *des)
{
	assert(des);
	assert(src);
	int ret = 0;
	while (!(ret = *(unsigned char *)src - *(unsigned char *)des) && *des){//将较大几率不成立的条件放置前面判断
		//此处判断条件,没有判断*src为空:(原因)当判断条件1成立时说明src和des相等,此时会去判断条件2
		//当条件2成立,则退出,此时代表ret == 0(也可以说明此时src == 0;(src-des == 0))
		src++;
		des++;
	}
	if (ret < 0){
		ret = -1;
	}
	else if (ret > 0){
		ret = 1;
	}
	else{
		ret = 0;
	}
	return ret;
}

//一个数组中只有两个数字是出现一次,其他所有数字都出现了两次, 找出这两个数字
void practice1(int arr[], int size, int *num1, int *num2)
{
	int i = 0;
	int ret = 0;
	int temp = 0;
	int pos = 0;
	while (i < size){
		ret ^= arr[i];
		i++;
	}//最终的ret的值为两个只出现一次的异或值
	temp = ret;
	while (ret){
		if (ret % 2){//1
			break;
		}
		pos++;
		ret >>= 1;//右移一位
	}//最终的pos位为1:找出的pos位是num1和num2不同的位
	//用num1来存放该位为1的数据,num2来存放该位为0的数据
	for (i = 0; i < size; i++){
		if ((arr[i] >> pos) & 1){
			*num1 ^= arr[i];
		}//取出数组中该位为1的数据(根据此位将数组分为两组:即该位为1(包含num1)和该位为0(包含num2)两组)
		//现在该问题就转化为求一个数组中仅有一个数据出现一次,其他数据都出现两次的情况。
	}
	*num2 = temp^*num1;//temp = *num1^*num2
}

//喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以有多少汽水
int practice2(int money)
{
	int n = money;
	int count = n;
	while (n > 1){//当拥有的瓶数大于等于2,才可以继续换
		count += n / 2;
		n = n / 2 + n % 2;
	}
	return count;
}

int main()
{
	//point2();
	//point3();
	//point4();
	/*char *str = "asassad";
	point6(str);*/

	//int arr[10] = { 2, 3, 5, 1, 3, 2, 6, 7, 4, 8 };
	//int size = sizeof(arr) / sizeof(arr[0]);
	////pracPointer1(arr, size);
	//pracPointer2(arr, size);

	/*char *str = "anxsdfj12";
	printf("%d\n", my_strlen1(str));
	printf("%d\n", my_strlen2(str));
	printf("%d\n", my_strlen3(str));*/

	/*char *src = "qweasdz765";
	char des[20];
	printf("%s\n", my_strcpy(des, src));*/

	/*char *src = "hjklg123";
	char des[20] = "asdf";
	printf("%s\n", my_strcat(des, src));*/

	/*char *src = "asdf";
	char *des = "qwasuitasdfcxnz";
	printf("%s\n", my_strstr(des, src));*/

	/*char *src = "abcdefg";
	char *des = "abcdekg";
	printf("%d\n", my_strcmp(src, des));*/

	int num1 = 0;
	int num2 = 0;
	int arr[12] = { 2, 6, 8, 15, 3, 5, 6, 8, 4, 2, 3, 5 };
	int size = sizeof(arr) / sizeof(arr[0]);
	practice1(arr, size, &num1, &num2);
	printf("%d %d\n", num1, num2);

	/*int money = 3;
	printf("%d\n", practice2(money));*/

	system("pause");
	return 0;
}


猜你喜欢

转载自blog.csdn.net/tec_1535/article/details/80291835