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