12天学好C语言——记录我的C语言学习之路(Day 9)
12天学好C语言——记录我的C语言学习之路
Day 9:
函数部分告一段落,但是我们并不是把函数完全放下,因为函数无处不在,我们今后的程序仍然会大量运用到函数
//转入指针部分的学习,了解指针是什么
/*//program 9.1
#include<stdio.h>
int main()
{
int c=123,d=345;
int *a,*b;//定义两个整型的指针变量(int 是指针变量的基类型,我们必须在定义指针变量的时候指定基类型,因为变量的类型不同决定了他们在内存中所占字节的不同,我们只知道地址2000(起始地址),但是我们不知道是在2000一个字节中取一个char型数据,还是在2000~2003 四个字节中取一个int 型数据)
a=&c;
b=&d;
printf("变量c的起始地址为%d,变量d的起始地址为%d\n",a,b);//format specifies type 'int' but the argument has type 'int *' 警告是什么意思。
//这里输出的是a,b表示的变量(c,d)的起始地址(变量c的起始地址为1606416376,变量d的起始地址为1606416372)
printf("*a=%d,*b=%d",*a,*b);//这里输出的是指针变量a,b指向的 地址 中的 值 (*a=123,*b=345),加星号*说明这个变量具有指向性,所以他表示的应该是指向的那个地址中变量的值
return 0;
}
*/
///这个比较大小,不是直接比较的变量a、b,而是比较的指针变量*p1、*p2指向的a、b的地址中的值
/*//program 9.2
#include "stdio.h"
int main()
{
int *p1,*p2;
int a,b,*temp;
scanf("%d%d",&a,&b);
p1=&a;
p2=&b;
if(a>b)
{
temp=p1; //地址互相交换
p1=p2;
p2=temp;
}
printf("较大的值为%d",*p2);
return 0;
}
*/
//比较大小的时候完全不用通过第三个值交换,可以通过互相交换地址得到,这样改变的是指针变量的值(a与b的地址),而不变两个整型变量的值。 当然下面的解法更简单,一共两个变量a、b,只要比较完之后让指针指向较大的,就ok了。
/*//program 9.3
#include <stdio.h>
int main()
{
int a,b;
int *p1;
p1=&a;
scanf("%d%d",&a,&b);
if (a<b) {
p1=&b;
}
printf("较大的数是%d",*p1);
}
*/
//用指针变量作为函数参数,然后将输入的两个整数输出最大值最小值
/*//program 9.4
#include "stdio.h"
int main()
{
int a,b;
scanf("%d%d",&a,&b);
int *p1=&a;
int *p2=&b;
void maxmin(int *m,int *n);
maxmin(p1,p2);
return 0;
}
void maxmin(int *m,int *n)
{
int *temp;
if(*m<*n)//这个程序交换的是指针变量的值,也就是交换的地址,地址改变了,但是&a存的还是a原本赋给他的值,&b中存的也是b原本的值。如果a>b则不执行交换,输出max是*p1(&a中的值),min是*p2(&b中的值),这没错,但是如果a<b,那么执行了函数中的地址交换,指针变量p1中存的值变成了&b,那么输出max是*p1(&b中的值),也是正确的。所以说改变指针变量的值(地址)也是可行的办法
{
temp=m;
m=n;
n=temp;
}
printf("max is %d,min is %d",*m,*n);
}
*/
数组名也就是数组的首地址,即首元素地址
/*//program 9.5
#include<stdio.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
void fun(int *arr);//这里就可以等于 void fun(int a[]); 因为*arr表示的是数组的首元素,也就是a[0]。 当数组名作为函数参数的时候,实参数组名表示该数组首元素地址,而形参是用来接受传递过来的数组元素的受地址的。
fun(a);
return 0;
}
void fun(int *arr)
{
printf("%d\n",*arr);
arr++;
printf("%d\n",*arr);
}
*/
//逆序输出一个整型数组,用指针变量作为实参(指针变量作为实参的时候,必须先让指针变量具备确定的值)
//int f(int a[],int n) 与 int f(int *a,int n) 是等价的
/*//program 9.6
#include <stdio.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,0};
int *p;
p=a;//给指针变量赋上确定的值
void abc(int *p,int n);
abc(p,10);
for(p=a;p<a+10;p++)
{
printf("%d\n",*p);
}
return 0;
}
void abc(int *p,int n)
{
int *i,*j,temp;
for (i=p,j=p+n-1; i<=p+(n-1)/2; i++,j--) {//这个地方i与j的初始值一定别错,还有就是i循环到什么地方结束?(也就是for循环的表达式2),等确定好这些值的时候再写程序,快速还不容易出错
temp=*i;
*i=*j;
*j=temp;
}
}
*/
当然上面的程序未免太复杂,是可以优化的(program 9.7)
/*//program 9.7
#include <stdio.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,0};
int *p=a+9;
for (; p>=a+0; p--) {
printf("%d\n",*p);
}
return 0;
}
*/
//用指针方法对10个整数进行排序(选择排序法)
//指针的选择排序法。其实就是用指针变量作为函数的形参和实参,然后在函数中交换值的时候用的是指针变量的地址中的值
/*//program 9.8
#include "stdio.h"
int main()
{
int a[10]={2,42,34,65,76,887,69,80,1,23};
int *p=a;
void abc(int *q,int n);
abc(a,10);
for (p=a; p<a+10; p++)//用p,也就是地址来控制整个输出,其中p=a是将数组a的首地址赋给了p,作为p的初始地址(也就是a[0]的地址)
{
printf("%d\n",*p);
}
return 0;
}
void abc(int *q,int n)
{
int temp,i,j,k;
for (i=0; i<n-1; i++) {
k=i;
for (j=i+1; j<n; j++) {//j只是控制了循环的次数
if (*(q+k)<*(q+j)) {
k=j;
}
}
temp=*(q+k);
*(q+k)=*(q+i);
*(q+i)=temp; //k指代的是需要和i换的值,k和i进行交换
}
}
*/
下面再用指针方法写一下冒泡排序法。其实都是换汤不换药的。
//用指针变量做函数形参和实参,对十个整数进行排序,冒泡排序法
/*//program 9.9
#include "stdio.h"
int main()
{
int a[10]={23,121,24,1,243,65,77,6,87,8};
int *p=a;
void bcd(int *b,int n);
bcd(p,10);
for (p=a; p<a+10;p++) {
printf("%d\n",*p);
}
return 0;
}
void bcd(int *b,int n)
{
int i,j,temp;
for (j=0; j<n-1; j++) { //j表示第几趟循环
for (i=0; i<9-j; i++) { //i表示每趟循环需要哪些数(i就是指代元素的下标)往下进行比较,因为一次循环将有一个数字沉底,所以说再从第一个元素a[0]开始与下面的每个数进行比较时,比较的数量随着循环一次次的结束而减少(下面沉底的数不需要比较了)
if(*(b+i)<*(b+i+1))
{
temp=*(b+i);
*(b+i)=*(b+i+1);
*(b+i+1)=temp;
}
}
}
}
*/
对于表示二维数组有关指针的格式,确实有很多,比较复杂,记起来不容易,下面我为大家整理了一下:
//对指针变量表示二维数组的格式小探究
/*//program 9.10
#include <stdio.h>
int main()
{
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
printf(“%d\n",*(*a+1)); //*a+1是0行1列元素的地址
printf(“%d\n",*(*(a+1))); //*(a+1)是1行0列的元素的地址
return 0;
}
*/
/*
二维数组的有关指针的表示形式
有这么一个数组: a[3][3]
①一般我们看到二维数组名,那么表示的就是该二维数组的首地址(该二维数组的首地址就是该二维数组首行的地址,也就是0行的首地址) a 代表的就是0行的地址
②我们看到二维数组中的一维数组名,那么表示的就是该行的首地址(该行的首地址就是该行首元素的地址,可以直接把它看成一维数组来理解,一维数组的数组名不就是表示的是该一维数组首元素的地址么,在循环输出一维数组的时候,我们常用for语句将p的初始值定为a,p=a) a[0]代表的就是0行0列的元素地址
③还有一种形式,比如说 a[1]+2 ,显然这是1行两列的元素a[1][2]的地址
④这种形式是最直观的 &a[1][2] ,显然这是表示元素a[1][2]的地址
⑤然后就是一种通用的格式,这种格式往往省略某一部分,一定要把你看到的地址形式带到通用式子里去,看看究竟表示什么。
这个式子就是 *(a+i)+j 这个形式非常的普遍,a是0行的地址,在a的基础上变化i也就是对行的变化,另外一个j肯定就是对列的变化了。我们平时做题的时候一般这种形式是给不全的,一般都是没有i,或者是没有j。如下面这几种形式
*a+1 这显然可以看成 *(a+0)+1 ,所以这表示的是0行1列元素的地址
*(a+1) 这显然可以看成 *(a+1)+0 ,也就是表示1行0列元素的地址
*a 0行0列元素的地址
*(a+2)+1 2行1列的元素的地址
⑥最后一种表示形式是对元素的值的表示(这次不是地址了),如下:
*(*(a+1)+1) 在表示1行1列元素地址的外面加星号和括号,表示1行1列的元素的值,当然也可以写成 *(a[1]+1)
★★★通用格式为: *(*(a+i)+j)或者*(a[i]+j) 都表示的是i行j列元素的值★★★
⑦最后一句话,二维数组名 是指向 行 的,而 一维数组名 是指向 元素的
*/
写一个程序:
//输入一个二维数组,然后有指针变量输出二维数组里面的元素的值
/*/program 9.11
#include "stdio.h"
int main()
{
printf("请输入二维数组的维度:\n");
int m,n;
scanf("%d%d",&m,&n);
int a[m][n];
printf("请为这个二维数组赋值:\n");
for (int i=0; i<m; i++) {
for (int j=0; j<n; j++) {
scanf("%d",*(a+i)+j);
}
}
int *p;
for (p=*a; p<*a+m*n; p++) {//将p依次指向下一个元素,显然一个用m*n个元素
if ((p-a[0])%n==0) {
printf("\n");
}
printf("%5d",*p);//因为二维数组中的元素地址是连续的,所以我们完全可以直接从第一个输出到最后一个,不用嵌套两个i、jfor循环
}
return 0;
}
*/
下面我们将认识二维数组做函数的形参,怎么声明定义,好好看这里。
//3个学生,四门课的成绩,求总平均成绩和第n名学生的所有成绩
/*//program 9.12
#include "stdio.h"
int main()
{
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
void abc(int (*b)[4],int n);//这个地方二维数组作为函数形参的声明和一维数组作为函数形参时的声明相仿,还是将二维数组看成多个一维数组的组合,声明的时候代表 列 的位置还是照样写上面,而代表 行 的位置要用指针变量表示,函数的int *b[4]这个地方要求我们传入的实参必须是一维数组的地址
void average(int *b,int n);//求所有元素的平均值,自然要从第一个元素开始,把所有的元素加起来然后除以元素个数,而我们声明的这个函数正是如此,*b我们知道就代表*(b+0)+0,也就是a[0][0]的地址,从这个地址开始,一直到最后一个的地址(第n个),取里面的数据相加。所以这个函数应该如此声明。换一种考虑,一维数组怎么声明来着?void a(int *p,int n); 因为传入的是首元素的地址,对元素的地址进行操作,要用的是元素地址里面的值,所以说多维数组用指针变量求平均值,其实和一维数组求平均值一样,都是对元素进行操作,看作一维数组就好了
abc(a,3);//实参是首行的地址
average(*a,12);//实参传入a[0][0]的地址,这个地方就不能和一维数组一样传入数组名了,因为这里a毕竟是二维数组的数组名
return 0;
}
void abc(int (*b)[4],int n)
{
int i=0;
for (i=0; i<4; i++) {
printf("%-4d",*(*(b+n-1)+i));
}
printf("\n");
}
void average(int *b,int n)
{
int *p=b;
int sum=0,aver;
for (p=b; p<b+12; p++) {
sum+=(*p);
}
aver=sum/n;
printf("average=%d",aver);
}
*/
今天对指针的理解就到此为止,一开始可能觉得挺难理解的,但是晚上睡觉的时候多想想,就差不多了。记住指针也是变量的一种类型,指针变量存的是地址!
版权声明:本文为博主原创文章,未经博主允许不得转载。
12天学好C语言——记录我的C语言学习之路(Day 9)的更多相关文章
- 12天学好C语言——记录我的C语言学习之路(Day 12)
12天学好C语言--记录我的C语言学习之路 Day 12: 进入最后一天的学习,用这样一个程序来综合考量指针和字符串的关系,写完这个程序,你对字符串和指针的理解应该就不错了. //输入一个字符串,内有 ...
- 12天学好C语言——记录我的C语言学习之路(Day 11)
12天学好C语言--记录我的C语言学习之路 Day 11: 因为指针部分比较的难,所以我们花费的时间也是最长的,希望大家耐的住性子,多多理解,多多打代码.好了,废话不多说,来看第11天的学习. //编 ...
- 12天学好C语言——记录我的C语言学习之路(Day 10)
12天学好C语言--记录我的C语言学习之路 Day 10: 接着昨天的指针部分学习,有这么一个题目: //还是四个学生,四门成绩,只要有学生一门功课没及格就输出这个学生的所有成绩 /*//progra ...
- 12天学好C语言——记录我的C语言学习之路(Day 8)
12天学好C语言--记录我的C语言学习之路 Day 8: 从今天开始,我们获得了C语言中很有力的一个工具,那就是函数.函数的魅力不仅于此,一个程序到最后都是由众多函数组成的,我们一定要用好函数,用熟练 ...
- 12天学好C语言——记录我的C语言学习之路(Day 7)
12天学好C语言--记录我的C语言学习之路 Day 7: 昨天进行了一天的数组学习,今天大家可以先写几个昨天的程序热热身,回顾回顾,然后今天第一个新程序也是关于数组的,比较难,准备好就开始啦! //输 ...
- 12天学好C语言——记录我的C语言学习之路(Day 6)
12天学好C语言--记录我的C语言学习之路 Day 6: 今天,我们要开始学习数组了. //①数组部分,数组的大小不能够动态定义.如下: //int n; scanf("%d,& ...
- 12天学好C语言——记录我的C语言学习之路(Day 5)
12天学好C语言--记录我的C语言学习之路 Day 5: 第五天的学习开始了,今天我们主要对几个程序进行编写,让自己充分的熟练编程语言,大量的题目会让自己变的精炼.以一个程序(program 5.1) ...
- 12天学好C语言——记录我的C语言学习之路(Day 4)
12天学好C语言--记录我的C语言学习之路 Day 4: 首先来看一段程序: //输出下面4*5的矩阵 /* 1 2 3 4 5 2 4 6 8 10 3 6 9 12 ...
- 12天学好C语言——记录我的C语言学习之路(Day 3)
12天学好C语言--记录我的C语言学习之路 Day 3: 不知不觉到了第三天的学习,我们前两天学习的东西很杂乱,各个方面都有学习.我觉得这不是不系统,也不是学的不扎实,这种学习对于初学者而言我认为是很 ...
随机推荐
- Java字节码(.class文件)格式详解(一)
原文链接:http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html 小介:去年在读<深入解析JVM>的时候写的,记得当时还 ...
- 可学习的多人人脸识别程序(基于Emgu CV)
源代码下载(需要安装Emgu CV,安装方法请百度) 很多朋友使用Emgu CV遇到CvInvoke()的报错,我找到一种解决方法. 把EmguCV目录下bin里面的所有dll复制到C:\WINDOW ...
- ios开发 数据库版本迁移手动更新迭代和自动更新迭代
数据库版本迁移顾名思义就是在原有的数据库中更新数据库,数据库中的数据保持不变对表的增.删.该.查. 数据持久化存储: plist文件(属性列表) preference(偏好设置) NSKeyedArc ...
- git 快速入门(二)
一.引子 git代码托管的优秀工具之一, 其工作原理和svn截然不同.一旦拥有主干master分支权限, 只要在本地拉取主干分支, 可以随时随地切换分支. 它拥有众多优点,eg :支持在断网的情况下, ...
- java定时任务接口ScheduledExecutorService
一.ScheduledExecutorService 设计思想 ScheduledExecutorService,是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说 ...
- 搭建maven+spring+mybatis工程
一.maven 命令搭建web项目 可以参考我之前的一篇博文maven快速入门 1.搭建web工程 mvn archetype:generate -DgroupId=com.yuanmeng.spri ...
- Mac窗口管理管理软件SizeUp
一.SizeUp 是一款 Mac窗口管理管理软件.借助SizeUp,可以快速变化窗口大小(最大化.最小化),可以快速切换窗口的不同位置. 尤其在双显示器,更是扮演者方便.高效.好用的角色,提供了快速切 ...
- 【转】Netty那点事(二)Netty中的buffer
[原文]https://github.com/code4craft/netty-learning/blob/master/posts/ch2-buffer.md 上一篇文章我们概要介绍了Netty的原 ...
- 自定义滚动条CSS样式
首先,给个默认css,清除掉以前的样式,给默认背景 .scrollbar { margin-left: 30px; float: left; height: 300px; width: 65px; b ...
- 【52】写了placement new也要写placement delete
1.Widget* pw = new Widget; 调用了两个方法:第一个方法是operator new 负责分配内存:第二个方法是在分配的内存上构造Widget,即调用Widget的default ...