分类 再学C 下的文章

变量名是由字母和数字组成的序列,但其中第一个字符必须为字母,其中下划线“_”被看做字母,通常用于命名较长的变量名,以提高其可读性。
库例程的名字通常是下划线开头,所以变量名最好不要以下划线开头。大写小写字母是有区别的,它们是不同的名字。通常,在C语言中,变量名使用小写字母,符号常量名全部使用大写字母。
if等关键字必须小写。


数据类型及长度
C语言中提供了下列几种基本数据类型:

名称类型说明
char字符型占用一个字节,可以存放本地字符集中的一个字符
int整型通常反映了所用机器中整数的最自然长度
float单精度浮点型 
double双精度浮点型 

此外,还可以在这些基本数据类型的前面加上一些限定符。short与long两个限定符用于限定整型:
short int sh;
long int counter;
在上述这种类型的声明中,关键字int可以省略。

  • short类型通常为16位
  • long类型通常为32位
  • unsigned char类型变量取值范围为0~255(char对象占8位)
  • signed char类型的取值范围则为-128~127(char对象占8位)
  • long double类型表示高精度的浮点数。

同整型一样,浮点型的长度也取决于具体的实现,float、double与long double类型可以表示相同的长度,也可以表示两种或三种不同的长度。
Q:编写一个程序以确定分别由signed及unsigned限定的char、short、int与long类型变量的取值范围。采用打印标准头文件中的相应值以及直接计算两种方式实现。后一种方法的实现较困难一些,因为要确定各种浮点类型的取值范围。
A:默认的定义文件在C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\,比如limits.h。用的是VS 2015版本,直接打开文件可以看到定义,下面贴一下:

//
// limits.h
//
//      Copyright (c) Microsoft Corporation. All rights reserved.
//
// The C Standard Library <limits.h> header.
//
#pragma once
#define _INC_LIMITS

#include <vcruntime.h>

_CRT_BEGIN_C_HEADER



#define CHAR_BIT      8         // number of bits in a char
#define SCHAR_MIN   (-128)      // minimum signed char value
#define SCHAR_MAX     127       // maximum signed char value
#define UCHAR_MAX     0xff      // maximum unsigned char value

#ifndef _CHAR_UNSIGNED
    #define CHAR_MIN    SCHAR_MIN   // mimimum char value
    #define CHAR_MAX    SCHAR_MAX   // maximum char value
#else
    #define CHAR_MIN    0
    #define CHAR_MAX    UCHAR_MAX
#endif

#define MB_LEN_MAX    5             // max. # bytes in multibyte char
#define SHRT_MIN    (-32768)        // minimum (signed) short value
#define SHRT_MAX      32767         // maximum (signed) short value
#define USHRT_MAX     0xffff        // maximum unsigned short value
#define INT_MIN     (-2147483647 - 1) // minimum (signed) int value
#define INT_MAX       2147483647    // maximum (signed) int value
#define UINT_MAX      0xffffffff    // maximum unsigned int value
#define LONG_MIN    (-2147483647L - 1) // minimum (signed) long value
#define LONG_MAX      2147483647L   // maximum (signed) long value
#define ULONG_MAX     0xffffffffUL  // maximum unsigned long value
#define LLONG_MAX     9223372036854775807i64       // maximum signed long long int value
#define LLONG_MIN   (-9223372036854775807i64 - 1)  // minimum signed long long int value
#define ULLONG_MAX    0xffffffffffffffffui64       // maximum unsigned long long int value

#define _I8_MIN     (-127i8 - 1)    // minimum signed 8 bit value
#define _I8_MAX       127i8         // maximum signed 8 bit value
#define _UI8_MAX      0xffui8       // maximum unsigned 8 bit value

#define _I16_MIN    (-32767i16 - 1) // minimum signed 16 bit value
#define _I16_MAX      32767i16      // maximum signed 16 bit value
#define _UI16_MAX     0xffffui16    // maximum unsigned 16 bit value

#define _I32_MIN    (-2147483647i32 - 1) // minimum signed 32 bit value
#define _I32_MAX      2147483647i32 // maximum signed 32 bit value
#define _UI32_MAX     0xffffffffui32 // maximum unsigned 32 bit value

// minimum signed 64 bit value
#define _I64_MIN    (-9223372036854775807i64 - 1)
// maximum signed 64 bit value
#define _I64_MAX      9223372036854775807i64
// maximum unsigned 64 bit value
#define _UI64_MAX     0xffffffffffffffffui64

#if _INTEGRAL_MAX_BITS >= 128
    // minimum signed 128 bit value
    #define _I128_MIN   (-170141183460469231731687303715884105727i128 - 1)
    // maximum signed 128 bit value
    #define _I128_MAX     170141183460469231731687303715884105727i128
    // maximum unsigned 128 bit value
    #define _UI128_MAX    0xffffffffffffffffffffffffffffffffui128
#endif

#ifndef SIZE_MAX
    #ifdef _WIN64
        #define SIZE_MAX _UI64_MAX
    #else
        #define SIZE_MAX UINT_MAX
    #endif
#endif

#if __STDC_WANT_SECURE_LIB__
    #ifndef RSIZE_MAX
        #define RSIZE_MAX (SIZE_MAX >> 1)
    #endif
#endif



_CRT_END_C_HEADER

根据:https://junchu25.wordpress.com/2008/02/29/the-c-programming-language-2-1/

先说关于符号和无符号的概念,有符号类型(signed),在二进制表示中,最高位为符号位。比如5(以short为例),二进制表现为0000000000000101,它的最高位是0代表正数,如果是1则代表负数。对于无符号类型,它的高位没有符号标记,所以可以空出一位来表示大一倍的正数。假设int类型可以表示2的31次方,则unsigned int可以表示2的32次方。 2^31 * 2所以是大一倍。

