<<c程序设计语言>>-4- 函数与程序结构

来源:互联网 发布:2015网络搞笑歌曲 编辑:程序博客网 时间:2024/05/29 15:08

4-1 函数的基本知识

在编写程序的时候,尽量将程序流程写出来,然后将里面的功能实现为一个个小函数

 

设计并编写一个程序,用于把输入中包含特定的“模式”或字符串的各行打印出来(这是U N I X程序g r e p的特殊情况)。例如,对如下一组文本行查找包含字母字符串“ o u l d”的
行:
Ah Love! could you and I with Fate conspire
To grasp this sorry Scheme of Things entire,
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desire!
可以产生如下输出:
Ah Love! could you and I with Fate conspire
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desire!

这个程序段可以清楚地分成三部分:
      while ( 还有未处理的行 )
              if ( 该行包含指定的模式 )
                      打印该行

#include <stdio.h>
#define MAXLINE 100 /*最大输入行长度 */
int getline (char line[ ], int max);
int strindex(char source[ ], char searchfor[ ]);
char pattern[] = "ould"; /*要查找的模式 */
/* 找出所有与模式匹配的行 */
      int main ( )
      {
           char line[MAXLINE];
           int found = 0;
           while ( getline(line, MAXLINE) > 0 )
                    if ( strindex(line, pattern) >= 0 ) {
                           printf( "%s", line);
                           found++;
                    }
           return found;
       }
       /* getline:取一行放到s中,并返回该行的长度 */
      int getline(char s[ ], int lim)
      {
         int c, i;
         i = 0;
         while ( -- lim > 0&& ( c = getchar() ) != EOF && c != '\n' )

                      s[i++] = c;
         if (c == '\n' )
                      s[i++] = c;
        s[i] = '\0';
       return i;
     }
    /* strindex:返回t在s中的位置,若未找到则返回-1 */
     int strindex(char s[], char t[] )
     {
           int i, j, k;
           for ( i = 0; s[i] != '\0'; i++ ) {
                  for ( j =i, k = 0; t[k] != '\0' && s[j] ==t[k]; j++, k++ )
                              ;       
                  if ( k > 0 && t[k] == '\0' )
                          return i;
           }
           return -1;
     }

方法二:

         int grep(char str[],char * s){
                int i=0,j=0,k=0;
                printf("enter grep\n");
               while(i<strlen(str)){
                       k=0;

                   while(str[i]==s[j]&&i<strlen(str)&&j<strlen(s)){
                                  i++;
                                  j++;
                    }
                  if(j==(strlen(s))) return i-j;
                  i++;
                   j=0;

               }
               if(j==0) return -1;
         }

dummy( ) { }
这个函数什么也不做、什么也不返回。像这种什么也不做的函数有时很有用,它可以在程序开
发期间用做占位符。如果在函数定义中省略了返回类型,则缺省为 i n t。

练习4-1     编写函数strindex(s, t),它返回字符串t在s中最右边出现的位置。
如果s中不包含t,则返回-1。

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

int strindex(char s[],char t[]);

int main()
{
  int index;
  char s[100],t[100],c;
 

while(1){

  printf("please choose the type\n");
  printf("0:go on   1:exit\n");
  if((c=getchar())=='0'){
      printf("please input the str\n");
      scanf("%s",s);
      printf("please input the substr\n");
      scanf("%s",t);
     index = strindex(s,t);
     if(index >=0)
         printf("the first right pos is %d\n",index);
    else
        printf("not found\n");
    c=getchar();

  }
  else if(c=='1') break;
  else printf("please input the right choose\n");
 }
  return 0;
}

int strindex(char s[],char t[]){
  int i,j,k;
  i=strlen(s)-1;
  j=strlen(t)-1;
  printf("the i is%d\n",i);
  printf("the j is %d\n",j);
  for(i;i>=0;i--){
      for(j,k=0;k<=j && s[i-k]==t[j-k];k++)
               ;
      if(k==strlen(t)) return i-k+1;

   }
  return -1;
}

 

4.2 返回非整数的返回值

 例如对于atof()函数返回值类型为double,调用函数必须知道 a t o f函数返回的是非整数值,一种方法是在调用函数中显式说明 a t o f函数.

