读书笔记:C程序设计语言,第二章:知识要点 和 课后题全解

这一章覆盖下面几个问题

  1. Variables and constants are the basic data objects manipulated in a program.
  2. Declarations list the variables to be used, and state what type they have and perhaps what their initial values are. Operators specify what is to
  3. be done to them. 
  4. Expressions combine variables and constants to produce new values. 
  5. The type of an object determines the set of values it can have and what operations can be performed on it. 
ANSI标准对语言的修改:
  1. 整形包括 signed和unsinged两种
  2. 支持枚举
  3. 字符串常量可以在编译时链接
  4. 对像声明为const,即常量则其值不能修改

2.1 变量名

  1. 变量名由数字和字母组成
  2. 第一个字符必须为字母
  3. 下划线认为是"_"字母,一般用于将长变量的命名,是一种习惯,但非必须。库列程(library routines)通常使用这样的形式,我们注意保证没有冲突。
  4. 区分大小写,关键字的字符全是校写
  5. 习惯中,变量使用小写,符号常量名全部用大写
  6. 变量名的选择尽量从字面考虑其用途。
  7. 局部变量一般将短,外部变量稍长写比较好。

2.2 数据类型和长度

除char、int、float、double外,short和long可以作限定符修饰整形。
当用short和long修饰int时,int可以省略
int表示特定机器中整数的自然长度,一般为16位或者32位。short至少为16位,long至少为32位。
unsigned表示正值或者0,signed表示则可以表示负数
值的范围和其占的bit有关,char就占1个字节,8个bit,所以其范围为2^8,unsigned下0-255,signed:-128-127。
类型长度的定义的符号常量和其他与机器和编译器相关的属性可以在找到。

练习题

Exercise 2-1. Write a program to determine the ranges of char, short, int, and long variables, both signed and unsigned, by printing appropriate values from standard headers and by direct computation. Harder if you compute them: determine the ranges of the various floating-point types.
答:
code:
#include 
#include 

int
main ()
{

  printf("Size of Char %d\n", CHAR_BIT);
  printf("Size of Char Max %d\n", CHAR_MAX);
  printf("Size of Char Min %d\n", CHAR_MIN);
  printf("Size of int min %d\n", INT_MIN);
  printf("Size of int max %d\n", INT_MAX);
  printf("Size of long min %ld\n", LONG_MIN);       /* RB */
  printf("Size of long max %ld\n", LONG_MAX);       /* RB */
  printf("Size of short min %d\n", SHRT_MIN);
  printf("Size of short max %d\n", SHRT_MAX);
  printf("Size of unsigned char %u\n", UCHAR_MAX);  /* SF */
  printf("Size of unsigned long %lu\n", ULONG_MAX); /* RB */
  printf("Size of unsigned int %u\n", UINT_MAX);    /* RB */
  printf("Size of unsigned short %u\n", USHRT_MAX); /* SF */

  return 0;
}
结果是非常有用的,输出如下:
Size of Char 8
Size of Char Max 127
Size of Char Min -128
Size of int min -2147483648
Size of int max 2147483647
Size of long min -2147483648//对半分的话,由于max多一个0,所以max要少一,最后一位7
Size of long max 2147483647
Size of short min -32768
Size of short max 32767
Size of unsigned char 255
Size of unsigned long 4294967295//(2^32-1)
Size of unsigned int 4294967295
Size of unsigned short 65535

