可恶的C语言——函数与指针

可恶的C语言——函数与指针

函数是一块代码,接收零个或多个参数,做一件事情,并返回零个或一个值。

函数定义

void sum(int begin,int end)//函数头
{
    int i;
    int sum=0;
    for(i=begin;i<=end;i++){
        sum+=i;
    }
    printf("%d到%d的和是%d\n",begin,end,sum);
}    
//大括号中为函数体。void为返回类型。sum为函数名。void表示不返回任何东西。sum后面的括号中内容为参数表,参数间用逗号隔开。

调用参数:函数名(参数值);

()起到了表示函数调用的重要作用,即使没有参数也需要()。

int main()
{
    sum(1,10);
    sum(20,30);
    sum(35,45);
    
    return 0;
}

从函数中返回值

return停止函数的执行,并送回一个值。一个函数里可以出现多个return语句。

没有返回值的函数:void函数名(参数表)

不能使用带值的return,可以没有return,调用的时候不能做返回值的赋值。

#include <stdio.h>

void sum(int begin,int end);

int main()
{
    sum(1,10);
    sum(20,30);
    sum(35,45);
    
    return 0;
}    

void sum(int begin,int end)
{
    int i;
    int sum=0;
    for(i=begin;i<=end;i++){
        sum+=i;
    }
    printf("%d到%d的和是%d\n",begin,end,sum);
}

对于函数参数表中的参数,叫做”形式参数“。

调用函数时给的值叫做”实际参数“。

本地变量:函数的每次运行,就产生一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的。定义在函数内部的变量就是本地变量。参数也是本地变量。

变量的生存期:什么时候这个变量开始出现了,到什么时候它消亡了。

变量的作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)

C语言不允许函数嵌套定义。

指针

运算符&:获得变量的地址,其操作数必须是变量。

int i;
printf("%p",&i);

地址的大小是否与int相同取决于编译器。

指针:保存地址的变量。

int i;
int* p=&i;
//下面两种表达方式相同
int* p,q;
int *p,q;

变量的值是内存的地址。普通变量的值是实际的值。指针变量的值是具有实际值的变量的地址。

void f(int *p);
int i=o;
f(&i);

在被调用的时候得到了某个变量的地址,在函数里面可以通过这个指针访问外面的这个i。

*是一个单目运算符,用来访问指针的值所表示的地址上的变量,可做右值也可做左值。

int k=*p; *p=k+1;

指针的使用

  • 需要传入较大的数据时用作参数
  • 传入数组后对数组做操作
  • 函数返回不止一个结果
  • 需要用函数来修改不止一个变量
  • 动态申请的内存

应用场景一

交换两个变量的值

#include <stdio.h>

void swap(int *pa,int *pb);

int main(void)
{
    int a=5;
    int b=6;
    swap(&a,&b);
    printf("a=%d,b=%d\n",a,b);
    
    return 0;
}

void swap(int *pa,int *pb)
{
    int t=*pa;
    *pa=*pb;
    *pb=t;
}

应用场景二a

函数返回多个值,某些值就只能通过指针返回。

传入的参数实际上是需要保存带回的结果的变量。

#include <stdio.h>

void minmax(int a[],int len,int *max,int *min);

