学习C语言第七天

2020/7/12

多级指针

\int a = 10;
int *p = &a;   	// 一级指针,是 int 变量的地址。
int **pp = &p;    // 二级指针,是 一级指针的地址。
int ***ppp = &pp;	// 三级指针,是 二级指针的地址。
int ****pppp = &ppp;	// 四级指针,是 三级指针的地址。
......
  • 多级指针,不能跳跃定义。必须有一级,才有二级,二级才有三级
ppp == &pp;		// 三级指针
*ppp == pp == &p;		// 二级指针
**ppp == *pp == p == &a  	// 一级指针
***ppp == **pp == *p == a   	// 普通整型变量 

指针和函数

栈帧

栈帧:

  • 当函数被调用时,系统会在 stack(栈)空间上,申请一块内存区域,来供函数调用。主要存放 形参 和 局部变量。
  • 当函数调用结束时,这块“栈帧”会被自动释放 (消失)。
    在这里插入图片描述

传值和传址

  • 传值:在函数调用期间,实参将自己的数据值拷贝一份,给形参。
    在这里插入图片描述
    传址:在函数调用期间,实参将自己的地址值拷贝一份,给形参。

  • 核心思想:在 A栈帧中,借助地址,修改B栈帧空间中的内容。
    在这里插入图片描述

数组做函数参数

  • 数组做函数参数时,传递的不再是整个数组,而是数组的首地址(指针),sizeof 的值要么4,要么8.
    在这里插入图片描述
  • 当整型数组做函数参数时,通常在函数定义中,封装两个参数。一个表示数组首地址,另外一个表示数组的元素个数。
  • 在这里插入图片描述

指针做函数返回值

  • 指针做函数的返回值,不能 返回局部变量(定义在函数内的变量)的地址值
    • 原因:函数调用结束,栈帧释放,局部变量的地址无效。
--- 以下是测试上述 结论。

// 定义函数,返回指针
int* test_ret(int a)
{
	printf("a = %d\n", a);

	int b = 1234;	// 定义局部变量b

	return &b;  // 根据函数定义,返回 int *
}

void test_use_stack()
{
	int arr[1000] = { 1,3,5,6 };
}

int main(void)
{
	int* ret = NULL;  // 定义一个空指针
	// 调用函数
	ret = test_ret(100);

	// 以下3次调用,加大系统重新分配内存给其他程序,覆盖 &b 的概率。
	test_use_stack();
	test_use_stack();
	test_use_stack();

	printf("ret = %d\n", *ret); // 将接收的地址,做解引用。取b的值。

	system("pause");
	return EXIT_SUCCESS;
}
  • 可以返回局部变量的 数据值

  • C语言中,数组不允许做函数返回值。只能写成指针形态。在这里插入图片描述

指针和字符串

基本知识

char str1[] = {'h','i','\0'};   // 变量,可读可写
char str2[] = "hi";		// 变量,可读可写。
str1[0] = 'R';  // 因为是变量,可以修改。

char *str3 = "hi";		// "hi"为常量,只读。

str3[0] = 'R';   // 因为是常量,不可以修改。(str3[0]==*(str3+0))

char *str4 = {'h','i','\0'};   // 错误定义。语法不允许!!!

总结:

  • char[] 保存变量地址,同一个字符串,地址值不同。

  • char * 保存常量地址,同一个字符串,地址值相同。

  • 当 字符串,做函数参数时,不需要两个参数。因为每个字符串都有 \0。

练习

字符串比较 strcmp()

  • 比较 str1 和 str2, 如果相同返回0, 不同则依次比较ASCII码,str1 > str2 返回1,否则返回 -1\

  • 不比较ASCII码的和 ( helloworld < helloz —> 返回-1)

  • 思路分析

    在循环中,依次取出对应位的字符,进行比较,直至\0, 都相同 —> 0
    如果对应位,有不同,比较 ASCII, 参1 > 参2 --> 1 , 参1 < 参2 --> -1

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

int mystrcmp(char* str1, char* str2);
int mystrcmp2(char* str1, char* str2);

