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: 不知不觉到了第三天的学习,我们前两天学习的东西很杂乱,各个方面都有学习.我觉得这不是不系统,也不是学的不扎实,这种学习对于初学者而言我认为是很 ...
随机推荐
- gtest框架使用
gtest文档说明: 由于公司单元测试的需要,自己花了大半天时间下载了一个gtest框架,使用了一些测试例子,总览了coderzh的玩转gtest测试框架,又看了几篇gtest博客,写下了以下内容,作 ...
- 1098. Insertion or Heap Sort (25)
According to Wikipedia: Insertion sort iterates, consuming one input element each repetition, and gr ...
- iOS 沙盒目录结构介绍
iOS系统中,每个应用都有自己的沙盒,且应用只能访问其对应的沙盒目录下面的文件.当然,在用户授权的情况下,应用也可以访问其他目录下面的文件.比如,用户授权情况下,应用可以访问相册.通讯录.在开发中,经 ...
- ECSHOP在线手册布局参考图--文章列表页 article_cat.dwt
A.购物车 1,设置方法 程序自动读取购物车的商品数量 2,代码相关 cart.lbi 中 {insert_scripts files='transport.js'} <div clas ...
- NSTimer、 NSTask、 NSThread 和 NSRunloop 之间的区别
NSTimer是一个计时器对象,方法调用在对未来的选择对象. NSThread是一个线程类. 也就是创建一个线程. NSTask类是一个过程,一种方式运行程序从您的其他程序. NSOperation是 ...
- Android设计模式系列--模板方法模式
模板方法,和单例模式是我认为GOF的23中最简单的两种模式.但是我个人对模板方法的经典思想特别推崇,虽然模板方法在大对数情况下并不被推荐使用,但是这种通过父类调用子类的方法,使用继承来改变算法的一部分 ...
- U盘安装SLES的方法
安装准备: 一个4G 或 大于4G的U盘 也同样适合移动硬盘, SLES-11-DVD-i586-GM-DVD1.iso 文件1) 将U盘格式化成FAT32格式; 2) 下载 syslinux工具 h ...
- 详解UML中的聚合,关联,泛化等关系
1. Overview UML设计类中,类的关系分为Generalization(泛化),Dependency(依赖关系).Association(关联关系).Aggregation(聚合关系).Co ...
- UIAlertViewController+TextField 输入框
if (IOS8) { UIAlertController *alertController=[UIAlertController alertControllerWithTitle:CustomLoc ...
- Nginx开发从入门到精通
nginx由于出色的性能,在世界范围内受到了越来越多人的关注,在淘宝内部它更是被广泛的使用,众多的开发以及运维同学都迫切的想要了解nginx模块 的开发以及它的内部原理,但是国内却没有一本关于这方面的 ...