2.3常量


  1. 1234属于整数常量int型
  2. long型的常量用L或l结尾
  3. 无符号的常量会以u、U结尾,ul、UL就是上面两者的结合
  4. 浮点可以包含一个小数或者指数
  5. 没有后缀的浮点型为double
  6. float的后缀为f、F
  7. 后缀为l、L的浮点型表示double类型
  8. 前缀为0为八进制,前缀为0x或0X为十六进制,也可以使用L或者U作为后缀
  9. 一个字符常量是一个整数,将其放入单引号,如‘x’,储存对应的ASCII中的数值。如‘0’的值为48
  10. 转义字符看起里像两个,其实是一个
  11. ‘\ooo’表示1到3位的8进制数字
  12. '\xhh'表示1个或者多个十六进制的数字。可将:
  13. #define VTAB ‘\013’表示为:#define VTAB ‘\xb’
  14. #define VTAB ‘\007’表示为:#define VTAB ‘\x7’
  15. 字符常量'\0'表示的值为0的字符,也是空字符
  16. 常量表达式,是仅仅包含常量的表达式。在编译时求值,而非运行中
  17. #define MAXLINE 1000
  18. char line[MAXLINE+1]
  19. 字符串常量是由双引号引起来的多个字符组成的字符序列:“I am a string”
  20. 技术的角度来看,字符串常量就是字符串数组,并且使用‘\0‘来作为结尾。这样数组导致比实际的字符串多一个。所以,c对字符串常量的大小没限制,但必须找到\0来确定字符串大小。标准函数的strlen(s)返回字串传s的长度,长度不包括'\0'代码如下,但是暂时有错,我也没想通错在哪里:
    #include 
    #include 
    
    /* strlen: return length of s */
    int strlen1(char s[])
    {
     int i;
     while (s[i] != '\0')
      ++i;
     return i;
    }
    intmain (){ char str[]="what's your name"; int length=strlen1(str); printf("%d",length);}
     
  21. 请注意'x'和“x”的区别,“x”还的末尾还有‘\0’
  22. 枚举常量:一个常量整型值的列表。如:enum boolean{NO,YES};默认情况下,第一个NO为0,第二个YES为1.可以指定部分,也可以指定全部。也可以用其他进制来指定,enum escapes { BELL = '\a'};这样的枚举的单引号,我认为是取了ASCII里面的值。enum months { JAN = 1, FEB},这一年个FEB会在前一个指定了的数值上加1。
  23. 枚举中名字必须不同,不同名字的值额可以相同
  24. 枚举比#define方便在值可以值可以自动生成。

2.4 声明

  1. 先声明,后使用
  2. 一次可声明多个变量
  3. 外部变量、静态变量初始化为0,没有显式初始化的值未被定义,也就是无效值
  4. 由const修饰后,表示其值,不能被修改。对数组而言,就是所有值都不能被修改。如果配合数组的参数,即长度,就可以限定数组长度

2.5 算术运行符

  1. 取模运算符不能用于float和double型
  2. 整数除法截取的方向和去模运算结果的符号取决于机器的实现,与处理上溢和下溢是一样的

2.6 关系运算符与逻辑运行符

  1. 逻辑运算符:&&和||,从左往右求值,一旦知道真假立刻停止
  2. &&优先级高于||
  3. 关系与逻辑运算符,如果为真,则求出的值为1,如果为假,求出的值为0

练习题