int main(void)
{
    int a[]={1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
	int min,max;
    minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
    printf("min=%d,max=%d\n",min,max);
    
    return 0;
}

void minmax(int a[],int len,int *min,int *max)
{
    int i;
    *min = *max=a[0];
    for(i=1;i<len;i++){
        if(a[i]<*min){
            *min=a[i];
        }
        if(a[i]>*max){
            *max=a[i];
        }
    }
}

应用场景二b

函数返回运算的状态,结果通过指针返回。常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错。但是当任何数值都是有效的可能结果,就得分开返回了。

#include <stdio.h>
//如果除法成功返回1,否则返回2
int divide(int a,int b,int *result);

int mian(void)
{
    int a=5;
    int b=2;
    int c;
    if(divide(a,b,&c)){
        printf("%d/%d=%d\n",a,b,c);
    }
    return 0;
}

int divide(int a,int b,int *result)
{
    int ret = 1;
    if (b==0)ret=0;
    else{
        *result=a/b;
    }
    result ret;
}

指针与数组

函数参数表中的数组实际上是指针,可以用数组的运算符[]进行运算。

下面四种函数原型是等价的:

int sum(int *ar,int n);

int sum(int*,int);

int sum(int ar[],int n);

int sum(int[],int);

数组变量是特殊的指针,数组变量本身表达地址。所以int a[10];int*p=a;无需用&取地址。

但是数组的单元表达的是变量,需要用&取地址。a==*a[0];

[]运算符可以对数组做,也可以对指针做。

运算符可以对指针做,也可以对数组做: * a=25;

数组变量是const的指针,所以不能被赋值

int a[]<==>int *const a=…

指针与const(只适用于C99)

指针是const表示一旦得到了某个变量的地址,便不能再指向其他变量。表示不能通过这个指针去修改那个变量,并不能使得那个变量成为const。

const int *p=&i;
*p=26;//ERROR!(*p)是const
i=26;//OK
p=&j;//OK

判断哪个被const

const int p 里的const是修饰int的,也就是说这个int值不可以改变
int const * p 里 const是修饰
p的 , 而*p的类型是int 也就是说const修饰的是int , 那也是跟上面的一样int值不可变

(int) const § p的类型是int是个指针 这个指针是一个放地址的变量
const修饰的是p也就是修饰的类型是int*也就是这个指针不可变 就是地址不可变
虽然地址不能改变,但是地址指向的int的值是可以变的,const修饰的只是一个地址。

const int *p : 值不可变

int const *p : 值不可变

int *const p : 地址不可变, 那个地址的值可以变

const int *const p : 地址和值都不可变

当要传递的参数类型比地址大的时候,可以将一个非const的值转换成const的。

void f(const int* x);
int a=15;
f(&a);//ok
const int b=a;

f(&b);//ok
b=a+1;//Error!

const数组

const int a[]={1,2,3,4,5,6,};

数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int,所以必须通过初始化进行赋值。

保护数组值:因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值,为了保护数组不被函数破坏,可以设置参数为const。

int sum(const int a[],int length);

指针运算

给一个指针加1表示要让指针指向下一个变量

int a[10];
int *p=a;
*(p+1)-->a[1]

如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义。

*p++:取出p所指的那个数据来,完事后顺便把p移到下一个位置去。*的优先级虽然高,但是没有++高。常用于某些数组类的连续空间操作,在某些CPU上可直接被翻译成一条汇编指令。

指针的类型

无论指向什么类型,所以的指针的大小都是一样的,因为都是地址。但是指向不同类型的指针是不能直接互相赋值的。

指针的类型转换

void*表示不知道指向什么类型的指针。

计算时与char*相同(但不相通)。

指针也可转换类型。

int *p=&i;void *q=(void *)p;

这并没有改变p所指的变量的类型,而是以不同的眼光通过p看其所指的变量。

动态内存分配

int *a = (int *)malloc(n *sizeof(int));

#include <stdio.h>
#include<stdlib.h>

int main(void)
{
    int number;
    int *a;
    int i;
    printf("输入数量:");
    scanf("%d",&number);
    //int a[number];
    a=malloc(number*sizeof(int));
    for(i=0;i<number;i++){
        scanf("%d",&a[i]);
    }
    for(i=number-1;i>=0;i--){
        printf("%d",a[i]);
    }
    free(a);
    
    return 0;
}    

malloc

#include <stdlib.h>

void*malloc(size_t size);

向malloc申请的空间的大小是以字节为单位的,返回的结果是void*,需要类型转换为自己需要的类型。

如果申请失败则返回0或NULL。

free():把申请来的空间还给系统。只是还申请来的空间的首地址。

练习题

输出一个中文版九九乘法表

#include <stdio.h>
int main()
{
	char a[21]={"0一二三四五六七八九十"};
	int i=1,j=1,t=1,u=1;
	
	for(i=1;i<=9;i++)
	{
		for(j=1;j<=i;j++)
		{
			if(i*j<=9&&i!=j)
			{
				printf("%c%c%c%c得%c%c\t",a[2*j-1],a[2*j],a[2*i-1],a[2*i],a[2*i*j-1],a[2*i*j]);
			}
			if(i*j<=9&&i==j)
			{
				printf("%c%c%c%c得%c%c\n",a[2*j-1],a[2*j],a[2*i-1],a[2*i],a[2*i*j-1],a[2*i*j]);
			}
			
			if(i*j>9&&i!=j)
			{
				printf("%c%c",a[2*j-1],a[2*j]);
				printf("%c%c",a[2*i-1],a[2*i]);
				printf("%c%c",a[2*(i*j/10)-1],a[2*(i*j/10)]);
				printf("%c%c",a[19],a[20]);
				if(i*j%10!=0)
				{
					printf("%c%c",a[2*(i*j%10)-1],a[2*(i*j%10)]);
				}
				printf("\t");
			}
			
			if(i*j>9&&i==j)
			{
				printf("%c%c",a[2*j-1],a[2*j]);
				printf("%c%c",a[2*i-1],a[2*i]);
				printf("%c%c",a[2*(i*j/10)-1],a[2*(i*j/10)]);
				printf("%c%c",a[19],a[20]);
				printf("%c%c",a[2*(i*j%10)-1],a[2*(i*j%10)]);
				printf("\t");
				printf("\n"); 
			}
		}
	}

	return 0; 
}

小鱼最近被要求参加一个数字游戏,要求它把看到的一串数字 (长度不一定,以 00 结束),记住了然后反着念出来(表示结束的数字 00 就不要念出来了)。这对小鱼的那点记忆力来说实在是太难了,你也不想想小鱼的整个脑袋才多大,其中一部分还是好吃的肉!所以请你帮小鱼编程解决这个问题。

#include <stdio.h>

int main()
{
	int a[100],i,t;
	
	while(t!=0){
		if(i<=99){
		printf("请输入一个整数:");
		scanf("%d",&t);
		i++;
		a[i]=t;
    	}else{
    		t=0;
    		a[100]=0; 
		}
	} 
	do{
		i--;
		printf("%d ",a[i]);
	}while(i>=2);
	
	return 0; 
}

输入L、R两个数(L一定小于或等于R且R小于1000),然后输入一个小于10的整数n,请统计范围[L,R]的整数中n出现的次数(如n=2,2 、12、 20、22、222等,注意当n=0时可能会出现bug)

#include <stdio.h>
int main()
{
	int n,t,u,i=0;
	double L,R;
	
	printf("请输入两个小于1000的数:");
	scanf("%lf %lf",&L,&R);
	
	printf("请输入一个小于10的整数:");
	scanf("%d",&n);
	
	if(R<L)
	{
		u=R;
		R=L;
		L=u;
	}
	
	if(L/100!=0)
	{
		t=L;
		while(t<=R)
		{
			if(t/100==n)
			{
				i++;
			}
			if(t/10%10==n)
			{
				i++;
			}
			if(t%10==n)
			{
				i++;
			}
			t++;
		}
	}
	
	if(L/100==0&&L/10!=0)
	{
		t=L;
		while(t<=R)
		{
			if(t/100==n)
			{
				i++;
			}
			if(t/10%10==n)
			{
				i++;
			}
			if(t%10==n)
			{
				i++;
			}
			t++;
		}
	}
	
	if(L/10==0)
	{
			t=L;
		while(t<=R)
		{
			if(t/100==n)
			{
				i++;
			}
			if(t/10%10==n)
			{
				i++;
			}
			if(t%10==n)
			{
				i++;
			}
			t++;
		}
	}
	
	printf("%d",i);
	
	return 0;	
}

输入10个打乱的整数,使用冒泡排序将这10个数从小到大排好并输出(可以百度冒泡排序,但是要理解怎么用)

#include <stdio.h>
int main()
{
	int i,j,t,a[10];
	
	for(i=0;i<10;i++)
	{
		scanf("%d",&a[i]);
	}
	
	for(i=0;i<10;i++)
	{
		for(j=0;j<10;j++)
		{
			if(a[i]<a[j])
			{
				t=a[i];
				a[i]=a[j];
				a[j]=t;
			}
		}
	}
	
	for(i=0;i<10;i++)
	{
		printf("%d ",a[i]);
	}
	
	return 0; 
}

输入一个4x5的矩阵,输出它的转置矩阵

#include <stdio.h>
int main()
{
	int i,j,a[10][10];
	
	for(i=0;i<4;i++)
	{
		for(j=0;j<5;j++)
		{
			scanf("%d",&a[i][j]);
		}
	}
	
	for(j=4;j>=0;j--)
	{
		for(i=3;i>=0;i--)
		{
			printf("%3d",a[i][j]);
		}
		printf("\n");
	}
	
	return 0; 
}

夏天到了,各家各户的用电量都增加了许多,相应的电费也交的更多了。小玉家今天收到了一份电费通知单。小玉看到上面写:据闽价电[2006]27号规定,月用电量在150千瓦时及以下部分按每千瓦时0.4463元执行,月用电量在151~400千瓦时的部分按每千瓦时0.4663元执行,月用电量在401千瓦时及以上部分按每千瓦时0.5663元执行;小玉想自己验证一下,电费通知单上应交电费的数目到底是否正确呢。请编写一个程序,已知用电总计,根据电价规定,计算出应交的电费应该是多少。

#include <stdio.h>
int main()
{
	int amount;
	float money;
	printf("请输入用电总计:");
	scanf("%d",&amount);
	
	if(amount<=150)
	{
		money=amount*0.4463;
	}
	else if(amount<=400)
	{
		money=150*0.4463+(amount-150)*0.4663;
	}
	else
	{
		money=150*0.4463+250*0.4663+(amount-400)*0.5663;
	}
	
	printf("应交电费为%.1f元",money);
	
	return 0; 
}

输入一串长度为20的字母串,其中有大写有小写,请将里面的大写转为小写,小写转为大写后输出

#include <stdio.h>
int main()
{
	int i;
	char a[20];
	
	for(i=0;i<20;i++)
	{
		scanf("%c",&a[i]);
	}
	
	for(i=0;i<20;i++)
	{
		if(a[i]>='A'&&a[i]<='Z')
		{
			printf("%c",a[i]+='a'-'A');
		}
		else
		{
			printf("%c",a[i]+='A'-'a');
		}
	}
	
	return 0; 
}

补充

当形式参数是一维数组时,可以不说明数组的长度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值