例子:

基本计算器程序(仅适用于支票簿计算)中给出了这个说明,程序一次读入一行数(一行只放一个数,数的前面可能有一个正负号),并
把它们加在一起,在每一次输入后把这些数的连续和打印出来:

在函数里面可以这样定义:double sum, atof ( char [ ] );
表明s u m是一个d o u b l e类型的变量,a t o f是一个具有char[ ]类型的变元且返回值类型为d o u b l e的函数。

 

由于atof函数的返回值类型不是int,因此该函数必须声明返回值的类型。
其次,调用函数必须知道atof函数返回的是非整型值。可以在调用函数中显示声明atof函数。
#include <stdio.h>
#define MAXLINE 100
main()
{
     double sum, atof(char []);
     char line[MAXLINE];
     int getline(char line[], int max);
     
     sum = 0;
     while (getline(line, MAXLINE) > 0)
          printf("\t%g\n", sum += atof(line));
     return 0;
}

函数atof的声明与定义必须一致。如果atof函数与调用它的main函数放在同一源文件中,
并且类型不一致,编译器会检测到该错误。如果atof函数式单独编译的,这种不匹配的
错误就无法检测出来。

如果没有函数原型,则函数将在第一次出现的表达式中被隐式声明。函数的返回值将被
假定为int类型,但上下文并不对其参数作任何假设。

练习4-2     对atof函数进行扩充,使它可以处理形如123.45e-6的科学表示法。其中,浮点数
后面可能会紧跟一个e或E以及一个指数(可能有正负号)。
答:
#include <stdio.h>
#include <ctype.h>

/* atof: convert string s to double */
double atof(char s[])
{
     double val, power, epow;
     int i, sign, j, esign;
     for (i = 0; isspace(s[i]); i++) /* skip white space */
          ;
     // 1. Sign
     sign = (s[i] == '-') ? -1 : 1;
     if (s[i] == '+' || s[i] == '-')
          i++;
     // 2. Integer part
     for (val = 0.0; isdigit(s[i]); i++)
          val = 10.0 * val + (s[i] - '0');
     // 3. Float part
     if (s[i] == '.')
          i++;
     for (power = 1.0; isdigit(s[i]); i++, power *= 10)
          val = 10.0 * val + (s[i] - '0');
     // 4. E part
     if (s[i] == 'e' || s[i] == 'E')
          i++;
     esign = (s[i] == '-') ? -1 : 1;
     if (s[i] == '+' || s[i] == '-')
          i++;
     for (j = 0; isdigit(s[i]); i++) {
          j = 10 * j + (s[i] - '0');
     }
     for (epow = 1.0; j > 0; j--) {
          if (esign == -1)
               epow /= 10;
          else
               epow *= 10;
     }
     return sign * val / power * epow;
}
main()
{
     printf("%.2lf\n", atof("4.85E3"));
     printf("%.2lf\n", atof("3.6"));
     printf("%.2lf\n", atof("3.6e5"));
     printf("%.2lf\n", atof("-3.6e5"));
     printf("%.8lf\n", atof("-3.6e-5"));
     printf("%.2lf\n", atof("3.6e12"));
}

 

 

4.3 外部 变量

i n t e r n a l用于描述定义在函数内部的函数变元以及变量。外部变量在函数外面定义,故可以在许多函数中使用。

由于外部变量是可以全局访问的,这就为在函数之间交换数据提供了一种可以代替函数变
元与返回值的方法。任何函数都可以用名字来访问外部变量,只要这个名字已在某个地方做了
说明。

 

如果要在函数之间共享大量的变量,那么使用外部变量要比使用一个长长的变元表更方便、
然而,这样使用必须充分小心,因为这可能破坏程序的结构,并且会使程序在各个函数之间产生大量的数据联系。

 

外部变量的用途还表现在它们比内部变量有更大的作用域和更长的生存期。自动变量只能
在函数内部使用,当其所在函数被调用时开始存在,当函数退出时消失。而外部变量是永久存
在的,它们的值在从一次函数调用到下一次函数调用之间保持不变。因此,如果两个函数必须
共享某些数据,而这两个函数都互不调用对方,那么最为方便的是,把这些共享数据作成外部
变量,而不是作为变元来传递

 

 