那么对于无符号数,直接给0取反并赋予对应的无符号类型,则可以得到这种无符号类型的最大值,而0均为无符号类型的最小值。
对于有符号数,同上,增加右移1位的操作,然后强制转换为对应的有符号类型即可得到最大值,最小值为负的最大值,直接取负即可。那么代码如下(基本属于copy,注意最后是个无符号类型的应当用%u输出,%d无法输出无符号整数,打印会有问题):

#include <stdio.h>

/*determine ranges of types*/
void main()
{
    /* signed ranges of types*/
    printf(" signed char min = %d \n", -(char)((unsigned char) ~0 >> 1));
    printf(" signed char max = %d \n", (char)((unsigned char) ~0 >> 1));
    printf(" signed short min = %d \n", -(short)((unsigned short) ~0 >> 1));
    printf(" signed short max = %d \n", (short)((unsigned short) ~0 >> 1));
    printf(" signed int min = %d \n", -(int)((unsigned int) ~0 >> 1));
    printf(" signed int max = %d \n", (int)((unsigned int) ~0 >> 1));
    printf(" signed long min = %d \n", -(long)((unsigned long) ~0 >> 1));
    printf(" signed long max = %d \n", (long)((unsigned long) ~0 >> 1));
    
    /*unsigned types*/
    printf(" unsigned char max = %u \n",(unsigned char) ~0);
    printf(" unsigned int max = %u \n",(unsigned int) ~0);
    printf(" unsigned short max = %u \n",(unsigned short) ~0);
    printf(" unsigned long max = %u \n",(unsigned long) ~0);
}

执行示例:

>cl demo.c
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.00.24215.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

demo.c
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:demo.exe
demo.obj

>demo.exe
 signed char min = -127
 signed char max = 127
 signed short min = -32767
 signed short max = 32767
 signed int min = -2147483647
 signed int max = 2147483647
 signed long min = -2147483647
 signed long max = 2147483647
 unsigned char max = 255
 unsigned int max = 4294967295
 unsigned short max = 65535
 unsigned long max = 4294967295

某些字符可以通过转义字符序列表示为字符和字符串常量。转义字符序列看起来像两个字符,但只表示一个字符。
另外我们可以用'\ooo'表示任意字节大小的位模式。其中ooo代表1~3个八进制数字(0-7)。这种位模式还可以用'\xhh'表示其中,hh是一个或多个十六进制数字(0-9,a-f,A-F)。因此,我们可以按照下列形式书写语句:

#define VTAB '\013'    /* ASCII纵向制表符 */
#define BELL '\007'    /* ASCII响铃符 */

十六进制形式:

#define VTAB '\xb'    /* ASCII纵向制表符 */
#define BELL '\x7'    /* ASCII响铃符 */

ANSI C语言中的全部转移字符序列如下:

\a    响铃符
\b    回退符
\f    换页符
\n    换行符
\r    回车符
\t    横向制表符
\v    纵向制表符
\\    反斜杠
\?    问号
\'    单引号
\"    双引号
\ooo  八进制数
\xhh  十六进制数

字符常量'\0'表示值为0的字符,也就是空字符null。通常用'\0'的形式代替0,以强调某些表达式的字符属性,但其数字值为0。常量表达式是仅仅只包含常量的表达式。这种表达式在编译时求值,而不在运行时求值。它可以出现在常量可以出现的任何位置。
字符串连接可以多个"one" " two"这样挨着就行(感觉又学到新知识了),代码举例:

#include <stdio.h>

void main()
{
    printf("shizsfia" " zheshi0");
}

输出是shizsfia zheshi0
'x'"x"的区别:前者是一个整数(用其他语言多了就会感觉这有点坑),其值是字母x在机器字符集中对应的数值;后者是一个包含一个字符以及一个结束符'\0'的字符数组。
枚举

enum boolean {NO, YES}

在没有显式说明的情况下,enum类型中的第一个枚举名值为0,第二个为1,第三个为2...如果指定了部分枚举名的值,那么未指定的枚举名的值将依着最后一个指定值向后递增。
Q:在不使用运算符&&或||的条件下编写一个与下面的for循环语句等价的循环语句。

for (i = 0; i < lim -1; && (c = getchar()) != '\n' && c != EOF; ++i)
    s[i] = c;

A:代码如下,用的if

i = 0;
if (i < lim -1)
    if ((c = getchar()) != '\n')
        if (c != EOF;)
            ++i;

Q:编写函数htoi(s),把由16进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值。字符串中允许包含的数字包括:0~9、a~f以及A~F。
A:一开始不明白,原来是转换十进制?。。。代码并非直接写的,只是添加了一点注释。

#include <stdio.h>

int htoi(char s[]);
main()
{
     char s1[] = "10";
     char s2[] = "2D";
     char s3[] = "3f";
     char s4[] = "0X4F";
     char s5[] = "0x3a";

     printf("%s -> %d\n", s1, htoi(s1));
     printf("%s -> %d\n", s2, htoi(s2));
     printf("%s -> %d\n", s3, htoi(s3));
     printf("%s -> %d\n", s4, htoi(s4));
     printf("%s -> %d\n", s5, htoi(s5));
}
int htoi(char s[])
{
     int n = 0;
     int i = -1;
    
     while (s[++i] != '\0') {
          if (i == 0 && s[i] == '0')//字符串以0开始,要么是0x 0X或者不必用于计算
               continue;
          else if (s[i] == 'x' || s[i] == 'X')//如果第二位是x 或者 X 那么后两位才是用于16进制转10进制计算所需要的
               continue;
          else if ('0'<= s[i] && s[i] <= '9')//这里就是字符到对应数大小的转换
               n = n * 16 + (s[i] - '0');
          else if ('a'<= s[i] && s[i] <= 'f')
               n = n * 16 + (s[i] - 'a' + 10);
          else if ('A' <= s[i] && s[i] <= 'F')
               n = n * 16 + (s[i] - 'A' + 10);
          else
               return -1;
     }
     return n;
}

