C语言谭浩强编程错误归纳

C语言谭浩强第三版第四章例题及课后题错误归纳:顺序程序设计

C语言谭浩强第三版第五章例题及课后题错误归纳:选择结构程序设计

1.double类型的数据用scanf输入时要用%lf的形式输入

#include<stdio.h>

void main(void){
    double a,b;

    printf("请输入两个实数:");
    scanf("%f%f", &a, &b);
    printf("这两个实数为:%f, %f\n", a, b);
}

#include<stdio.h>

void main(void){
    double a,b;

    printf("请输入两个实数:");
    scanf("%lf%lf", &a, &b);
    printf("这两个实数为:%f, %f\n", a, b);
}

2.double型的数据不能用%d的形式输出
3.

char c; 

 (c >= 65 && c <= 97) ? c += 32 : c;       //(c >= 65 && c <= 97) ? c += 32; : c;    wrong form
   (c >= 'A' && c <= 'Z') ? c += 32 : c; 
可以直接写成上述的样子

条件运算符 https://blog.csdn.net/weixin_42072280/article/details/85006449

4. 闰年

if((year % 4 == 0 && year % 100) || (year % 400 == 0)){
        leap = 1;
    }

 if(year % 400 ==0 || (year % 4 ==0 && year % 100 != 0))

5.

double a;  //a为一个实数

if(a == 0)...    if(fabs(a) <= 1e-6)...

//对于判断a是否等于0时,要注意:由于a是实数,而实数在计算和存储时会有一些微小的误差,因此不能直接进行如下判断:if(a == 0)...,因为这样可能会出现本来是零的量,由于上述误差而被判别为不等于0而导致结果错误。所以,采取的方法就是判别a的绝对值(fabs(a))是否小于一个很小的数(例如10^(-6)),如果小于此数,就认为a等于0. 

6.

(1)在case子句后如果没有break;会一直往后执行一直到遇到break;才会跳出switch语句。

运行结果: 学英语学C语言好好休息 

(2)多个case可以共用一组执行语句

switch(c){    //记得一定要加break,否则就会一直运行直到遇到break,才会跳出switch语句 
        case 0: d = 0; break;
        case 1: d = 2; break;
        case 2: 
        case 3: d = 5;
        case 4: 
        case 5: 
        case 6: 
        case 7: d = 8;

        case 8: 
        case 9: 
        case 10: 
        case 11: d = 10;
        case 12: d = 15;
    }

7.

https://blog.csdn.net/YHxiaohao/article/details/79861154

 

C语言谭浩强第三版第六章例题及课后题错误归纳:

eg6.10译密码

1)如何输入一行字符 不知道个数呀  

     haha  用getcahr()  学到了   https://mp.csdn.net/postedit/82781637

    while((c = getchar()) != '\n'){ }

2)比较字符时,可以不用写具体的ASCII码值,直接用单引号括起来写字符就可以了

    while((c = getchar()) != '\n'){
        if((c >= 'A' && c <= 'V') || (c >= 'a' && c <= 'v')){  //前面编程get到的一个方法,这样子即使记不住这些字母的ASCII值也没有关系 
            c+=4;
        }else if((c >= 'W' && c <= 'Z') || (c >= 'w' && c <= 'z')){
            c-=22;
        }
        printf("%c", c) ;
    }

6.1输入两个正整数m和n,求其最大公约数和最小公倍数

求最大公约数的代码

int gcd(int a, int b){    //他们怎么这么聪明呢,真是太厉害了! 
    return b == 0 ? a: gcd(b, a%b);
}

6.4求1!+2!+3!+...+20!

int型为4个字节,32位,取值范围为:-2^31 ~(2^31-1),即-2147483648 ~ 2147483647

float为4个字节,32位,但其表示的范围为:-3.4*10^(-38) ~ 3.4*10^38

6.14输出星星图案

//如何按照用户输入的数字输入星星的个数

void star(int n){
    int i;
    for(i = 0; i < n; i++){
        printf("*");
    }
}

void space(int n){
    int i;
    for(i = 0; i < n; i++){
        printf(" ");
    }
}

6.15比赛赛手名单匹配

//思路:需要每次的X Y Z各出现且仅出现一次 ,如何实现呢? 见下面,神奇! 
#include<stdio.h>

void main(void){
    char i;
    char j;
    char k;
    
    for(i = 'X'; i <= 'Z'; i++){
        for(j = 'X'; j <= 'Z'; j++){
            if(i!=j){
                for(k = 'X'; k <= 'Z'; k++){
                    if(k!=i && k!=j){
                        printf("A=%c, B=%c, C=%c\n", i, j, k);
                    }
                }
            }
        }
    }
}

一个数如果恰好等于除它以外的因子之和,这个数就称为“完数”

C语言谭浩强第三版第七章例题及课后题:数组

