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

    科技2024-11-30  7

    可恶的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; }

    补充

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

    Processed: 0.061, SQL: 8