Q:重新编写函数squeeze(s1, s2),将字符串s1中任何与字符串s2中字符匹配的字符都删除。
A:代码如下:

#include <stdio.h>

void squeeze(char s[], char t[]);
void main()
{
    char string_1[] = "ggqweewqeqweqweqweqeqweqweqweqhh";
    char string_2[] = "qwe";
    printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
    squeeze(string_1, string_2);
    printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
}
void squeeze(char string_1[], char string_2[])
{
    int i, j, k;
    for (k = 0; string_2[k] != '\0'; k++)
    {
        for (i = j = 0; string_1[i] != '\0'; i++)
            if (string_1[i] != string_2[k])
                string_1[j++] = string_1[i];
        string_1[j] = '\0';
    }
}

Q:重新编写函数any(s1, s2),将字符串s2中任一字符在字符串s1中第一次出现的位置作为结果返回。如果s1中不包含s2中的字符,则返回-1.(标准库函数strpbrk具有同样的功能,但它返回的是指向该位置的指针)
A:代码如下:

#include <stdio.h>

int any(char s1[], char s2[]);
void main()
{
    char string_1[] = "ggqweewqeqweqweqweqeqweqweqweqhh";
    char string_2[] = "okqwe";
    int res;
    printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
    res = any(string_1,string_2);
    printf("position is %d.\n", res);
}
int any(char s1[], char s2[])
{
    int i, j, posit = -1;
    for (i = 0; s2[i] != '\0'; i++)
    {
        for (j = 0; s1[j] != '\0'; j++)
        {
            if (s2[i] == s1[j])
            {
                posit = i;
                break;
            }
        }
        if (posit == -1)
            continue;
        else
            break;
    }
    return posit;
}

Q:编写一个函数setbits(x,p,n,y),该函数返回对x执行下列操作后的结果值:将x从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。
A:代码如下:

#include <stdio.h>

unsigned setbits(unsigned x, int p, int n, unsigned y);
void main()
{
    unsigned x = 0xa3, y = 0xb4, res;
    //x 163 10100011 y 180 10110100 --> 10101001 163
    int p = 1, n = 3;//p从右向左第0位开始
    res = setbits(x, p, n, y);
    printf("%u.\n", res);
}
unsigned setbits(unsigned x, int p, int n, unsigned y)
{
    unsigned temp;
    int i;
    i = p;
    temp = ~0 << n;//取反构造匹配p位开始n个的部分,与之相与使其置零
    while(i-- > 0){
        temp = temp << 1;
        temp++;
    }
    x = x & temp;
    temp = ~(~0 << n);
    y = y & temp;//将y移位到对应的位置
    while(p-- > 0){
        y = y << 1;
        y++;
    }
    
    return (x | y);//y与之相或,作用是覆盖对应的部分,即替换
}

Q:编写一个函数invert(x,p,n),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位取反(即,1变成0,0变成1),x的其余各位保持不变。
A:代码如下:

#include <stdio.h>

unsigned invert(unsigned x, int p, int n);
void main()
{
    unsigned x = 0xa3, y = 0xb4, res;
    //x 163 10100011 y 180 10110100 --> 10101001 163
    // 10101101 --> 173
    int p = 1, n = 3;//p从右向左第0位开始
    res = invert(x, p, n);
    printf("%u.\n", res);
}

unsigned invert(unsigned x, int p, int n)
{
    unsigned temp;
    temp = ~0 << n;
    while(p-- > 0){
        temp = temp << 1;
        temp++;
    }
    return (~(~temp & x) & (~temp | x));
    //前半部分得到中间部分为取反结果,两侧是1
    //后半部分得到两侧是原结果,中间部分是1
    //两者相与两边则是原结果,中间则是取反结果
}

Q:编写一个函数rightrot(x,n),该函数返回将x循环右移(即从最右端移出的位将从最左端移入)n(二进制)位后所得到的值。
A:代码如下:

#include <stdio.h>

unsigned rightrot(unsigned x, int n);
void main()
{
    unsigned res, x = 0xa3;
    //10100011 --> 01100000000000000000000000010100 电脑上是32位
    int n = 3;
    res = rightrot(x, n);
    printf("%u.\n", res);
}

unsigned rightrot(unsigned x, int n)
{
    unsigned temp = ~0;
    unsigned temp_1 = ~0;
    while(temp != 0)
    {
        temp_1 = temp_1 << 1;
        temp = temp_1 << 1;
    }
    printf("temp_1 is %u.\n", temp_1);
    temp = ~(~0 >> 1);
    printf("temp is %u.\n", temp);
    
    while(n-- > 0)
    {
        temp = (~0 << 1) | x;//得到x最右一位的值 只有两种情况
        x = x >> 1;
        if (temp == ~0)//x最后一位为1
        {
            // x = ~(~0 >> 1) | x;
            x = temp_1 | x;
        }
        // else//x最后一位为0
        // {
            // //x最后一位为0 那么x右移已经自动补0了 这里就不用操作
        // }
    }
    return x;
}

Q:在求对二的补码时,表达式x&=(x - 1)可以删除x中最右边值为1的一个二进制位,请解释这样做的道理。用这一方法重写bitcount函数,以加快其执行速度。
A:x&=(x - 1)等效于 x = x & (x - 1),如果x最右边的一个二进制位为1,那么减1之后前面的位不变,最右这位变成0,这样与原本相“与”,那么x就变成了x-1,且最右一位是0,达到了删除最右边值为1的二进制位效果。更改代码如下(参考的),不过好像效率并没有提高?