eg7.3用起泡法对10个数排序(由小到大)

    //数组数据输入:由用户在执行框中输入
    int a[10];
    int i;
    
    printf("Please input 10 numbers:");
    for(i = 0; i < 10; i++){
        scanf("%d", &a[i]);
    } 

eg7.4将一个二维数组a的行和列的元素互换,存到另一个二维数组b中

    int row = 3;
    int col = 4;
    int a[row][col];
可以这样子写

eg7.5有一个3x4的矩阵,编程求出其中值最大的那个元素的值,以及其所在的行号和列号

小技巧:a[3][4]找最大值时,先将max=a[0][0];

eg7.9有3个字符串,要求找出其中最大者

1.字符串比较函数strcmp
2.自己一直在纠结,三个字符串怎么比较大小,要是三个里面有2个一样该怎么办,该不该继续向下比较,该算在哪一个里面呢 
答案是两个两个进行比较的,就像当年朱老师比较的,先找到两个里面的最大值,然后用这个最大值和第三个进行比较,从而找到3个里面的最大值 
3.自己还找了一下三个字符串最长字符串的字符个数,其实不用找的
字符串比较的规则是:对两个字符串自左向右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到'\0'为止。
如果全部字符相同,则认为相等;若出现不相同的字符,则以第一个不相同的字符的比较结果为准。
所以,人家比到'\0'就结束了,不用你给人家定界啦。 

