算法竞赛入门经典_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\n", 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\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> // 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\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 *,他可以通过强制类型转化变成任意类型的指针。

今天就到这吧!

能力决定价值!

posted @ 2017-08-25 00:51  easydots  阅读(331)  评论(0)    收藏  举报