#include <stdio.h>

int bitcount(unsigned x);

void main(void)
{
    unsigned x = 0xa3;
    int b = 0;
    
    b = bitcount(x);
    printf("b is %d.\n", b);
}

int bitcount(unsigned x)
{
    int b = 0;
    while(x != 0)
    {
        b++;
        x &= (x -1);
    }
    return b;
}

Q:重新编写将大写字母转换为小写字母的函数lower,并用条件表达式替换其中的if-else结构。
A:代码如下:

#include <stdio.h>

int bitcount(unsigned x);

void main(void)
{
    char string[] = "WGHigGYljuo";
    int n = 0;
    while(string[n++] != '\0')
        printf("%c", (string[n] >= 'A' && string[n] <= 'Z') ? (string[n] - 'A' + 'a') : string[n]);
    printf("\n");
}

写在前面,因为电脑之前装过VS 2015了,虽然一直没有怎么用,不过打开软件写代码还是有点耗费资源,正当纠结选哪个编译器的时候,突然想起有命令行编译的工具cl,那就用cl吧~~~
tips:要在屏幕上打印文字。比如下面的hello world,要么加上注释那句,要么在命令行直接执行exe,就能看到效果了~


第一个C语言程序:

#include <stdio.h>

main()
{
    printf("hello, world !\n");
    // system("pause");
}

Q:有意去掉部分内容,会得到什么出错信息?
A:无法编译(当然也可能编译通过但是执行没有任何信息显示),比如printf——>pritf,编译结果如下图:
QQ截图20181106201449.png
Q:做个实验,当printf函数参数字符串中包含c时,观察一下会出现什么情况?
A:编译提示helloworld.c(5): warning C4129: “c”: 不可识别的字符转义序列,执行程序还是有个c。
QQ截图20181106202105.png

Q:修改温度转换程序,使之能在转换表的顶部打印一个标题。
A:如图:QQ截图20181106204144.png

#include <stdio.h>

main()
{
    float fahr, celsius;
    int lower, upper, step;
    lower = 0;
    upper = 300;
    step = 20;
    fahr = lower;
    printf("fahr celsius generate\n");
    while (fahr <= upper) {
        celsius = (5.0 / 9.0) * (fahr - 32);
        printf("%6.0f  %6.1f\n", fahr, celsius);
        fahr = fahr + step;
        }
    // system("pause");
}

Q:编写一个程序打印摄氏温度转换相应华氏温度的转换表。
A:代码如下:
QQ截图20181106204708.png

#include <stdio.h>

main()
{
    float fahr, celsius;
    int lower, upper, step;
    lower = 0;
    upper = 150;
    step = 10;
    celsius = lower;
    printf("celsius fahr generate\n");
    while (celsius <= upper) {
        fahr = (celsius * 9.0) / 5.0 + 32;
        printf("%6.0f  %6.1f\n", celsius, fahr);
        celsius = celsius + step;
        }
    // system("pause");
}

Q:修改温度转换程序,要求以逆序打印温度转换表。
A:代码如下:
QQ截图20181106205856.png

#include <stdio.h>

main()
{
    int fahr;
    printf("celsius fahr generate\n");
    for (fahr = 300; fahr >= 0; fahr = fahr - 20)
        printf("%6d  %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
    // system("pause");
}

Q:打印EOF的值。
A:在主程序中使用printf("%d\n", EOF);打印,结果为-1。
Q:验证表达式getchar() != EOF的值是0还是1。
A:代码如下,通过输入验证,该表达式的值在成立的条件下为1

#include <stdio.h>

main()
{
    int char_in;
    char_in = (getchar() != EOF);
    printf("%d\n", char_in);
}

Q:编写一个统计空格、制表符与换行个数的程序。
A:代码如下:

#include <stdio.h>

main()
{
    char char_in;
    int num_s,num_t,num_e;//空格、制表符、换行符
    num_s = 0;
    num_t = 0;
    num_e = 0;
    char_in = getchar();
    while (char_in != EOF){
        if (char_in == ' ')
            num_s++;
        if (char_in == '\t')
            num_t++;
        if (char_in == '\n')//每次输入时都会回车,所以通过回车将字符送入时,送入的字符包括了回车/换行
            num_e++;
        putchar(char_in);
        char_in = getchar();
    }
    printf("%d %d %d", num_s, num_t, num_e);
}

Q:编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替。
A:代码如下:

#include <stdio.h>

main()
{
    char char_in[100],char_out[100];
    int i, j;
    j = 0;
    puts("input your string:\n");
    gets(char_in);
    for (i = 0; i <= strlen(char_in); i++)
    {
        if ((char_in[i] == ' ') && (char_out[j-1] == ' '))
            continue;
        char_out[j] = char_in[i];
        j++;
    }
    printf("%s", char_out);
}

运行示例:

>helloworld.exe
input your string:

hi  my name is wm, what   is your name?
hi my name is wm, what is your name?

Q:编写一个将输入复制到输出的程序,并将其中的制表符换成t,把回退符换成b,把反斜杠换成\。这样可以将制表符和回退符以可见的方式显示出来。
A:注意,代码中使用的是gets,输入回退符实际不起作用,因为它是在回车之后读取的,实际上回退符会删除字符,并且不会处理回车输入的回车符。要处理可以用getch(需要引入头文件conio.h)。

#include <stdio.h>

main()
{
    char char_in[100],char_out[100];
    int i, j;
    j = 0;
    puts("input your string:\n");
    gets(char_in);
    for (i = 0; i <= strlen(char_in); i++)
    {
        if (char_in[i] == '\t')
        {
            char_out[j++] = '\\';
            char_out[j++] = 't';
            continue;
        }
        if (char_in[i] == '\b')
        {
            char_out[j++] = '\\';
            char_out[j++] = 'b';
            continue;
        }
        if (char_in[i] == '/')
        {
            char_out[j++] = '\\';
            continue;
        }
        char_out[j++] = char_in[i];
    }
    printf("%s", char_out);
}

Q:你准备如何测试单词计数程序?如果程序中存在某种错误,那什么样的输入最可能发现这类错误呢?
A:代码如下,编译后执行输入测试呗。有可能存在的情况是,比如输入的字符中包括类EOF的字符,然后程序提前结束了。

#include <stdio.h>
#include <conio.h>

#define IN 1
#define OUT 0

main()
{
    int c, n1, nw, nc, state;
    
    state = OUT;
    n1 = nw = nc = 0;
    while ((c = getchar()) != EOF)
    {
        ++nc;
        if (c == '\n')
            ++n1;
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT)
        {
            state = IN;
            ++nw;
        }
    }
    printf("%d %d %d\n", n1, nw, nc);
}