7.1用筛选法求100之内的素数

   //每一行输出10个数字  i=0时就要输出回车怎么办? 加个1就好了

 for(i = 0; i < 100; i++){
        printf(...);
        if((i+1)%10 == 0){     //哈哈,我真是太聪明了 
            printf("\n");
        }

7.10有一篇文章,共有3行文字,每行有80个字符要求分别统计出其中英文大学字母、小写字母、数字、空格以及其他字符的个数

    char c[3][80];
    int i,j;
    
    for(i = 0; i < 3; i++){
        printf("请输入一串字符:"); 
        gets(c[i]);
        printf("%s\n", c[i]);
        for(j = 0; c[i][j] != '\0'; j++){    //for(j = 0; j < 80; j++){ 
            ......
        }
    }
  

7.14比较两个字符串

printf("*%d*", 'Z'-'A');

7.15字符串拷贝,不要用strcpy函数

小技巧:寻找字符串结束标志

方法1:自己

    for(i = 0; i < 80; i++){  
        if(str2[i] == '\0'){
            index = i;
            break;
        }
    }
方法2:课本

index = strlen(str2);

C语言谭浩强第三版第八章例题及课后题:函数

eg8.7递归求年龄

一个递归问题可以分为“回推”好“地推”两个阶段,要经历若干步才能求出最后的值。显而易见,如果要求递归过程不是无限制进行下去,必须具有一个结束递归过程的条件。

总结:递归其实也不难,首先分析题意,写出递归表达式,根据表达式进行程序编写。

eg8.11有一个一维数组score,内放10个学生成绩,求平均成绩

数组名做函数参数:此时形参应该是数组名或用指针变量

在被调用函数中声明形参数组的大小是不切任何作用的,因为c语言编译对形参数组不做检查,只是将实参数组的首元素的地址传给形参数组。

形参数组可以不指定大小,在定义数组时在数组名后面跟一个空的方括号。有时为了在被调用函数中处理数组元素的需要,可以另设一个形参,传递需要处理的数组元素的个数。

       

eg8.14求二维数组元素中的最大值 

多维数组名做函数参数

   

局部变量:

(1)形参也是局部变量

(2)在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句称为“分程序”或“程序块”。

全局变量:

程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,在函数之外定义的变量称为外部变量,外部变量是全局变量(也称为全程变量)。全局变量可以为本文件其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。

由于函数的调用只能带回一个返回值,因此有时可以利用全局变量增加函数间的联系渠道,通过函数调用能得到一个以上的值。

8.4转置二维数组的行列

小技巧:之前的那个题要求存到另一个数组中,所以需要另外开辟一个数组 ;现在这个题只要求转置行列,书中给了一个比较好的方法,只开辟一个变量,就像交换两个数一样,这样就可以节省很多存储空间 

错误:如果遍历数组的所有元素,则会转置两次,相当于没有转置;所以课本中从i+1遍历,这样只遍历上三角的元素,对角线也不用转置,所以省了很多操作。

void convert(int a[][N]){
    int i,j;
    int tmp;
    
    for(i = 0; i < N; i++){
        for(j = 0; j < N; j++){
            tmp = a[i][j];
            a[i][j] = a[j][i];
            a[j][i] = tmp;
        }
        printf("\n");
    }    


} void convert(int a[][N]){
    int i,j;
    int tmp;
    
    for(i = 0; i < N; i++){
        for(j = i+1; j < N; j++){
            tmp = a[i][j];
            a[i][j] = a[j][i];
            a[j][i] = tmp;
        }
        printf("\n");
    }  
}

8.5写一个函数,使输入的字符串按反序存放,在主函数中输入和输出字符串

小技巧:数组元素反序存放

void convert(char str[]){
    int index;
    int i;
    int tmp;
    
    index = strlen(str);   //找零字符结束标志
    printf("index=%d\n", index);
    
    for(i = 0; i < index/2; i++){   //for(i = 0; i < index; i++){   //和上一个题一样,不能全部遍历,只能遍历一半
        tmp = str[i];
        str[i] = str[index-i-1];     //str[i] = str[index-i+1];
        str[index-i-1] = tmp;     //str[index-i+1] = tmp;
        //printf("**%c**\n", str[i]);
    } 
}

//若index=10,即零字符结束标志在下标为10的地方

 i  ---- index-i-1

0 ----- 9

1 ----- 8

2 ----- 7

3 ----- 6

4 ----- 5

8.8输入一个4位数字,要求输出这4个数字字符,但每两个数字间空一个空格

小技巧:中间加空间输出

void showBySomeRule(char str[]){
    int i;
    
    for(i = strlen(str); i>0; i--){
        str[2*i] = str[i];
        str[2*i-1] = ' ';
    } 
    printf("%s", str);

思考:为什么要用--的形式?

因为用++的形式会覆盖原值,用--的话先从数组的后面开始,这样就不会影响原来数组中的值;这也给了我们一种新的输出数值的方法。

8.10(50)输入一行字符,将此字符串中最长的单词输出

由于函数只能向外传递一个值,故自己的做法是传入一个字符数组用来存放最长的单词,而课本是在函数中找到最长单词的起始位置,通过return传出来。

8.12(20)用牛顿迭代法求根

f = 2x^3 - 4x^2 + 3x -6

f = ((2x -4)x +3)x -6 这样子表示的表达式在运算时可节省时间

8.14输入10个学生5门课的成绩,分别求平均数最高分方差等

要求:输入10个学生5门课的成绩

思考:该如何定义变量,用什么结构?
答案:课本用的是二维数组,10个学生5门课 

8.15(100)输入10个职工的姓名和职工号,进行排序查找

/*题目要求 
1.输入10个职工的姓名和职工号
2.按职工号由小到大顺序排序,姓名顺序也随之调整
3.要求输入一个职工号,用折半查找法找出该职工的姓名,从主函数输入要查找的职工号,输出该职工姓名 
*/

要求:10个职工的姓名和职工号

分析:用什么数据结构

课本:用一个二维数组存放姓名 name[N][8] (每一个人可存放4个汉字) 

            用一个一维数组存放职工号

1.(1)

void inputData(char name[][8], int num[]){
    int i;
    
    for(i = 0; i < N; i++){
        printf("请输入第%d个职工的姓名:", i+1);
        gets(name[i]);
        printf("请输入第%d个职工的职工号:", i+1);
        scanf("%d", &num[i]);
    } 
}

出错了,这样子写程序的话,就只能输入第一个职工的姓名,其余职工的姓名无法输入。

(2)

void inputData(char name[][8], int num[]){
    int i;
    
    for(i = 0; i < N; i++){
        printf("请输入第%d个职工的姓名:", i+1);
        gets(name[i]);    //此处注意格式
        printf("请输入第%d个职工的职工号:", i+1);
        scanf("%d", &num[i]);
        getchar();
    } 
}

这样子可以全部输入。

分析:用scanf输入的时候最后会加一个回车作为结束输入的标志,而下一轮的gets函数直接接受这个回车作为自己的输入,所以导致正常的数据无法输入。

 

gets(s)函数与scanf("%s",s)相似,但不完全相同,使用scanf("%s",s) 函数输入字符串时存在一个问题,就是如果输入了空格会认为字符串结束,空格后的字符将作为下一个输入项处理,但gets()函数将接收输入的整个字符串直到遇到换行为止。但换行符会被丢弃,然后在末尾添加'\0'字符。

2.

void sort(char name[][8], int num[]){ //用起泡法排序 
    int i;
    int j;
    
    for(i = 0; i < N-1; i++){
        for(j = 0; j < N-1-i; j++){
            if(num[j] > num[j+1]){
                swap(&num[j], &num[j+1]);
                swapString(name[j], name[j+1]);   //地址
            }
        }
    } 
}

3.查找时,如何实现多次查找,即查找完一次之后判断是否要继续查找

 //一次查找 

   printf("\n请输入要查找的z职工号:");
    scanf("%d", &nu);
    index = search(name, num, na, nu);
    
    if(index == -1){
        printf("该职工号不存在\n");
    }else{
        printf("该职工号所对应的职工为:%s\n", na);
    }
 

 //多次查找

    int flag = 1;

    char  c;
    ......

   while(flag == 1){
        printf("\n请输入要查找的职工号:");
        scanf("%d", &nu);
        index = search(name, num, na, nu);
       ......    
        printf("\n是否要继续查找(Y/N 不区分大小写):");
        getchar();   //上一个scanf输入之后有一个回车,这个getchar是为了接收那个回车 
        c = getchar();
        if((c == 'N') || (c == 'n')){
            flag = 0;
        }        

    }

8.17(35)用递归法将一个整数n转换成字符串

void main(void){
    putchar(1+'0');
    printf("\n%d\n", 1+'0');
    printf("%c\n", 1+'0');

putchar函数的基本格式为:putchar(c)。

(1)当c为一个被单引号(英文状态下)引起来的字符时,输出该字符(注:该字符也可为转义字符);

(2)当c为一个介于0~127(包括0及127)之间的十进制整型数时,它会被视为对应字符的ASCII代码,输出该ASCII代码对应的字符;

(3)当c为一个事先用char定义好的字符型变量时,输出该变量所指向的字符。

用递归法将一个整数n转换成字符串

void convert(int n){
    int i;
    
    if((i = n/10) != 0){
        convert(i);
    }
    putchar(n%10 + '0');
}

8.18(60)给出年月日,计算该日是该年的第n天

//自己

int dayOfTheYear(int year, int month, int day){
    int num = 0;
    
    switch(month){
        case 1:  num=day; break;
        case 2:     num=31+day; break;
        case 3:     num=31+29+day; break;
        case 4:  num=31+29+31+day; break;
        case 5:  num=31+29+31+30+day; break;
        case 6:  num=31+29+31+30+31+day; break;
        case 7:  num=31+29+31+30+31+30+day; break;
        case 8:  num=31+29+31+30+31+30+31+day; break;
        case 9:  num=31+29+31+30+31+30+31+31+day; break;
        case 10: num=31+29+31+30+31+30+31+31+30+day; break;
        case 11: num=31+29+31+30+31+30+31+31+30+31+day; break; 
        case 12: num=31+29+31+30+31+30+31+31+30+31+30+day; break;            
    }
    if((leapYear(year) == 1) && (month == 2)){
        num--;
    } 
    return num;
}

//课本

int dayOfTheYear(int year, int month, int day){
    int day_tab[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int i;
    int num = day;
    
    for(i = 0; i < month-1; i++){
        num+=day_tab[i];
    }
    
    if((leapYear(year) == 1) && (month == 2)){
        num--;
    } 

    return num;

C语言谭浩强第三版第九章例题及课后题:预处理命令

9.2输入两个整数,求它们相除的余数

//#define remainder(a,b) a%b       //自己 
#define remainder(a,b) ((a)%(b))   //课本 

9.3求三角形面积

#define  S(a,b,c)     (((a)+(b)+(c))/2.0 )     
//#define  AREA(a,b,c)  sqrt(S*(S-a)*(S-b)*(S-c))    //错误的
#define  AREA(a,b,c)  sqrt(S(a,b,c)*(S(a,b,c)-a)*(S(a,b,c)-b)*(S(a,b,c)-c))

要把参数带上,否则是错误的

一般宏定义用大写

9.5分析下面的宏所定义的输出格式

对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。

9.9试述“文件包含”和程序文件的连接的概念,二者有何不同?

“文件包含”是事先将程序中需要用到的信息分别存放在不同的“头文件”中(文件后缀为.h),用户在编写程序时,利用#include命令将该头文件的内容包含进来,成为程序中的一部分,特别应当注意的是,该头文件与它所在的源文件共同组成一个文件模块(而不是两个文件模块)。在编译时它是作为一个文件进行编译的。

连接(link)则与此不同,它的作用是将多个目标文件连接起来,得到两个或多个目标文件(后缀为.obj),在连接(link)阶段,把这些目标文件与系统提供的函数库等文件连接成一个可执行的文件(后缀为.exe)。

eg9.7输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写字母输出

一般情况下,源程序中所有行都参加编译。但是有时候希望程序中一部分内容只在满足一定条件时才进行编译,也就是对这一部分内容指定编译的条件,这就是“条件编译”。

疑问:不用条件编译命令而直接用if语句也能达到要求,用条件编译命令有什么好处?

的确,对这个问题完全可以不用条件编译处理而用if语句处理,但那样做,目标程序长(因为所有语句都编译),运行时间长(因为在程序运行时对if语句进行测试)。而采用条件编译,可以减少被编译的语句,从而减少目标程序的长度,减少运行时间。当条件编译段比较多时,目标程序长度可以大大减少。

#include<stdio.h>
//#define LETTER  1  
//在对条件编译命令进行预处理时,由于LETTER为真,则对第一个if语句进行编译,运行时使小写字母变成大写 
#define LETTER  0
//在对条件编译命令进行预处理时,由于LETTER为假,则对第二个if语句进行编译,运行时使大写字母变成小写

void main(void){
    char str[20] = "C language";
    char c;
    int i = 0;
    
    while((c = str[i]) != '\0'){
        i++;
        #if  LETTER
        if(c >= 'a' && c <= 'z'){
            c = c-32;
        } 
        #else
        if(c >= 'A' && c <= 'Z'){
            c = c+32;
        }
        #endif
        printf("%c", c);
    }
    printf("\n");

9.10用条件编译输入一行电报文字选择输出形式

#include<stdio.h> 

#define  MAX 80
#define  CHANGE  1

void main(void){
    char str[MAX];
    int i;
    
    printf("Please input text:");
    gets(str);
    
    #if(CHANGE)
    {
        for(i = 0; i < MAX; i++){
            if(str[i] != '\0'){
                if((str[i] >= 'a' && str[i] < 'z') || (str[i] >= 'A' && str[i] < 'Z')){
                    str[i]+=1;
                }else if(str[i] == 'z' || str[i] == 'Z'){
                    str[i]-=25;
                }
            }
        }
    }
    #endif
    printf("output:%s\n", str);
}

比较(这两个很相似):

char str[80];
int i;

for(i = 0; i < strlen(str); i++){
   ......
}

char str[20] = "C language";
char c;
int i = 0;
    
while((c = str[i]) != '\0'){

    i++;
     ......
}

C语言谭浩强第三版第十章例题及课后题:指针

eg10.3输入a和b两个整数,按先大后小的顺序输出a和b

#include<stdio.h>

void swap(int *a, int *b);

void swap(int *a, int *b){
    int tmp;
    
    tmp = *a;
    *a = *b;
    *b = tmp;
}

void main(void){
    int m, n;
    int *p1 = &m, *p2 = &n;
    
    printf("请输入两个整数:");
    scanf("%d%d", &m, &n);
    printf("这两个整数为:%d,%d\n\n", m, n);
    

 swap(&m, &n);

//等价于swap(p1,p2);


    printf("交换后的两个整数为:%d,%d\n", m, n);
    printf("交换后两个指针指向的整数为:%d,%d\n", *p1, *p2);
}

#include<stdio.h>

void swap(int *a, int *b);

void swap(int *a, int *b){
    int *tmp;
    
    tmp = a;
    a = b;
    b = tmp;
}

 
void main(void){
    int m, n;
    int *p1 = &m, *p2 = &n;
    
    printf("请输入两个整数:");
    scanf("%d%d", &m, &n);
    printf("这两个整数为:%d,%d\n\n", m, n);
    
    swap(&m, &n);

//等价于swap(p1,p2);
    printf("交换后的两个整数为:%d,%d\n", m, n);
    printf("交换后两个指针指向的整数为:%d,%d\n", *p1, *p2);
}

#include<stdio.h>

void swap(int **a, int **b);

void swap(int **a, int **b){
    int *tmp;
    
    tmp = *a;
    *a = *b;
    *b = tmp;
}

 
void main(void){
    int m, n;
    int *p1 = &m, *p2 = &n;
    
    printf("请输入两个整数:");
    scanf("%d%d", &m, &n);
    printf("这两个整数为:%d,%d\n\n", m, n);
    
    swap(&p1, &p2);

//swap(&(&m), &(&n));    //[Error]lvalue required as unary '&' operand     //unary  a.一元的 
    //swap(&&m, &&n);      //[Error]label 'm' used but not defined      [Error]label 'n' used but not defined
    printf("交换后的两个整数为:%d,%d\n", m, n);
    printf("交换后两个指针指向的整数为:%d,%d\n", *p1, *p2);
}

交换的是两个变量的值  

交换的是两个指针的值

eg10.5输出数组中的全部元素

用for循环输出

#include<stdio.h>

void main(void){
    int a[10];
    int i;
    
    printf("请输入10个整数:");
    for(i = 0; i < 10; i++){
        scanf("%d", &a[i]);
    }
    
    printf("数组中的元素为:");
    for(i = 0; i < 10; i++){
        printf("%d ", a[i]);
    }
    printf("\n");
}

#include<stdio.h>

void main(void){
    int a[10];
    int i;
    
    printf("请输入10个整数:");
    for(i = 0; i < 10; i++){
        scanf("%d", &a[i]);
    }
    
    printf("数组中的元素为:");
    for(i = 0; i < 10; i++){
        printf("%d ", *(a+i));
    }
    printf("\n");
}

#include<stdio.h>

void main(void){
    int a[10];
    int i;
    int *p;
    
    printf("请输入10个整数:");
    for(i = 0; i < 10; i++){
        scanf("%d", &a[i]);
    }
    
    printf("数组中的元素为:");
    for(p = a; p < (a+10); p++){
        printf("%d ", *p);
    }

    printf("\n");
}

#include<stdio.h>

void main(void){
    int a[10];
    int i;
    int *p;
    
    printf("请输入10个整数:");
    for(i = 0; i < 10; i++){
        scanf("%d", &a[i]);
    }
    
    printf("数组中的元素为:");
    for(p = a,i=0; i<10; i++){
        printf("%d ", *p);

        p++;
    }

    printf("\n");
}

这个和下一个执行效率是相同的。C编译系统是将a[i]转换为*(a+i)处理的,即先计算地址元素。   比前两种方法快,用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的。  

用while循环输出

p = a;
while(p<a+10){
    printf("%d ", *p++);  

    //*p++ <=> *p(++)  先得到p所指向的变量的值(即*p),然后再使p+1=>p
}

p = a;
while(p<a+10){
    printf("%d ", *p);  
    p++;
}

eg10.7将数组a中n个整数按相反顺序存放

void inv(int *a, int n){
    int i;
    int tmp;
    
    for(i = 0; i <= (n-1)/2; i++){   
    //for(i = 0; i < (n-1)/2; i++){  不写=的话,最中间的那两个就没有交换 
        tmp = a[i];
        a[i] = a[n-1-i];
        a[n-1-i] = tmp;
    }
    printf("\n");
}

void inv(int *a, int n){
    int *p,*q;
    int tmp;
    
    for(p = a, q = a+n-1; p <= a+(n-1)/2; p++, q--){   
        tmp = *p;
        *p = *q;
        *q = tmp;
    }
    printf("\n");
}

两种方法得到的结果相同,都可以实现题目要求功能  

有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:

void inv(int *a, int n){

    ...

}

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    ...    
    inv(a, 5);
 }

void inv(int a[], int n){

    ...

}

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    ...    
    inv(a, 5);
 }

void inv(int *a, int n){

    ...

}

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    int  *p = a;

    ...    
    inv(p, 5);


 }

void inv(int a[], int n){

    ...

}

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    int  *p = a;

    ...    
    inv(p, 5);


 }

用数组名作函数的参数

fun(int arr[], int n)     <=>      fun(int *arr, int n)    

实参数组名代表该数组首元素的地址,而形参是用来接收从实参传递过来的数组首元素地址的。因此,形参应该是一个指针变量(只有指针变量才能存放地址)。实际上,C编译都是将形参数组名作为指针变量来处理的。

在该函数被调用时,系统会建立一个指针变量arr,用来存放从主调函数传递过来的实参数组首元素的地址。

eg10.11用指针变量输出二维数组元素的值

#include<stdio.h>

void main(void){
    int a[3][4] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23};
    int *p;
    
    for(p=a[0]; p < a[0]+12; p++){
        if((p-a[0])%4 == 0){   
            printf("\n");
        }
        printf("%4d", *p);
    }
    printf("\n");
}

#include<stdio.h>

void main(void){
     int a[3][4] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23};
    int *p;
    
    for(p=a[0]; p < a[0]+12; p++){
        if((p+1-a[0])%4 == 0){    
            printf("\n");
        }
        printf("%4d", *p);
    }
    printf("\n");
}