int main(void)
{
	char* str1 = "hellof";
	char* str2 = "hellof";

	// 调用函数
	int ret = mystrcmp2(str1, str2);

	if (ret == 0)
		printf("str1 == str2\n");
	else if (ret == 1)
		printf("str1 > str2\n");
	else if (ret == -1)
		printf("str1 < str2\n");
	else
		printf("异常\n");

	system("pause");
	return EXIT_SUCCESS;
}
//指针实现
int mystrcmp(char* str1, char* str2)
{
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	return *str1 > * str2 ? 1 : -1;
}
//数组实现
int mystrcmp2(char* str1, char* str2)
{
	int i = 0;
	while (str1[i] == str2[i])
	{
		if (str1[i] == '\0')
		{
			return 0;
		}
		i++;
	}
	return str1[i] > str2[i] ? 1 : -1;
}

字符串拷贝 strcpy

  • 思路分析
    在这里插入图片描述
  • 代码实现
// 定义函数,实现 字符串拷贝 --- 数组实现
void mystrcpy(const char* src, char* dst)  //src:源; dst:目标
{
	int i = 0;
	while (src[i] != '\0')
	{
		dst[i] = src[i];
		i++;
	}
	dst[i] = '\0';  // main中初始化了 dst 为 0,可以省略此步。
}

// 定义函数,实现 字符串拷贝 --- 指针实现
void mystrcpy2(const char* src, char* dst)
{
	while (*src)   // !='\0' 可以省略
	{
		*dst = *src;
		src++;
		dst++;
	}
	*dst = '\0';
}

int main(void)
{
	char* src = "helloworld";  // soruce 缩写 src
	char dst[100] = { 0 };	// 定义足够大空间  dest 缩写 dst

	mystrcpy2(src, dst);

	printf("dst = %s\n", dst);

	system("pause");
	return EXIT_SUCCESS;
}

在字符串中查找字符出现的位置

  • 说明:实现strchr()函数。
"helloworld"
    判断 'e' 在 helloworld 中的位置。  ——>  "elloworld"
    判断 'l' 在 helloworld 中的位置。  ——>  "lloworld"
    判断 'r' 在 helloworld 中的位置。  ——>  "rld"
  • 代码实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

char* strchr1(char* str, char chr);
char* strchr2(char* str, char ch);

int main(void)
{
	char str[] = "hello world";
	char ch = 'l';
	char* ret = NULL;
	ret = strchr2(str, ch);
	if (ret == NULL)
		printf("字符串%s中,不包含字符%c\n", str, ch);
	else
		printf("ret = %s\n", ret);
	system("pause");
	return EXIT_SUCCESS;
}

char* strchr1(char* str, char chr)
{
	while(*str)
	{
		if (*str == chr)
		{
			return str;
		}
		str ++;
	}
	return NULL;
}

char* strchr2(char* str, char ch)
{
	int i = 0;
	while (str[i])
	{
		if (str[i] == ch)
		{
			return &str[i];   // 返回对应字符的地址。
		}
		i++;
	}
	return NULL;  // 没有在字符串中,找到对应字符。
}

字符串去空格

在这里插入图片描述

// 定义函数,实现字符串去空格 —— 数组版
void str_no_space(char* str, char* dst)
{
	// 定义循环因子
	int i = 0;	// 遍历字符串 str
	int	j = 0;	// 记录dst存储字符位置
	while (str[i] != 0)
	{
		if (str[i] != ' ')  // 取非空格字符,写入det
		{
			dst[j] = str[i];
			j++;    // 没取到空格,j后移
		}
		i++;  // 依次遍历下一个元素。
	}
	dst[j] = '\0';
}

// 定义函数,实现字符串去空格 —— 指针版
void str_no_space2(char* str, char* dst)
{
	while (*str)
	{
		if (*str != ' ')  // 取非空格字符,写入det
		{
			*dst = *str;
			dst++;    // 没取到空格,dst数组存储位置后移
		}
		str++;
	}
	*dst = '\0';
}