Exercise 2-2. Write a loop equivalent to the for loop above without using && or ||.
答:
需要转换是:
  for(i=0; i
solution I,code:
#include 

#define MAX_STRING_LENGTH 100

int main(void)
{
	/*
	for (i = 0; i < lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
		s[i] = c;
	*/

	int i = 0,
		lim = MAX_STRING_LENGTH,
		c;
	char s[MAX_STRING_LENGTH];

	while (i < (lim - 1))
	{
		c = getchar();

		if (c == EOF)
			break;
		else if (c == '\n')
			break;

		s[i++] = c;
	}

	s[i] = '\0';   /* terminate the string */

	return 0;
}

solution II,非常好玩的答案:
#include 

#define lim 80

int main()
{
        int i, c;
        char s[lim];

        /* There is a sequence point after the first operand of ?: */

        for(i=0; i

2.7 类型转换

  1. 自动转换把把``narrower''转为``wider'',为了不丢失信息
  2. 如果把int转为float,可能到导致信息丢失,编译器会给出警告信息
  3. 由于可以自动转换,所以char可以放心使用,如atoi的函数代码,可以这样做的因为是因为ASCII字符的对应的数组差距是递增的:
    /* atoi: convert s to integer */
    int atoi(char s[])
    {
    	int i, n;
    	n = 0;
    	for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
    		n = 10 * n + (s[i] - '0');
    	return n;
    }

    也可以由此产生大小写转换的例子:
    /* lower: convert c to lower case; ASCII only */
    int lower(int c)
    {
     if (c >= 'A' && c <= 'Z')
      return c + 'a' - 'A';
     else
      return c;
    }


  4. 标准头文件定义了一组与字符集无关的测试和转换函数
  5. s[i] >= '0' && s[i] <= '9'还可以用于判断是否是数字的函数:isdigit(c)
  6. char转int时,其结果是,根据机子的不同是不同的。所以尽量指定char是signed还是unsigned
  7. d = c >= '0' && c <= '9' 这句话是说:c如果是数字那么,d就为1,否则d为0.
  8. 不是所有函数都会返回1或者0,isdigit()为真时返回非0
  9. 二元操作符(+、-涉及两个操作数),在没有指定unsigned情况,都是精度更大的方向转。但是float不会转向double。
  10. double计算特别费时,相比float能节省空间,和计算时间
  11. 含有unsigned情况,在自动转换时和机器有很大的关系
  12. 赋值也会引起自动转换,如:
    float x;
    int i;
    x=i;//float回被转为成int

  13. 将参数传递给函数时,也可能进行类型转换。无函数原型的情况,char和short转为int。float转为double。
  14. 强制转换:(类型名)表达式。如:
    int n
    sqrt((double)n)//强制转换n为double
  15. 然后一般情况下,参数是由函数原型声明的,所以函数调用时,会自动对参数进行强制转换。所以,上面的强制转换会被隐式的执行了。
  16. 使用了强制转换类型的rand函数,可以用于产生一个随机数,可以看出如果用相同的种子,会产生相同的随机,srand()函数用于设置种子:
    #include 
    unsigned long int next = 1;
    /* rand: return pseudo-random integer on 0..32767 */
    int rand(void)
    {
     next = next * 1103515245 + 12345;
     return (unsigned int)(next/65536) % 32768;
    }
    /* srand: set seed for rand()
     * 而使用同种子相同的数调用 srand()会导致相同的随机数序列被生成。
     * */
    void srand(unsigned int seed)
    {
     next = seed;
    }
    
    int main(){
     int i=rand();
     printf("%d",i);
    }

练习题

Exercise 2-3. Write a function htoi(s), which converts a string of hexadecimal digits (including an
optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A
through F.(a到f,和A到F)
/* Write the function htoi(s), which converts a string of hexadecimal
 * digits (including an optional 0x or 0X) into its equivalent integer
 * value. The allowable digits are 0 through 9, a through f, and
 * A through F.
 *
 * I've tried hard to restrict the solution code to use only what
 * has been presented in the book at this point (page 46). As a
 * result, the implementation may seem a little naive. Error
 * handling is a problem. I chose to adopt atoi's approach, and
 * return 0 on error. Not ideal, but the interface doesn't leave
 * me much choice.
 *
 * I've used unsigned int to keep the behaviour well-defined even
 * if overflow occurs. After all, the exercise calls for conversion
 * to 'an integer', and unsigned ints are integers!
 */

/* These two header files are only needed for the test driver */
#include 
#include 

/* Here's a helper function to get me around the problem of not
 * having strchr
 */

int hexalpha_to_int(int c)
{
  char hexalpha[] = "aAbBcCdDeEfF";
  int i;
  int answer = 0;

  for(i = 0; answer == 0 && hexalpha[i] != '\0'; i++)
  {
    if(hexalpha[i] == c)
    {
      answer = 10 + (i / 2);//原来是这样处理的哇
    }
  }

  return answer;
}

unsigned int htoi(const char s[])
{
  unsigned int answer = 0;
  int i = 0;
  int valid = 1;
  int hexit;

  if(s[i] == '0')//去掉0
  {
    ++i;
    if(s[i] == 'x' || s[i] == 'X')//去掉X
    {
      ++i;
    }
  }

  while(valid && s[i] != '\0')
  {
    answer = answer * 16;//因为先算得是最高位的,又是16进制的
    if(s[i] >= '0' && s[i] <= '9')
    {
      answer = answer + (s[i] - '0');
    }
    else
    {
      hexit = hexalpha_to_int(s[i]);//zog_c这个字符串在这里会被当作0来处理掉。
      if(hexit == 0)
      {
        valid = 0;
      }
      else
      {
        answer = answer + hexit;
      }
    }

    ++i;
  }

  if(!valid)
  {
    answer = 0;
  }

  return answer;
}

/* Solution finished. This bit's just a test driver, so
 * I've relaxed the rules on what's allowed.
 */

int main(void)
{
  char *endp = NULL;
  char *test[] =
  {
    "F00",
    "bar",
    "0100",
    "0x1",
    "0XA",
    "0X0C0BE",
    "abcdef",
    "123456",
    "0x123456",
    "deadbeef",
    "zog_c"
  };

  unsigned int result;
  unsigned int check;
  //size_t是标准C库中定义的,应为unsigned int,在64位系统中为 long unsigned int
  size_t numtests = sizeof test / sizeof test[0];//这里的test不用加()么。得到数组的长度

  size_t thistest;

  for(thistest = 0; thistest < numtests; thistest++)
  {
    result = htoi(test[thistest]);
    //strtoul是c标准提高的函数,16代表的将字符串转换为16进制的,当然也可以填写10
    check = (unsigned int)strtoul(test[thistest], &endp, 16);

    if((*endp != '\0' && result == 0) || result == check)
    {
      printf("Testing %s. Correct. %u\n", test[thistest], result);
    }
    else
    {
      printf("Testing %s. Incorrect. %u\n", test[thistest], result);
    }
  }

  return 0;
}

2.8 自增和自减运算符

  1. 前缀时,先加再使用其值。后缀时,先使用其值再自加
  2. 自增和自减运算符只能用于变量
    s[j++] = s[i];等于同于
    s[j] = s[i];
    j++;
  3. 这段代码很有意思,完成了删除字符串s中所有字符c:
    /* squeeze: delete all c from s */
    void squeeze(char s[], int c)
    {
    int i, j;
    	for (i = j = 0; s[i] != '\0'; i++)
    			if (s[i] != c)
    				s[j++] = s[i];
    	s[j] = '\0';
    }


  4. 标准函数:strcat(s,t),将字符串t连接到字符串s尾部。假定s有足够的空间。这并不是c标准库中的函数,只是完成了类似的功能。
    /* strcat: concatenate t to end of s; s must be big enough */
    void strcat(char s[], char t[])
    {
    	int i, j;
    	i = j = 0;
    	while (s[i] != '\0') /* find end of s */
    		i++;
    	while ((s[i++] = t[j++]) != '\0') /* copy t */
    		;
    }

练习题

Exercise 2-4. Write an alternative version of squeeze(s1,s2) that deletes each character in s1 that matches any character in the string s2.
答:
code:
/*
 * Exercise 2-4 Page 48
 *
 * Write an alternate version of squeeze(s1,s2) that deletes each
 * character in s1 that matches any character in the string s2.
 *
 */

/* squeeze2: delete all characters occurring in s2 from string s1. */

void squeeze2(char s1[], char s2[])
{
  int i, j, k;
  int instr2 = 0;

  for(i = j = 0; s1[i] != '\0'; i++)
  {
    instr2 = 0;
    for(k = 0; s2[k] != '\0' && !instr2; k++)
    {
      if(s2[k] == s1[i])
      {
        instr2 = 1;
      }
    }

    if(!instr2)
    {
      s1[j++] = s1[i];
    }
  }
  s1[j] = '\0';
}

/* test driver */

#include 
#include 

int main(void)
{
  char *leftstr[] =
  {
    "",
    "a",
    "antidisestablishmentarianism",
    "beautifications",
    "characteristically",
    "deterministically",
    "electroencephalography",
    "familiarisation",
    "gastrointestinal",
    "heterogeneousness",
    "incomprehensibility",
    "justifications",
    "knowledgeable",
    "lexicographically",
    "microarchitectures",
    "nondeterministically",
    "organizationally",
    "phenomenologically",
    "quantifications",
    "representationally",
    "straightforwardness",
    "telecommunications",
    "uncontrollability",
    "vulnerabilities",
    "wholeheartedly",
    "xylophonically", /* if there is such a word :-) */
    "youthfulness",
    "zoologically"
  };
  char *rightstr[] =
  {
    "",
    "a",
    "the",
    "quick",
    "brown",
    "dog",
    "jumps",
    "over",
    "lazy",
    "fox",
    "get",
    "rid",
    "of",
    "windows",
    "and",
    "install",
    "linux"
  };

  char buffer[32];
  size_t numlefts = sizeof leftstr / sizeof leftstr[0];
  size_t numrights = sizeof rightstr / sizeof rightstr[0];
  size_t left = 0;
  size_t right = 0;

  for(left = 0; left < numlefts; left++)
  {
    for(right = 0; right < numrights; right++)
    {
      strcpy(buffer, leftstr[left]);

      squeeze2(buffer, rightstr[right]);

      printf("[%s] - [%s] = [%s]\n", leftstr[left], rightstr[right], buffer);
    }
  }
  return 0;
}

Exercise 2-5. Write the function any(s1,s2), which returns the first location in a string s1 where any character from the string s2 occurs, or -1 if s1 contains no characters from s2. (The standard library function strpbrk does the same job but returns a pointer to the location.)
答:
下面是一种优秀的做法,时间复杂为O(n+m)(n和m分别是字符串的长度):
#include  /* for NULL */

int any(char *s1, char *s2)
{
        char array[256]; /* rjh comments
                          * (a) by making this char array[256] = {0}; the first loop becomes unnecessary.
                          * (b) for full ANSIness, #include , make the array unsigned char,
                          *     cast as required, and specify an array size of UCHAR_MAX + 1.
                          * (c) the return statements' (parentheses) are not required.
                          */
        int  i;
        if (s1 == NULL) {
                if (s2 == NULL) {
                        return(0);
                } else {
                        return(-1);
                }
        }

        for(i = 0; i < 256; i++) {
                array[i] = 0;
        }
        //核心所在:居然让s2的值去作为一个下标
        while(*s2 != '\0') {
                array[*s2] = 1;//比如当*s2=‘a’时,就代表了ASCII里面的97,array数组内的下标为97的位置改为了1
                s2++;
        }

        i = 0;
        while(s1[i] != '\0') {
                if (array[s1[i]] == 1) {
                        return(i);
                }
                i++;
        }
        return(-1);
}

/* test driver by Richard Heathfield */

/* We get a helpful boost for testing from the question text, because we are
 * told that the function's behaviour is identical to strpbrk except that it
 * returns a pointer instead of a position. We use this fact to validate the
 * function's correctness.
 */

#include 

int main(void)
{
  char *leftstr[] =
  {
    "",
    "a",
    "antidisestablishmentarianism",
    "beautifications",
    "characteristically",
    "deterministically",
    "electroencephalography",
    "familiarisation",
    "gastrointestinal",
    "heterogeneousness",
    "incomprehensibility",
    "justifications",
    "knowledgeable",
    "lexicographically",
    "microarchitectures",
    "nondeterministically",
    "organizationally",
    "phenomenologically",
    "quantifications",
    "representationally",
    "straightforwardness",
    "telecommunications",
    "uncontrollability",
    "vulnerabilities",
    "wholeheartedly",
    "xylophonically",
    "youthfulness",
    "zoologically"
  };
  char *rightstr[] =
  {
    "",
    "a",
    "the",
    "quick",
    "brown",
    "dog",
    "jumps",
    "over",
    "lazy",
    "fox",
    "get",
    "rid",
    "of",
    "windows",
    "and",
    "install",
    "linux"
  };

  size_t numlefts = sizeof leftstr / sizeof leftstr[0];
  size_t numrights = sizeof rightstr / sizeof rightstr[0];
  size_t left = 0;
  size_t right = 0;

  int passed = 0;
  int failed = 0;

  int pos = -1;
  char *ptr = NULL;

  for(left = 0; left < numlefts; left++)
  {
    for(right = 0; right < numrights; right++)
    {
      pos = any(leftstr[left], rightstr[right]);
      ptr = strpbrk(leftstr[left], rightstr[right]);

      if(-1 == pos)
      {
        if(ptr != NULL)
        {
          printf("Test %d/%d failed.\n", left, right);
          ++failed;
        }
        else
        {
          printf("Test %d/%d passed.\n", left, right);
          ++passed;
        }
      }
      else
      {
        if(ptr == NULL)
        {
          printf("Test %d/%d failed.\n", left, right);
          ++failed;
        }
        else
        {
          if(ptr - leftstr[left] == pos)
          {
            printf("Test %d/%d passed.\n", left, right);
            ++passed;
          }
          else
          {
            printf("Test %d/%d failed.\n", left, right);
            ++failed;
          }
        }
      }
    }
  }
  printf("\n\nTotal passes %d, fails %d, total tests %d\n",
         passed,
         failed,
         passed + failed);
  return 0;
}
还有一种做法,时间复杂度稍稍高很多,是常规的想法,O(m*n):
int any(char s1[], char s2[])
{
  int i;
  int j;
  int pos;

  pos = -1;

  for(i = 0; pos == -1 && s1[i] != '\0'; i++)
  {
    for(j = 0; pos == -1 && s2[j] != '\0'; j++)
    {
      if(s2[j] == s1[i])
      {
        pos = i;
      }
    } 
  }

  return pos;
}

2.9 按位运算符

  1. 6个位操作符,只能作用于带符号和不带符号的char、short、int、long类型
  2. 按位与:&,用于屏蔽某些二进制位
    n=n&0177;//将n中除了7个低二进制位外的各位均设置为0
  3. 按位或:|,用于将某些二进制的位置设置为1
    x=x|SET_ON//将SET_ON中的那些二进制位置设置为1
  4. 按位异或:^,两个位不同时设置为1,相同时设置为0
  5. x=1,y=2;
    x&y==0
    x&&y==1
  6. x<<2,将x的值左移两位,右边空出的两位使用0来填补,等价于让x乘以4
  7. 对unsigned右移时,左边用0来补
    对signed右移时,左边有些机子用符号位来补,有些机子用0来补
  8. 一元运算符~,用于求整数的二进制反码。即0变成1,1变成0。
  9. getbits(x,p,n)函数获得了在x中,从右边数第p位开始向右数n个的字段。getbits(x,4,3)返回x中第4,3,2的值。代码如下:
    /* getbits: get n bits from position p */
    unsigned getbits(unsigned x, int p, int n)
    {
    	return (x >> (p+1-n)) & ~(~0 << n);
    }
    x>>(p+1-n)将期望获得的字段移动到最右端
    ~0将所有位设置为0
    ~0< 再用~运算符,对其按位取反
    这样就能建立最右边n位全1的屏蔽码

练习题

Exercise 2-6. Write a function setbits(x,p,n,y) that returns x with the n bits that begin at position p set to the rightmost n bits of y, leaving the other bits unchanged.
x中从第p位开始的n个位,设置为y中最右边n个位的值,x中其余保持不变
答:
#include 

unsigned setbits(unsigned x, int p, int n, unsigned y)
{
  return (x & ((~0 << (p + 1)) | (~(~0 << (p + 1 - n))))) | ((y & ~(~0 << n)) << (p + 1 - n));
}

int main(void)
{
  unsigned i;
  unsigned j;
  unsigned k;
  int p;
  int n;
  
  for(i = 0; i < 30000; i += 511)
  {
    for(j = 0; j < 1000; j += 37)
    {
      for(p = 0; p < 16; p++)
      {
        for(n = 1; n <= p + 1; n++)
        {
          k = setbits(i, p, n, j);
          printf("setbits(%u, %d, %d, %u) = %u\n", i, p, n, j, k);
        }
      }
    }
  }
  return 0;
}


Exercise 2-7. Write a function invert(x,p,n) that returns x with the n bits that begin at position p inverted (i.e., 1 changed into 0 and vice versa), leaving the others unchanged.
将x从p位开始的n个求反:
答:
unsigned invert(unsigned x, int p, int n)
{
    return x ^ (~(~0U << n) << p);
}

/* main driver added, in a hurry while tired, by RJH. Better test driver suggestions are welcomed! */

#include 

int main(void)
{
  unsigned x;
  int p, n;

  for(x = 0; x < 700; x += 49)
    for(n = 1; n < 8; n++)
      for(p = 1; p < 8; p++)
        printf("%u, %d, %d: %u\n", x, n, p, invert(x, n, p));
  return 0;
}

Exercise 2-8. Write a function rightrot(x,n) that returns the value of the integer x rotated to the right by n positions.
将从右端移出的n个二进制移进左端。
答:
unsigned rightrot(unsigned x, unsigned n)
{
    while (n > 0) {
        if ((x & 1) == 1)
            x = (x >> 1) | ~(~0U >> 1);
        else
            x = (x >> 1);
        n--;
    }
    return x;
}

/* main driver added, in a hurry while tired, by RJH. Better test driver suggestions are welcomed! */

#include 

int main(void)
{
  unsigned x;
  int n;

  for(x = 0; x < 700; x += 49)
    for(n = 1; n < 8; n++)
      printf("%u, %d: %u\n", x, n, rightrot(x, n));
  return 0;
}


2.10 赋值运算符和表达式

  1. expr1 op= expr2 等价于:expr1 = (expr1) op (expr2)。前一个就将赋值运算符。expr1和expr2是两个表达式,op是操作符。还有例子如下:
    x *= y + 1等价于:x = x * (y + 1)
    而非:x = x * y + 1
  2. 统计x中的二进制位中有多少1
    /* bitcount: count 1 bits in x */
    int bitcount(unsigned x)
    {
      int b;
      for (b = 0; x != 0; x >>= 1)
         if (x & 01)
            b++;
      return b;
    }
    
  3. i+=2 比i=i+2更容易让人理解,前一句直接认为是将2加到i上去
  4. 赋值运算符可以防止代码过长
  5. 赋值运算符更能产生高效的代码

练习题

Exercise 2-9. In a two's complement number system, x &= (x-1) deletes the rightmost 1-bit in x. Explain why. Use this observation to write a faster version of bitcount.
答:
Answer: If x is odd, then (x-1) has the same bit representation as x except that the rightmost 1-bit is now a 0. In this case, (x & (x-1)) == (x-1).
如果x是奇数,那么与x-1只有最右边一位不同,x-1是0,所以 (x & (x-1)) == (x-1)。删除了x中最右边一个1。

 If x is even, then the representation of (x-1) has the rightmost zeros of x becoming ones and the rightmost one becoming a zero. Anding the two clears the rightmost 1-bit in x and all the rightmost 1-bits from (x-1).
上段英文有点难度,所以看个例子把:当x=8时:1000,x-1:111
1000&111=0,所以将x中最由边一个x消除了。

Here's the new version of bitcount: 
/* bitcount:  count 1 bits in x */
int bitcount(unsigned x)
{
    int b;

    for (b = 0; x != 0; x &= (x-1))//x &= (x-1)会得到0,如果不是0,就表示之前为0
        b++;
    return b;
}

2.11条件表达式

  1. 三元运算符:z = (a > b) ? a : b;   /* z = max(a, b) */
  2. (n > 0) ? f : n 如果,f是float型,n是int型的话,那么表达式为float型,与n是否为正值无关
  3. 可以用这个创建非常简洁的代码,比如:
    printf("You have %d items%s.\n", n, n==1 ? "" : "s");

练习题

Exercise 2-10. Rewrite the function lower, which converts upper case letters to lower case, with a
conditional expression instead of if-else.
答:
原来的:
int lower(int c)
{
  if(c >= 'A' && c <= 'Z')
    return c + 'a' - 'A';
  else
    return c;
}


现在的:
/* lower: convert c to lower case; ASCII only */
int lower(int c)
{
  return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c;
}

2.12 运算符优先级与求值次序

  1. 优先级
    读书笔记:C程序设计语言,第二章:知识要点 和 课后题全解_第1张图片
    一元运算符+、-、$、*都比二元的优先级高。
  2. printf("%d %d\n", ++n, power(2, n));  /* WRONG */ 不要这样用,后一个函数中的n不知道是用了新值就是旧值
    如下才正确:
    ++n;
    printf("%d %d\n", n, power(2, n));

  3. a[i] = i++; 这个无法解释,a的下标i是新i值还所旧i值,根据编译器可能不同


其他问题总结

为了使用gdb调试

为了使用gdb调试,请在gcc的时候加上参数-g:
zy@zy:~/Documents/eclipseWorkSpace/test/src$ gcc -g test.c 
zy@zy:~/Documents/eclipseWorkSpace/test/src$ gdb a.out 

一本书

Hacker's Delight:据说不错。




你可能感兴趣的:(C/C++)