#include<stdio.h>

void main(void){
    int a[3][4] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23};
    int *p;
    
    for(p=a[0]; p < a[0]+12; p++){
        printf("%4d", *p);      
        if((p+1-a[0])%4 == 0){   
            printf("\n");
        }    
    }
    printf("\n");
}

eg10.12输出二维数组任一行任一列元素的值

#include<stdio.h>

void main(void){
    int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
    int *p;
    int i,j;
    
    p = a[0];
    printf("请输入行号和列号:");
    scanf("%d%d", &i, &j);
    printf("a[%d][%d]=%d\n", i, j, *(p+i*4+j));
}

#include<stdio.h>

void main(void){
    int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
    int (*p)[4];
    int i,j;
    
    p = a;
    printf("请输入行号和列号:");
    scanf("%d%d", &i, &j);
    printf("a[%d][%d]=%d\n", i, j, *(*(p+i)+j));
}

p是指向整型数据的,p+1所指向的元素是p所指向的元素的下一元素。 p是指向一个包含m个元素的一维数组,p的增值以一维数组的长度为单位。
 

eg10.13有3个学生各学4门课,计算总平均分数以及第n个学生的成绩

多维数组名做函数参数

1.用指向变量的指针变量

2.用指向一维数组的指针变量

float average(float *s, int n){
  ......
}