测试示例(Ctrl Z结束输入):

>helloworld.exe
my name is wang




^Z
5 4 20

Q:编写一个程序,以每行一个单词的形式打印其输出。
A:代码如下,对比了下最好的答案,错在思路上?

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

main()
{
    int i=0, j=0;
    char char_out[5][10]={"","","","",""};
    char char_temp[10];
    char c;
    while ((c = getchar()) != EOF)
    {
        if (c == '\n')
        {
            strcpy(char_out[j],char_temp);
            break;
        }
        else//不用else的话 在回车输入的时候就会把回车放到字符串
            char_temp[i++] = c;
        if (c == ' ' || c == '\t')
        {
            char_temp[i] = 0;//这个时候已经包含了空格,需要去掉
            strcpy(char_out[j],char_temp);
            // strcpy(char_temp,"");
            // char_temp[0] = 0;
            memset(char_temp, 0 , sizeof(char_temp));
            i = 0;
            j++;
        }
    }
    i = 0;
    for (i = 0; i <= 4; i++)
    {
        printf("%s\n", char_out[i]);
    }
    
}

较标准答案:

#include <stdio.h>
#define IN 1
#define OUT 0

int main()
{
    int c, state;
    state = OUT;
    while( (c = getchar()) != EOF)
    {
        if(c == ' ' || c == '\n' || c == '\t')
        {
            if(state == IN)
            {
                putchar('\n');
                state = OUT;
            }
        }
        else if (state == OUT)
        {
            state = IN;
            putchar(c);
        }
        else
            putchar(c);
    }
}

Q:编写一个程序,打印输入中各个字符出现频度的直方图。
A:代码如下,就是对字符做统计,可以参考书本的例子,稍作改变即可实现

#include <stdio.h>

print_same(int num)
{
    int i;
    for (i = 0; i < num; i++)
        printf("-*");
    printf("\n");
}

main()
{
    int c, i, nother = 0;
    int nletter[26];
    for (i = 0; i < 26; i++)
        nletter[i] = 0;
    
    while((c = getchar()) != '\n')
        if (c >= 'a' && c <= 'z')
            ++nletter[c - 'a'];
        else if (c >= 'A' && c <= 'Z')
            ++nletter[c - 'A'];
        else
            ++nother;
    for (i = 0;i < 26; i++)
    {
        printf("%c ", (65 + i));
        if (nletter[i] == 0)
            printf("\n");
        else
            print_same(nletter[i]);
    }
    
}

执行示例:

>helloworld.exe
hello, my name is li hua.you can see that i am very love this car. its beautiful and cool. i think you will love it too.
A -*-*-*-*-*-*-*-*
B -*
C -*-*-*
D -*
E -*-*-*-*-*-*-*-*
F -*
G
H -*-*-*-*-*
I -*-*-*-*-*-*-*-*-*-*
J
K -*
L -*-*-*-*-*-*-*-*-*
M -*-*-*
N -*-*-*-*
O -*-*-*-*-*-*-*-*-*
P
Q
R -*-*
S -*-*-*-*
T -*-*-*-*-*-*-*-*
U -*-*-*-*-*
V -*-*-*
W -*
X
Y -*-*-*-*
Z

Q:编写一个程序,打印输入中单词长度的直方图,水平的直方图比垂直的直方图简单。
A:主要是巧妙处理费字母字符以及连续的非字母字符出现情况。代码如下:

#include <stdio.h>

print_same(int num)
{
    int i;
    for (i = 0; i < num; i++)
        printf("-*");
    printf("\n");
}

main()
{
    int c, i, n, flag;
    int nletter[100];
    n = flag = 0;
    for (i = 0; i < 100; i++)
        nletter[i] = 0;
    i = 0;
    while((c = getchar()) != '\n')
    {
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            i++;
        else
        {
            if (i == 0)
                continue;
            nletter[n] = i;
            n++;
            i = 0;
        }
    }
    i = 0;
    while(nletter[i] != 0)
    {
        printf("%d ", nletter[i]);
        print_same(nletter[i]);
        i++;
    }
    
}

执行示例:

>helloworld.exe
hello, my name is li hua.you can see that i am very love this car. its beautiful and cool. i think you will love it too.
5 -*-*-*-*-*
2 -*-*
4 -*-*-*-*
2 -*-*
2 -*-*
3 -*-*-*
3 -*-*-*
3 -*-*-*
3 -*-*-*
4 -*-*-*-*
1 -*
2 -*-*
4 -*-*-*-*
4 -*-*-*-*
4 -*-*-*-*
3 -*-*-*
3 -*-*-*
9 -*-*-*-*-*-*-*-*-*
3 -*-*-*
4 -*-*-*-*
1 -*
5 -*-*-*-*-*
3 -*-*-*
4 -*-*-*-*
4 -*-*-*-*
2 -*-*
3 -*-*-*