int main(void)
{
	char* str = "ni chou sha ? chou ni za di!jfkl fdl jdsk kd fks";
	char dst[100] = { 0 };

	// 调用函数
	str_no_space2(str, dst);

	printf("dst = %s\n", dst);

	system("pause");
	return EXIT_SUCCESS;
}

带参数的 main 函数

  • 无参版 main函数
int main(void) 
{
    return 0;
}
  • 有参版main函数
int main(int argc, char *argv[])
{
    return 0;
}1:表示给main函数,传递的参数的个数。
参2:是一个字符指针(字符串)数组。数组的每一个元素都是字符串。
// 测试使用 有参main函数。
  • 测试:gcc 编译、查看
    在这里插入图片描述
  • VS中,使用带有参数的main。
  • 项目名——右键——属性
    在这里插入图片描述
    在这里插入图片描述

编程练习

1.str 中 substr 出现的次数

  • 使用库函数strstr()
char *strstr(char *str, char *substr)1:原串
    参2:子串
    返回: 子串在串中的地址值。   
"helloworld"(原串)
    判断 "llo"(子串)在 helloworld 中的位置。  ——>  "lloworld"
// 测试使用  strstr函数
int main(void)
{
	char* ret = strstr("hellollollolloworld", "llo");
	printf("ret = %s\n", ret);

	return 0;
}    
运行结果:ret = llollollolloworld
  • 分析实现
    在这里插入图片描述
// 定义函数,统计原串中包含多少个子串
int substr_times(char* str, char* substr)
{
	int counter = 0;
	char* p = strstr(str, substr);  // 先判断一次, 原串中是否有子串。

	while (p != NULL)
	{
		counter++;   // 原串中,有子串。

		p += strlen(substr);	// 不能使用sizeof(substr)

		p = strstr(p, substr);	// 判断,原串中,是否有子串。 
	}

	return counter;
}

int main(void)
{
	char* str = "helloabclloxyzllolUlollollo";  // 原串
	char* substr = "llo";   // 子串

	int ret = substr_times(str, substr);

	printf("在原串%s中,子串%s出现了%d次\n", str, substr, ret);

	system("pause");
	return EXIT_SUCCESS;
}

2.求字符串非空格元素个数

int counter_no_space(const char* str)
{
	int count = 0;
	char* p = str;		// 使用指针方法实现
	while (*p)
	{
		if (*p != ' ')
			count++;
		p++;
	}
	return count;
}

int main(void)
{
	char* str = " sha ?";

	// 调用,返回结果
	int ret = counter_no_space(str);

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

	system("pause");
	return EXIT_SUCCESS;
}

3.字符串逆置

// 封装函数,实现字符串逆置 --- 指针法。
void str_inserve(char* str)
{
	char* start = str;			// 记录首字符地址。
	char* end = str + strlen(str) - 1;	// 记录最后一个有效字符地址。

	char temp = '\0';  //用来实现3杯水交换

	while (start < end)
	{
		temp = *start;	// 三杯水交换
		*start = *end;
		*end = temp;
		start++;		// 首元素指针后移
		end--;			// 尾元素指针前移
	}
}

int main(void)
{
	// 计划直接在 原串上修改,所以,不能使用 char *str = "xxx";
	char str[] = "this is a test";  
	str_inserve(str);

	printf("str=%s\n", str);

	system("pause");
	return EXIT_SUCCESS;
}

4.判断字符串是回文

// 定义函数判断:1:真、0:假
int str_is_abcba(const char* str)
{
	char* start = str;
	char* end = str + strlen(str) - 1;

	while (start < end)
	{
		// 找对应位置不一致的字符,结束循环。
		if (*start != *end)
		{
			return 0;  // 不是回文。
		}
		start++;
		end--;
	}
	return 1;
}

int main(void)
{
	char *str = "amkillikma";

	// 调用,测试
	int ret = str_is_abcba(str);

	if (ret == 0)
		printf("不是回文字符串\n");
	else
		printf("是回文字符串\n");

	system("pause");
	return EXIT_SUCCESS;
}c
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值