void main(void){
    float score[3][4] = {......};
    ......

   av = average(*score, 12);   //*score, 即score[0],即&score[0][0]
    ......
}

void search(float (*s)[4], int n){
    ......
}

void main(void){
    float score[3][4] = {......};
    ......
    search(score, ...);
    ......
}

eg10.15定义一个字符数组,对它初始化,然后输出该字符串

eg10.16定义字符指针

C语言中,可以用两种方法访问一个字符串

(1)用字符数组存放一个字符串 (2)用字符指针指向一个字符串

#include<stdio.h>

void main(void){
    char string[] = "I love China!";
    printf("%s\n", string);


void main(void){
    char *string = "I love China!";
    printf("%s\n", string);
}
 

char *string = "I love China!";      <=>

char *string;   string = "I love China!";

  把"I love China!"的第一个字符的地址赋给指针变量string,string只能指向一个字符变量

printf(“%s…”, …)与puts(…)只有一点点差别:

printf(“%s\n”, s);  <=>  puts(s);

puts()函数输出字符串后,自动换行。

%s是输出字符串时所用的格式符,在输出项中给出字符指针变量名string,则系统先输出它所指向的一个字符数据,然后自动使string加1,使之指向下一个字符,然后再输出一个字符……如此直到遇到字符串结束标志'\0'为止。

eg10.17将字符串a复制为字符串b

eg10.18用指针变量处理eg10.17问题

对字符串中字符的存取,可以用下标方法,也可以用指针方法。

7.15字符串拷贝 下标法 和2等价 指针方法
#include<stdio.h>
#include<string.h> 
 
void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    int index = -1; 
    int i;
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    index = strlen(str2);
    printf("字符串结束标志下标为:%d\n",index); 
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    for(i = 0; i < index+1; i++){    
        str1[i] = str2[i]; 
    }
 
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}
#include<stdio.h>
#include<string.h> 
 
