算法竞赛入门经典_4.2_函数调用和参数_gcc调试器的使用

来源:互联网 发布:unity3d js教程 编辑:程序博客网 时间:2024/05/21 10:16
本节涉及到函数的调用,形参和实参等知识

下面是一个有问题的代码,聪明的你肯定知道哪里出了问题

#include <stdio.h>//有错误,不能进行实际的变量交换void swap(int a, int b){    int t = a;    a = b;    b = t;}

下面解释一下

第一步:    在main函数中,a = 3, b = 4,所以swap(a, b)等价于swap(3, 4), 而这里的参数3,4称为实参(实际参数)

第二步:    把实参赋值给函数中的形参a, b,swap函数中,a = 3, b =4

第三步:   形参a , b的值进行交换了,所以现在是a = 4, b =3;

注意:函数的形参和在该函数中定义的变量都称为该函数的局部变量(local variable),不同函数的局部变量是相互独立的,局部变量的存储空间是临时分配的,函数执行完毕时,

局部变量的空间将被释放。而全局变量(global variable),此变量在函数外声明,可以在任何时候,由任何函数访问。

  调用栈(Call stack)描述的是函数之间的调用关系,它由多个栈帧(stack frame)组成,每个栈帧对应着一个未运行完的函数。

下面我们使用gcc来调试我们的程序。

gcc swap.c -std=c99 -g

gcc命令是编译程序,-g告诉编译器生成调试信息,编译选项-std=c99 告诉编译器按c99标准来编译代码 

gdb a.exe

这就是输入以上命令会出现的情况,我们输入 l可以查看源码

(gdb) l3       //4       void swap(int a, int b)5       {6               int t = a;7               a = b;8               b = t;9       }10      int main()11      {12              int a = 3, b = 4;(gdb)
12              int a = 3, b = 4;(gdb) Quit (expect signal SIGINT when the program is resumed)(gdb) b 9Breakpoint 1 at 0x4013cf: file 4.2.1.c, line 9.(gdb) rStarting program: H:\\2\\4.2.1_\a.exe[New Thread 5904.0xb50]Breakpoint 1, swap (a=4, b=3) at 4.2.1.c:99       }(gdb)

上面通过b 9来在程序的第9行增加一个断点,r运行该程序,然后显示了a和b的值,现在a = 4, b  = 3

(gdb) bt#0  swap (a=4, b=3) at 4.2.1.c:9#1  0x00401403 in main () at 4.2.1.c:13(gdb) p a$1 = 4(gdb) p b$2 = 3(gdb) up#1  0x00401403 in main () at 4.2.1.c:1313              swap(a, b);(gdb) p a$3 = 3(gdb) p b$4 = 4(gdb) qA debugging session is active.        Inferior 1 [process 5904] will be killed.Quit anyway? (y or n) y

  我们使用bt命令调用栈中包含的两个栈帧#0,#1,#0是当前栈帧swap函数,#1是上一栈帧main函数

   p命令可以打印变量的值,up命令可以选择上一个栈帧q退出gdb,在这里可以很清楚的看到

在main函数中的变量值是没有改变的,还是a = 3,b = 4.下面给出正确代码:

#include <stdio.h>void swap(int *a, int *b){    int t = *a;    *a = *b;    *b = t;}int main(){    int a = 3, b = 4;    swap(&a, &b);    printf("%d %d\n", a, b);    getchar();    return 0;}

再次使用以上方式将上面代码进行调试

(gdb) b 7Breakpoint 1 at 0x4013d7: file swap.c, line 7.(gdb) rStarting program: H:\\2\\a.exe[New Thread 2468.0x1110]Breakpoint 1, swap (a=0x28ff1c, b=0x28ff18) at swap.c:77       }(gdb) bt#0  swap (a=0x28ff1c, b=0x28ff18) at swap.c:7#1  0x0040140b in main () at swap.c:11(gdb) p a$1 = (int *) 0x28ff1c(gdb) p b$2 = (int *) 0x28ff18(gdb) p *a$3 = 4(gdb) p *b$4 = 3(gdb) up#1  0x0040140b in main () at swap.c:1111              swap(&a, &b);(gdb) p a$5 = 4(gdb) p b$6 = 3(gdb) p &a$7 = (int *) 0x28ff1c(gdb) p &b$8 = (int *) 0x28ff18(gdb)

注意: 千万不能滥用指针,这不仅会把自己搞糊涂,还会让程序产生各种奇怪的错误。

  •   数组作为参数
    • 代码
      #include <stdio.h>int sum(int *a, int n){    int ans = 0;    for(int i = 0; i < n; i++)        ans+=a[i];    return ans;}int main(){    int a[100],i;    int index = 0;    while( scanf("%d", &i) == 1 && i)    {        a[index++] = i;    }    int ans = sum(a, index);//此时Index刚好是数组个数    printf("%d\n", ans);    return 0;}

      注:不能直接将a[]传递过来,因为int a[]等价于int *a,在只知道地址信息的情况下,是无法知道数组里有多少个元素的,所以要加一个数组参数。

  效果

  •   古老的密码问题
    • Ancient Cipher
      给定两个长度相同且不超过100的字符串,判断是否能把其中一个字符串的各个字母重排,
      然后对26个字母做一个一一映射,使得两个字符串相同,例如,JWPUDJSTVP重排后可以得到
      WJDUPSJPVT,然后把每个字母映射到它的前一个字母(B->A...),得到VICTORIOUS,
      输入两个字符串,
      输出YES 或者NO

    • 代码
      //古老的密码 2017-8-26#include<stdio.h>#include<stdlib.h> // qsort#include<string.h> // strlenint cmp(const void *a, const void *b){    return *(int *)a - *(int *)b;}int main(){    char s1[200], s2[200];    while(scanf("%s%s", s1, s2) == 2){        int n = strlen(s1);        int cnt1[26] = {0}, cnt2[26] = {0};        for(int i = 0; i < n; i++)            cnt1[s1[i] - 'A']++;        for(int j = 0; j < n; j++)            cnt2[s2[j] - 'A']++;        qsort(cnt1, 26, sizeof(int), cmp);        qsort(cnt2, 26, sizeof(int), cmp);        int ok = 1;        for(int k = 0; k < 26; k++)            if(cnt1[k] != cnt2[k]) ok = 0;        if(ok) printf("YES\n");else printf("NO\n");    }        return 0;}

      分析:既然可以重排,则每个字母的位置并不重要,重要的是每个字母出现的次数。这样可以先统计出两个字符串中的各个字母的出现次数,得到两个数组cnt1[26],cnt2[26],只要两个数组排序之后的结果相同,输入的两个串就可以通过重排和一一映射变得相同。

    • 注:在c语言中stdlib.h中有个叫qsort的库函数,实现了著名的快速排序算法。
      •   void qsort(void *base, size_t num, size_t size, int (*comparator)(const void *, const void *));
      • 这里的const void *,他可以通过强制类型转化变成任意类型的指针。

今天就到这吧!

能力决定价值!