1.指针的使⽤和传址调⽤
1.strlen的模拟实现
库函数strlen的功能是求字符串⻓度,统计的是字符串中\0 之前的字符的个数。函数原型如下:
参数str接收⼀个字符串的起始地址,然后开始统计字符串中 \0 之前的字符个数,最终返回⻓度。如果要模拟实现只要从起始地址开始向后逐个字符的遍历,只要不是\0 字符,计数器就+1,这样直到 \0 就停⽌。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int my_strlen(const char* str)
{int count = 0;while (*str != '\0'){count++;str++;}return count;
}
int main()
{char arr[] = "abcdrf";int len = my_strlen(NULL);printf("%d\n", len);return 0;
}
2.传值调⽤和传址调⽤
学习指针的⽬的是使⽤指针解决问题,那什么问题,⾮指针不可呢?
例如:写⼀个函数,交换两个整型变量的值
我们可以发现,此时并没有完成交换。
为什么呢?
进行调试观察:
x86:
继续调试
由此可见:
当实参传递给形参的时候,形参是实参的一份临时拷贝
对形参的修改不会影响实参的
我们发现在main函数内部,创建了a和b,a的地址是0x00cffdd0,b的地址是0x00cffdc4,在调⽤ Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,但是 x的地址是0x00cffcec,y的地址是0x00cffcf0,x和y确实接收到了a和b的值,不过x的地址和a的地址不 ⼀样,y的地址和b的地址不⼀样,相当于x和y是独⽴的空间,那么在Swap1函数内部交换x和y的值, ⾃然不会影响a和b,当Swap1函数调⽤结束后回到main函数,a和b的没法交换。Swap1函数在使⽤ 的时候,是把变量本⾝直接传递给了函数,这种调⽤函数的⽅式我们之前在函数的时候就知道了,这 种叫传值调⽤。
那怎么办呢?
我们现在要解决的就是当调⽤Swap函数的时候,Swap函数内部操作的就是main函数中的a和b,直接 将a和b的值交换了。那么就可以使⽤指针了,在main函数中将a和b的地址传递给Swap函数,Swap 函数⾥边通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。
对代码进行如下改写:
就可以啦!
我们可以看到实现成Swap2的⽅式,顺利完成了任务,这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数,这种函数调⽤⽅式叫:传址调⽤。
传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所 以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改 主调函数中的变量的值,就需要传址调⽤。
2.数组名的理解
之前在使⽤指针访问数组的内容时,有这样的代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
这⾥我们使⽤ &arr[0] 的⽅式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址,⽽且 是数组⾸元素的地址,我们来做个测试。
发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样,数组名就是数组⾸元素(第⼀个元素)的地 址。
那么下面一段代码怎么理解呢?
x86:
输出的结果是:40,如果arr是数组⾸元素的地址,那输出应该的应该是4/8才对。
其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:
• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节
• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的) 除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。
数组名就是首元素的地址,类型是int*,+1就是跳过一个整型(4个)
这⾥我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,是因为&arr[0]和arr都是 ⾸元素的地址,+1就是跳过⼀个元素。
但是&arr和&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的。
3.使⽤指针访问数组
有了前⾯知识的⽀持,再结合数组的特点,我们就可以很⽅便的使⽤指针访问数组了。
前提需要掌握以下两个要点:
1.数组在内存中是连续存在的
2.指针的+-整数运算,方便我们获得每一个元素的地址
如果我们再分析⼀下,数组名arr是数组⾸元素的地址,可以赋值 给p,其实数组名arr和p在这⾥是等价的。那我们可以使⽤arr[i]可以访问数组的元素,那p[i]是否也可 以访问数组呢?
1.数组就是数组,是一块连续的空间(数组的大小和数组元素个数和元素类型都有关系)
2.指针(变量)就是指针(变量),是一个变量(4/8个字节)
3.数组名是地址,是首元素的地址
4.可以使用指针来访问数组
arr[i] ====> *(arr+i)
[]是下标引用操作符
所以:
arr[i] ====> *(arr+i) ====>*(i+arr) ====>i[arr]
arr[i] == i[arr]
| |
*(arr+i) *(i+arr)
相当于:2+1 ==== 1+2
这种方法是可行的在运行代码的时候不会报错,但是,不推荐这种方法!
4.⼀维数组传参的本质
数组是可以传递给函数的,这个⼩节我们讨论⼀下数组传参的本质。 ⾸先从⼀个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给⼀个函 数后,函数内部求数组的元素个数吗?
我们对代码进行以下修改:
发现输出只有1,之后对这段代码进行调试:
发现循环只循环了一次:
解析如下:
1.数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组的。
2.形参的数组是不会单独再创建数组空间的,所以形参的数组是可以省略掉数组大小的。
修改如下:
所以:
⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
5.冒泡排序
排序:
很多数据,要排序
排序的算法有很多:
冒泡
插入
选择
快速
希尔
堆排序
……
冒泡排序的核⼼思想就是:两两相邻的元素进⾏⽐较。
10个数字9趟
n个数字n-1趟
如果数组为:9,0,1,2,3,4,5,6,7,8 进行一趟就有序了,怎么样才可以提高代码运行效率呢?
如下所示:对这一块的代码进行改写
本文中所涉及到的代码如下所示(运行时,取消“//”注释即可):
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//int my_strlen(const char* str)
//{
// int count = 0;
// while (*str != '\0')
// {
// count++;
// str++;
// }
// return count;
//}
//int main()
//{
// char arr[] = "abcdrf";
// int len = my_strlen(NULL);
// printf("%d\n", len);
// return 0;
//}//写⼀个函数,交换两个整型变量的值
//void Swapl(int x, int y)
//{
// int tmp = x;
// x = y;
// y = tmp;
//}
//int main()
//{
// int a = 10;
// int b = 20;
// printf("交换前 a= %d b= %d\n", a, b);
// Swapl(a,b);
// printf("交换后 a= %d b= %d\n", a, b);
// return 0;
//}//void Swap2(int* pa, int* pb)
//{
// int tmp = 0;
// tmp = *pa;//tmp = a
// *pa = *pb;//a = b
// *pb = tmp;//b = tmp
//}
//int main()
//{
// int a = 10;
// int b = 20;
// printf("交换前 a= %d b= %d\n", a, b);
// Swap2(&a, &b);
// printf("交换后 a= %d b= %d\n", a, b);
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// int* p = &arr[0];
// int* p = arr;//数组名是数组首元素的地址
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// printf(" arr = %p\n", arr);
// printf("&arr[0] = %p\n", &arr[0]);
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// printf("%zd\n", sizeof(arr));
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// printf(" arr = %p\n", arr);
// printf(" arr+1 = %p\n", arr+1);
// printf("&arr[0] = %p\n", &arr[0]);
// printf("&arr[0]+1= %p\n", &arr[0]+1);
// printf(" &arr = %p\n", &arr);
// printf(" &arr+1 = %p\n", &arr + 1);
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// //使用指针来访问数组
// int sz = sizeof(arr) / sizeof(arr[0]);
// //输入10个值
// int* p = arr;
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// //输入一个值
// scanf("%d", p+i);//&arr[i]
// }
// //输出10个值
// for (i = 0; i < sz; i++)
// {
// printf("%d ", *(p + i));
// }
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// //使用指针来访问数组
// int sz = sizeof(arr) / sizeof(arr[0]);
// //输入10个值
// int* p = arr;
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// //输入一个值
// scanf("%d", p + i);//&arr[i]
// }
// //输出10个值
// for (i = 0; i < sz; i++)
// {
// printf("%d ", p[i]);//*(p+i) //arr[i] //*(arr+i)
// }
// return 0;
//}//void Print(int arr[10],int sz)
//{
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// printf("%d ", arr[i]);
// }
//}
//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// int sz = sizeof(arr) / sizeof(arr[0]);
// Print(arr, sz);
// return 0;
//}//void Print(int arr[10])
//{
// int sz = sizeof(arr) / sizeof(arr[0]);
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// printf("%d ", arr[i]);
// }
//}
//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// Print(arr);
// return 0;
//}数组传参的时候,形参是可以写成数组的形式的
但是本质上还是指针变量
//void Print(int arr[10])//int* arr
//{
// int sz = sizeof(arr) / sizeof(arr[0]);
// //得不到元素个数的
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// printf("%d ", arr[i]);
// }
//}//void Print(int* p, int sz)//应该是指针
//{
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// printf("%d ", *(p + i));
// }
//}
//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// int sz = sizeof(arr) / sizeof(arr[0]);
// Print(arr, sz);//arr数组名就是数组首元素的地址
// //sizrof(arr)
// //&arr
// return 0;
//}冒泡升序
//void bubble_sort(int arr[],int sz)
//{
// //确定趟数
// int i = 0;
// for (i = 0; i < sz - 1; i++)
// {
// //一趟排序的过程
// int j = 0;
// for (j = 0; j < sz-1-i ; j++)
// {
// if (arr[j] > arr[j + 1])
// {
// int tmp = arr[j];
// arr[j] = arr[j + 1];
// arr[j + 1] = tmp;
// }
// }
// }
//}
//print_arr(int arr[], int sz)
//{
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// printf("%d ", arr[i]);
// }
//}
//int main()
//{
// int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
// //排序
// int sz = sizeof(arr) / sizeof(arr[0]);
// bubble_sort(arr,sz);
// //打印
// print_arr(arr, sz);
// return 0;
//}//冒泡升序
void bubble_sort(int arr[], int sz)
{//确定趟数int i = 0;for (i = 0; i < sz - 1; i++){//假设已经有序了int flag = 1;//一趟排序的过程int j = 0;for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 0;}}if (flag == 1){break;}}
}
print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };//排序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);//打印print_arr(arr, sz);return 0;
}