void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    int i;
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    for(i = 0; str2[i] != '\0'; i++){    
        str1[i] = str2[i]; 
    }
    str1[i] = str2[i];    
 
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}
#include<stdio.h>
#include<string.h> 
 
void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    int i;
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    for(i = 0; *(str2+i) != '\0'; i++){    
        *(str1+i) = *(str2+i); 
    }
    *(str1+i) = '\0';   
 
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}
#include<stdio.h>
#include<string.h> 
 
void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    char *p1;
    char *p2; 
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    for(p1 = str1, p2 = str2; *p2 != '\0'; p1++, p2++){    
        *p1 = *p2; 
    }
    *p1 = '\0';    
 
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}

eg10.19用函数调用实现字符串的复制

将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法(即用字符数组名作参数),也可以用指向字符的指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。

归纳起来,作为函数参数,有以下几种情况:

实参:数组名 

形参:数组名

实参:字符指针变量

形参:数组名

实参:数组名

形参:字符指针变量

实参:字符指针变啦

形参:字符指针变啦

#include<stdio.h>
#include<string.h> 

void stringCopy(char from[], char to[]);

 void stringCopy(char from[], char to[]){
     int i = 0;
     
     while(from[i] != '\0'){
         to[i] = from[i];
         i++;
     }
     to[i] = '\0';
 }
 