C语言函数结构:

返回值类型 函数名(0个或多个参数声明)
{
    声明部分
    语句序列
}

通常把函数定义中圆括号内列表中出现的变量称为形式参数,而把函数调用中形式参数对应的值称为实际参数。
Q:修改温度转换程序,使用函数实现温度转换计算
A:代码如下

#include <stdio.h>

main()
{
    float fahr, celsius;
    int lower, upper, step;
    float f2c(float fahr);//声明函数
    lower = 0;
    upper = 300;
    step = 20;
    fahr = lower;
    printf("fahr celsius generate\n");
    while (fahr <= upper) {
        celsius = f2c(fahr);
        printf("%6.0f  %6.1f\n", fahr, celsius);
        fahr = fahr + step;
        }
}

float f2c(float fahr)
{
    float celsius; 
    celsius = (5.0 / 9.0) * (fahr - 32);
    return celsius;
}

Q:修改打印最长文本行的程序的主程序main,使之可以打印任意长度的输入行的长度,并尽可能多的打印文本。
A:修改后的代码如下,说明:去除了输入字符的限制,最多10个字符,其中最后两位用于存放换行和0结束标志。问题,如果在一行中输入EOF(比如 Ctrl Z),那么会继续输入计数,正常的结束时在一行换行结束此行输入后输入EOF(Ctrl Z),然后再Ctrl Z问题已解决,方法是EOF、换行等直接写入零结束标志

#include <stdio.h>
#define MAXLINE 10

int getline(char line[], int maxline);
void copy(char to[], char from[]);

/* 打印最长的输入行 */
main()
{
    int len;/* 当前的行长度 */
    int max;/* 目前为止发现的最长行长度 */
    char line[MAXLINE];/* 当前的输入行 */
    char longest[MAXLINE];/* 用于保存最长的行 */
    
    max = 0;
    while ((len = getline(line, MAXLINE)) > 0){
        printf("the length of this line is %d.\n", len);
        if (len > max){
            max = len;
            copy(longest, line);
    }}
    if (max > 0)
        printf("%s", longest);/* 存在这样的行 */
    return 0;
}

/* getline函数:将一行读入到s并返回其长度 */
int getline(char s[], int lim)
{
    int c, i;
    
    for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c!= '\n'; ++i)
        s[i] = c;
    if (c == '\n' || c == EOF){
        s[i] = '\0';
    }
    else
    {
        s[i] = '\0';
        while ((c = getchar()) != '\n' && i != 0)
            ++i;
    }
    return i;
}

