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


      本节涉及到函数的调用,形参和实参等知识

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

    #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) l
    3       //
    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 9
    Breakpoint 1 at 0x4013cf: file 4.2.1.c, line 9.
    (gdb) r
    Starting program: H:\2\4.2.1_a.exe
    [New Thread 5904.0xb50]
    
    Breakpoint 1, swap (a=4, b=3) at 4.2.1.c:9
    9       }
    (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:13
    13              swap(a, b);
    (gdb) p a
    $3 = 3
    (gdb) p b
    $4 = 4
    (gdb) q
    A 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
    ", a, b);
        getchar();
        return 0;
    }

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

    (gdb) b 7
    Breakpoint 1 at 0x4013d7: file swap.c, line 7.
    (gdb) r
    Starting program: H:\2\a.exe
    [New Thread 2468.0x1110]
    
    Breakpoint 1, swap (a=0x28ff1c, b=0x28ff18) at swap.c:7
    7       }
    (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:11
    11              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
        ", 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> // strlen
        
        int 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
        ");else printf("NO
        ");
            }
            
            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 *,他可以通过强制类型转化变成任意类型的指针。

    今天就到这吧!

    能力决定价值!

  • 相关阅读:
    POJ-3026 Borg Maze(BFS+最小生成树)
    HDU-1875 畅通工程再续(最小生成树+判断是否存在)
    POJ-1679 The Unique MST(次小生成树、判断最小生成树是否唯一)
    POJ-1751 Highways(最小生成树消边+输出边)
    POJ-2349 Arctic Network(最小生成树+减免路径)
    POJ-2031 Building a Space Station (球的最小生成树)
    八皇后问题 2n皇后问题
    约瑟夫环
    判断1/N是否为无限小数
    HDU-4004 The Frog's Games (分治)
  • 原文地址:https://www.cnblogs.com/ncgds/p/7426049.html
Copyright © 2020-2023  润新知