void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    stringCopy(str2, str1);
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}

#include<stdio.h>
#include<string.h> 

void stringCopy(char from[], char to[]);

 void stringCopy(char from[], char to[]){
     int i = 0;
     
     while(from[i] != '\0'){
         to[i] = from[i];
         i++;
     }
     to[i] = '\0';
 }
 
void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    char *p1 = str1;
    char *p2 = str2;
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    stringCopy(p2, p1);
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}

#include<stdio.h>
#include<string.h> 

void stringCopy(char *from, char *to);

 void stringCopy(char *from, char *to){
     
     
     while(*from != '\0'){
         *to = *from;
         from++;
         to++;
     }
     *to = '\0';
 }
 
void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    stringCopy(str2, str1);
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}

#include<stdio.h>
#include<string.h> 

void stringCopy(char *from, char *to);

 void stringCopy(char *from, char *to){
     
     
     while(*from != '\0'){
         *to = *from;
         from++;
         to++;
     }
     *to = '\0';
 }

void main(void){
    char str1[80] = {0};
    char str2[80] = {0};
    char *p1 = str1;
    char *p2 = str2;
    
    printf("请输入第二串字符:"); 
    gets(str2);
 
    printf("拷贝前的第一串字符为:");
    puts(str1);
        
    stringCopy(p2, p1);
    printf("拷贝后的第一串字符为:");
    puts(str1);
 
}

10.2(45)输入3个字符串,按由小到大的顺序输出