/* copy函数:将from复制到to;这里假定to足够大 */
void copy(char to[], char from[])
{
    int i;
    
    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

Q:编写一个程序,打印长度大于80的所有输入行
A:代码在上一个函数中修改main函数即可,这里测试大于15即可,main函数代码如下:

main()
{
    int len;/* 当前的行长度 */
    char line[MAXLINE];/* 当前的输入行 */
    char longest[MAXLINE];/* 用于保存最长的行 */
    
    while ((len = getline(line, MAXLINE)) > 0){
        printf("the length of this line is %d.\n", len);
        if (len > 15){
            printf("%s\n", line);
    }}
    return 0;
}

Q:编写一个程序,删除行末尾的制表符和空格,并删除完全是空格的行。
A:在检查到输入结束,比如换行或零结束标志作为输入完成,通过getchar向前循环判断是不是制表符或空格,用零标志替换即可,没有则末尾就是零标志。
Q:编写函数reverse(s),将字符串s中的字符顺序颠倒过来。使用该函数编写一个程序,每次颠倒一个输入行中的字符顺序。
A:代码如下,感觉我写的怪怪的

#include <stdio.h>
#define MAXLINE 1000

int getline(char line[], int maxline);
char *reverse(char line[], int linelen);

/* 颠倒输入行 */
main()
{
    int linelen;/* 行长度 */
    char line[MAXLINE];/* 输入行 */
    char *revline;
    
    while ((linelen = getline(line, MAXLINE)) > 0)
    {
        revline = reverse(line, linelen);
        printf("%s\n", revline);
    }
    return 0;
}

/* getline函数:将一行读入到s并返回其长度 */
int getline(char s[], int lim)
{
    int c, i;
    
    for (i = 0; i < lim - 2 && (c = getchar()) != EOF && c!= '\n'; ++i)
        s[i] = c;
    if (c == '\n' || c == EOF){
        s[i] = '\0';
    }
    else
    {
        s[i] = '\0';
        while ((c = getchar()) != '\n' && i != 0)
            ++i;
    }
    return i;//这个长度不包括末尾的换行和零标志
}

/* reverse函数:颠倒字符数组顺序 */
char *reverse(char line[], int linelen)
{
    int i;
    char revline[MAXLINE];
    
    i = 0;
    while ((linelen - i - 1) != -2)
    {
        revline[i] = line[linelen - i - 1];
        ++i;
    }
    revline[i] = '\0';
    return revline;
}

Q:编写程序detab,将输入中的制表符替换成适当数目的空格,使空格充满到下一个制表符终止位的地方。假设制表符终止位的位置是固定的,比如每隔n列就会出现一个制表符终止位。n应该作为变量还是符号常量呢?
A:一个制表符通常和四个字符(文本是这样,但命令行一个TAB是8个字符宽)一样宽,n应当是作为常量。关键在于,如果你输入了两个字符,此时你按下TAB(制表符),实际上只向后走了2个符号的位置。代码如下:

#include <stdio.h>
#define MAXLINE 1000
#define n 8

int getline(char line[], int maxline);
void detab(char s[], int len, char detab_line[]);


main()
{
    int linelen;/* 行长度 */
    char line[MAXLINE];/* 输入行 */
    char detab_line[MAXLINE];/* 结果行 */
    
    linelen = getline(line, MAXLINE);
    printf("length is %d.\n", linelen);
    detab(line, linelen, detab_line);
    printf("%s.\n", detab_line);
    return 0;
}

/* getline函数:将一行读入到s并返回其长度 */
int getline(char s[], int lim)
{
    int c, i;
    
    for (i = 0; i < lim - 2 && (c = getchar()) != EOF && c!= '\n'; ++i)
    {
        s[i] = c;
    }
    if (c == '\n' || c == EOF){
        s[i] = '\0';
    }
    else
    {
        s[i] = '\0';
        while ((c = getchar()) != '\n' && i != 0)
            ++i;
    }
    return i;//这个长度不包括末尾的换行和零标志
}

void detab(char s[], int len, char detab_line[])
{
    int i, j, num=0;
    for (i = 0; i < len; i++)
    {
        if (s[i] == '\t')
        {
            //j 需要的空格数
            for (j =0; j < n - (i % n); j++)
            {
                detab_line[num++] = ' ';
            }
        }
        else if (s[i] == '\0')
        {
            detab_line[num++] = '\0';
            break;
        }
        else
        {
            detab_line[num++] = s[i];
        }
    }
}

Q:编写程序entab,将空格串替换为最少数量的制表符和空格,但要保持单词之间的间隔不变。假设制表符终止位的位置与上一个问题的detab程序的情况相同。当使用一个制表符或者一个空格都可以到达下一个制表符终止位时,选用哪一种替换字符比较好?
A:我的理解是比如9个空格(表述不够清晰,是刚好制表符位置这样)组成的空格串,转换为一个制表符和一个空格(假设一个制表符是8个字符宽)。当然是尽量使用制表符,因为这样可以节省数组空间。关键在于空格开始的位置和空格的个数,有空格在制表符末位,即第8、16位并且向前至少1个字符是空格使用制表符才有意义。代码如下,我的方案有点绕的地方,可能有点不好理解...

#include <stdio.h>
#define MAXLINE 1000
#define n 8

int getline(char line[], int maxline);
void entab(char s[], int len, char entab_line[]);

main()
{
    int linelen;/* 行长度 */
    char line[MAXLINE];/* 输入行 */
    char entab_line[MAXLINE];/* 结果行 */
    
    linelen = getline(line, MAXLINE);
    printf("length is %d.\n", linelen);
    entab(line, linelen, entab_line);
    printf("%s.\n", entab_line);
    return 0;
}

/* getline函数:将一行读入到s并返回其长度 */
int getline(char s[], int lim)
{
    int c, i;
    
    for (i = 0; i < lim - 2 && (c = getchar()) != EOF && c!= '\n'; ++i)
    {
        s[i] = c;
    }
    if (c == '\n' || c == EOF){
        s[i] = '\0';
    }
    else
    {
        s[i] = '\0';
        while ((c = getchar()) != '\n' && i != 0)
            ++i;
    }
    return i;//这个长度不包括末尾的换行和零标志
}

void entab(char s[], int len, char entab_line[])
{
    int i, j=0, num=0;
    for (i = 0; i < len; i++)
    {
        if ((i + 1) % n == 0 && s[i] ==' ')
        {
            while(s[--i] == ' ' && j < n - 1)
            {
                entab_line[num--] = '\0';//每向前一个是空格,则在这个位置准备好用制表符替换,并将将被制表符代替的位置写0
                j++;//用于判断是不是存在向前至少2个都是空格
            }
            i = i + j + 1;
            if (j > 0)//说明至少两个空格,可以选择用一个制表符代替
            {
                entab_line[num++] = '\t';
                j = 0;
            }
        }
        else
        {
            entab_line[num++] = s[i];
        }
    }
}

Q:编写一个程序,把较长的输入行”折“成短一些的两行或多行,折行的位置在输入行的第n列之前的最后一个非空格之后。要保证程序能够智能地处理输入行很长以及在指定的列前没有空格或制表符的情况。
A:代码如下,具体说明见注释

#include <stdio.h>
#define MAXLINE 1000
#define n 8//制表符固定列数
#define goodlen 10//换行的适宜长度 可自行设置

int getline(char line[], int maxline);
void fmatcode(char s[], int len, char fmat_line[]);

main()
{
    int linelen;/* 行长度 */
    char line[MAXLINE];/* 输入行 */
    char fmat_line[MAXLINE];/* 结果行 */
    
    linelen = getline(line, MAXLINE);
    printf("length is %d.\n", linelen);
    fmatcode(line, linelen, fmat_line);
    printf("%s.\n", fmat_line);
    return 0;
}

/* getline函数:将一行读入到s并返回其长度 */
int getline(char s[], int lim)
{
    int c, i;
    
    for (i = 0; i < lim - 2 && (c = getchar()) != EOF && c!= '\n'; ++i)
    {
        s[i] = c;
    }
    if (c == '\n' || c == EOF){
        s[i] = '\0';
    }
    else
    {
        s[i] = '\0';
        while ((c = getchar()) != '\n' && i != 0)
            ++i;
    }
    return i;//这个长度不包括末尾的换行和零标志
}

//合理拆分过长行,假设一行30字符是最合理的
//思路:遍历字符数组,到第30个位置时,作一些判断。
//如果31位是空格或者制表符,那么往前挪一个单词然后折行。
//题目意思是上面这种情况直接可以折行了,但是我认为这样直接折行,
//新行开头就是空格,不美观。(划去,英文文章好像是允许空格。直接折行
//如果是单词,那么向前遍历,遇到空格就可以折行了
//还有个问题,一个制表符的宽度要考虑下,所以是按实际宽度与30对比的
void fmatcode(char s[], int len, char fmat_line[])
{
    int i, temp, j=0, num=0;
    for (i = 0; i < len; i++)
    {
        if (s[i] == '\t')
        {
            printf("检查到制表符,且j为%d.\n", j);
            j = j + n - j % n;//用于判断制表符是不是刚好在最适合折行的位置
            //当位置为24 25 26 27 28 29 30的时候,制表符占据(横跨)了折行位置,需要将其放到下一行
            if (j % goodlen > 0 && j % goodlen < n)
            {
                j = n;//制表符放到新行开始了,j置为制表符长度
                fmat_line[num++] = '\n';//插入换行符
                printf("插入换行符\n");
                // i--;
            }
            // else
            // {
                // printf("j当前值是%d.\n", j);
                // j = j + n - j % n;//j用来记显示的字符串(包含空格、制表符)的长度
                // printf("j增加一个制表符宽度后值是%d.\n", j);
            // }
            fmat_line[num++] = '\t';
            printf("插入制表符ttttt\n");
            printf("j值是%d.\n", j);
        }
        else if (++j % goodlen != 0)
        {
            printf("字符%c当前不位于换行处.\n", s[i]);
            fmat_line[num++] = s[i];
            printf("正常复制单字符%c.\n", s[i]);
        }
        else
        {
            printf("字符%c当前位于换行处.\n", s[i]);
            if (s[i] == ' ')//换行位置是空格
            {
                fmat_line[num++] = '\n';
                fmat_line[num++] = s[i];
                printf("插入换行符 新行空格\n");
            }
            else//是单词 单词横跨了换行(不考虑标点符号 要考虑可以在前面的if上加条件)
            {
                printf("换行处是单词.\n");
                printf("num是%d i是%d.\n", num, i);
                temp = fmat_line[--i];
                while (temp != ' ' && temp != '\t')//直到向前遇到空格或制表符 那么就可以换行了
                {
                    num--;
                    //将循环从新换行的单词开始
                }
                num--;//遇到空格 制表符 num还需要前移一位,空格 制表符放新行
                printf("num是%d i是%d.\n", num, i);
                fmat_line[num++] = '\n';
                i--;//空格or制表符都被换到新行了 那么循环得往前调整一个字符
                printf("插入换行符 单词新行\n");
            }
        }
    }
}

Q:编写一个删除C语言程序中所有的注释语句。要正确处理带引号的字符串与字符常量。在C语言中,注释不允许嵌套。
A:这个题想了挺久,后面还是求助了。原来是用状态机这种思想,最开始的题就已经涉及了,而且前面几个题也可以这样做,看来没有领悟到作者的用意啊~~~下面是(基本copy)基于状态机的方法。这个可以改一点,就是以整个程序输入完了再输出结果,不过得改些地方。{附上来源:https://www.cnblogs.com/zhanghaiba/p/3569928.html}

#include <stdio.h>

void delnote();

int main(void)
{
    delnote();
    return 0;
}

void delnote()
{
    int c, state;

    state = 0;
    while ((c = getchar()) != EOF) {
        if (state == 0 && c == '/')         // ex. [/]
            state = 1;
        else if (state == 1 && c == '*')     // ex. [/*]
            state = 2;
        else if (state == 1 && c == '/')    // ex. [//]
            state = 4;
        else if (state == 1) {                // ex. [<secure/_stdio.h> or 5/3]
            putchar('/');
            state = 0;
        }

        else if (state == 2 && c == '*')    // ex. [/*he*]
            state = 3;
        else if (state == 2)                // ex. [/*heh]
            state = 2;

        else if (state == 3 && c == '/')    // ex. [/*heh*/]
            state = 0;
        else if (state == 3)                // ex. [/*heh*e]
            state = 2;

        else if (state == 4 && c == '\\')    // ex. [//hehe\]
            state = 9;
        else if (state == 9 && c == '\\')    // ex. [//hehe\\\\\]
            state = 9;
        else if (state == 9)                // ex. [//hehe\<enter> or //hehe\a]
            state = 4;
        else if (state == 4 && c == '\n')    // ex. [//hehe<enter>]
            state = 0;

        else if (state == 0 && c == '\'')     // ex. [']
            state = 5;
        else if (state == 5 && c == '\\')     // ex. ['\]
            state = 6;
        else if (state == 6)                // ex. ['\n or '\' or '\t etc.]
            state = 5;
        else if (state == 5 && c == '\'')    // ex. ['\n' or '\'' or '\t' ect.]
            state = 0;

        else if (state == 0 && c == '\"')    // ex. ["]
            state = 7;
        else if (state == 7 && c == '\\')     // ex. ["\]
            state = 8;
        else if (state == 8)                // ex. ["\n or "\" or "\t ect.]
            state = 7;
        else if (state == 7 && c == '\"')    // ex. ["\n" or "\"" or "\t" ect.]
            state = 0;

        if ((state == 0 && c != '/') || state == 5 || state == 6 || state == 7 || state == 8)
            putchar(c);
    }
}

Q:编写一个程序,查找C语言程序中的基本语法错误,如圆括号、方括号、花括号不配对。要正确处理引号(包括单引号和双引号)、转移字符序列与注释。(如果读者想把程序编写成完全通用的程序,难度会比较大。)
A://TODO 思路同上一题?也是类似如此,可以先把注释通过上面的程序去掉,剩下纯代码。整理出一个状态机,匹配语法的括号啊什么的(其实上面的思路里面对于引号就是个例子),又想了想,似乎还是有很多问题要处理,比如某一段就是缺少了对应的括号,怎么确定是错在这里的?打算搁置这个问题。。