4.4 作用域规则

 

 

 

 

 

 

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

编写程序中遇到的问题:

1.  #include<malloc.h>

     char *str;

     str=malloc(sizeof(char)*100);

     scanf("%s",&str);

2. 如何键盘输入获得n行?

    while((getline(str)>0)  ||while(getline(str)>1)  ===》这样可以循环地进入getline,那么就在getline()通过\n来将字符串分割为一行一行地放入str[]进行处理

    在getline()里面, while((c=getchar())!=EOF && c!='\n') || while((c=getchar())!='*' && c!='\n')   str[I++]=c;  if (c=='\n')  str[I++]='\n';   str[I]='\0'; return I;

3. 对于字符串数组,对于空数组,char s[100] , strlen(s)是0 ---strlen()求的是数组中元素 个数,包括'\0'   #include<string.h>

4.  对于lvalue required as decrement operand 这个错误,while(--lim>0), 为什么呢?因为我这里#define lim 100 , lim其实是一个常量, 而--需要的是一个变量

     所以在函数中,可以将lim传递给函数参数,这样while(--ingrement>0)就可以了

5. 对于error: expected declaration or statement at end of input, 一般是括号不匹配
6.对于Segmentation fault (core dumped), 使用gcc -o main -g test.c(加上-g参数就可以使用gdb来调试了), 这样gdb main, 输入r运行,就可以直接定位到出错的那一行,

    一般是数组越界 或者 访问了系统数据区(比如往系统保护区域写数据,比如给指针赋0地址)

7.注意使用数组的时候,不要越界,在while循环或者if判断的条件中记得判断是否str[I]==‘\0' 或者len <strlen(str),防止出现段错误

8. 什么是补码?

    8.1 时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是0~2^(n)-1,模=2^(n)。

    8.2 “模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算。

例如:假设当前时针指向10点,而准确时间是6点,调整时间可有以下两种拨法:一种是倒拨4小时,即:10-4=6;另一种是顺拨8小时:10+8=12+6=6
在以12模的系统中,加8和减4效果是一样的。
    8.3 对于计算机,其概念和方法完全一样。n位计算机,设n=8, 所能表示的最大数是11111111,若再加1称为100000000(9位),但因只有8位,最高位1自然丢失。又回了00000000,所以8位二进制系统的模为2^8。在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。把补数用到计算机对数的处理上,就是补码。
    8.4 正数的补码=源码,  负数的补码==除去符号位外,其他位取反,然后+1, 补码的补码=源码
    8.5
  1. 自身逻辑意义的完整性
    补码这个编码方案要解决的是如何在机器中表示负数,其本质意义为用一个正数来表示这个正数对应的负数。所谓-20的补码是指:如何在机器中用补码形式表示-20。具体过程是这样的:将20的二进制形式直接写出00010100,然后所有位取反变成11101011,再加1变成了11101100。最简单的补码转换方式,不必去理会转换过程中的符号位,只关注转换前和最终转换后的符号位就行了。
    那么对11101100求出其补码又具有什么现实含义呢?对一个数求补,逻辑过程是对这个数的所有的二进制位按位取反再加1。现实含义是求出这个数对应的负数形式。对11101100求补就是求出这个数对应的负数的形式,直接操作下11101100,先所有位取反00010011,再加上1就成了00010100。对11101100求出其补码的含义:11101100按照现行补码码制表示的有符号数是-20,对于-20求补就是求出其对应的负数-(-20),现实中-(-20)是+20,那么求补运算的结果符合现实情况吗,00010100转换成有符号数正是+20,这就说明了补码自身逻辑意义是完整的,是不会自相矛盾的。
  2. 最后,补码的总前提是机器数,不要忘了机器数的符号位含义,最高位为0表示正数,最高位为1表示负数,而最高位是指机器字长的最左边一位。字节数100B,最高位为00000100中的最左边的0。

9. 使用scanf("%s",str)给数组str[]赋值,最后并没有自动添加'\0'

10.  10的33次方,可以直接用pow(10,33),也可以用循环,for(j=0,j<33,j++)  v/=10;

   

0 0
原创粉丝点击