void swap(char *a, char *b){
      //char *tmp = a;
    
    *tmp = *a;
    *a = *b;
    *b = *tmp;
void swap(char *a, char *b){
     char tmp[ARRAY_SIZE]; //char *tmp = a;
    
    *tmp = *a;
    *a = *b;
    *b = *tmp;
这样子的函数其实只能交换每个字符串的第一个字符,但tmp的初值实在是无处安放,要是真的想交换第一个字符,就要先让tmp指向这三个之外的一个字符串,这样才可以达到目的。 定义tmp为一个字符数组,而不是指针变量,就可以了
void swap(char *a, char *b){
     char tmp[ARRAY_SIZE]; //char *tmp;
    
    *tmp = *a;
    *a = *b;
    *b = *tmp;   
void swap(char *a, char *b){
    char tmp[ARRAY_SIZE]; //char *tmp;
    
    tmp = a;
    a = b;
    b = tmp;
void swap(char *a, char *b){
    char tmp[ARRAY_SIZE]; //char *tmp;
    
    strcpy(tmp, a);
    strcpy(a, b);
    strcpy(b, tmp);
这样只能交换每个字符串的第一个字符 这样子交换的是指针的值,但是指针的值并不会通过函数传递出来,我们的目的是通过指针的指向改变内存中字符串的值。 这才是正解

10.12用指针数组处理上一题目,字符串不等长

优先级:()>[]>*

用指针数组作为参数

void stringSort(char *a[], int row);

void main(void){

    int i = 0;
    char a[10][20];
    char *p[10];
    
    for(i = 0; i < 10; i++){
        p[i] = a[i];
    }
    .......

    stringSort(p, 10);
    .......

}

eg10.22求a和b中的最大值(指向函数的指针)

函数的调用

可以通过函数名调用 也可以通过函数指针调用(即用指向函数的指针变量调用)

#include<stdio.h>

int max(int a, int b);

int max(int a, int b){
    return (a>b) ? a : b;
}

void main(void){
    int a, b, c;
    
    printf("请输入两个整数:");
    scanf("%d%d", &a, &b);
    
    c = max(a, b);
    printf("%d和%d中的最大值是:%d\n", a, b, c); 
}

#include<stdio.h>

int max(int a, int b);

int max(int a, int b){
    return (a>b) ? a : b;
}

void main(void){
    int a, b, c;
    int (*p)(int, int);     
    
    printf("请输入两个整数:");
    scanf("%d%d", &a, &b);
    
    p = max;
    c = (*p)(a, b);

    printf("%d和%d中的最大值是:%d\n", a, b, c); 
}

 

int (*p)(int, int); //定义

用来定义p是一个指向函数的指针变量,它不是固定指向哪一个函数的,而只是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址的。在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。在一个程序中,一个指针变量可以先后指向同类型的不同函数。

*p两侧的括号不可省略,表示p先与*结合,是指针变量,然后再与后面的()结合,表示此指针变量指向函数。

 p = max;      //赋值

在给函数指针变量赋值时,只需给出函数名而不必给出参数,因为这是将函数的地址赋给p,不涉及实参与形参的问题。

将函数max的人口地址赋给指针变量p

这时,p是指向函数max的指针变量

调用*p就是调用max函数

(注)p是指向函数的指针变量,它只能指向函数的入口地址而不可能指向函数中的某一条指令处。对指向函数的指针变量,像p+n、p++、p--等运算都是无意义的。

c = (*p)(a, b);     //调用

<=>c = max(a, b); 

用函数指针变量调用函数时,只需将(*p)代替函数名即可,在(*p)后面的括号中根据需要写上实参。

eg10.23有一个函数process,在调用它的时候,每次实现不同的功能(用指向函数的指针作函数参数)

int max(int a, int b);
int min(int a, int b);
int add(int a, int b);
void process(int (*p)(int, int), int x, int y);

void main(void){
    int a, b, c;
     
    printf("请输入两个整数:");
    scanf("%d%d", &a, &b);
    
    printf("%d和%d中的最大值是:", a, b); 
    process(max, a, b);
    
    printf("%d和%d中的最小值是:", a, b); 
    process(min, a, b);
    
    printf("%d和%d之和是:", a, b); 
    process(add, a, b);

eg10.25对上例中的学生,找出其中不及格课程的学生及其学生号

//思考;返回的指针只能返回一个不及格的学生,万一有多个学生该怎么办?
//课本:在主函数中对各个学生进行循环,分别判断其是否及格 

10.20用指向指针的指针的方法对5个字符串排序并输出

指针数组的元素指向字符串

(1)

char *a[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
    char **p = a;

(2)

char *a[ ] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
    char **p = a;

(3)用户输入字符串    

    char str[5][20];
    char *pstr[5];
    char **p;
    int i;
    
    // pstr = str;   这样不对
    for(i = 0; i < 5; i++){
        pstr[i] = str[i];
    } 
    p = pstr;
        
    printf("请输入5个字符串:\n");
    inputString(pstr,5);

10.21用指向指针的指针的方法对n个整数排序并输出

指针数组的元素指向整型数组

(1)

    int a[5] = {1, 3, 5, 7, 9};
    int *num[5] = {&a[0], &a[1], &a[2], &a[3], &a[4]};
    int **p;
    int i;
    
    p = num;
    for(i = 0; i < 5; i++){
        printf("%d ", **p);
        p++;
    }
    printf("\n");
(2)

    int a[10];
    int *pa[10] = {&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &a[8], &a[9]};
    int **p = pa;
    int i;
    
    printf("请输入10个整数:");
    for(i = 0; i < 10; i++){
        scanf("%d", &a[i]);
    }

 

猜你喜欢

转载自blog.csdn.net/weixin_42072280/article/details/85012169
今日推荐